org.rifidi.emulator.reader.llrp.rospec.ROSpecController.java Source code

Java tutorial

Introduction

Here is the source code for org.rifidi.emulator.reader.llrp.rospec.ROSpecController.java

Source

/*
 *  ROSpecController.java
 *
 *  Created:   Jun 20, 2006
 *  Project:   RiFidi Emulator - A Software Simulation Tool for RFID Devices
 *              http://www.rifidi.org
 *              http://rifidi.sourceforge.net
 *  Copyright:   Pramari LLC and the Rifidi Project
 *  License:   Lesser GNU Public License (LGPL)
 *              http://www.opensource.org/licenses/lgpl-license.html
 */
package org.rifidi.emulator.reader.llrp.rospec;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rifidi.emulator.reader.llrp.trigger.DurationTrigger;
import org.rifidi.emulator.reader.llrp.trigger.GPIWithTimeoutTrigger;
import org.rifidi.emulator.reader.llrp.trigger.ImmediateTrigger;
import org.rifidi.emulator.reader.llrp.trigger.PeriodicTrigger;
import org.rifidi.emulator.reader.llrp.trigger.TimerTrigger;
import org.rifidi.emulator.reader.llrp.trigger.TriggerObservable;

/**
 * This is a controller that manages all of the ROSpecs. It listens for start
 * and stop triggers, and changes the state of rospecs according to the start
 * and stop triggers.
 * 
 * LLRP defines three states for a ROSpec to be in: 1)Disabled. 2)Inactive
 * 3)Running. When a ROSpec is added, it is in the Disabled Spec. An
 * ENABLE_ROSPEC message moves the ROSpec to the inactive state. Then it waits
 * for a start trigger to put it in the execution state. When it is executing, a
 * stop trigger will move it back to the disabled state. A DISABLE_ROSPEC
 * Message moves it back to the Disabled state. Finally a DELETE_ROSPEC message
 * will remove it completely
 * 
 * @author Matthew Dean - matt@pramari.com
 * @author Kyle Neumeier
 */
public class ROSpecController implements Observer {

    /**
     * The logger for this class.
     */
    private static Log logger = LogFactory.getLog(ROSpecController.class);

    /**
     * A map of every ROSpec that exists in this reader.
     */
    private HashMap<Integer, _ROSpec> allROSpecs;

    /**
     * Queue of disabled rospecs.
     */
    private HashMap<Integer, _ROSpec> disabledList;

    /**
     * Queue of active rospects.
     */
    private ArrayList<_ROSpec> activeList;

    /**
     * Queue of active rospects.
     */
    private HashMap<Integer, _ROSpec> inactiveList;

    /**
     * Hashmap of SpecStates. Each rospec has a spec state that will start and
     * stop its execution.
     */
    private HashMap<Integer, TriggerObservable> executionStateList;

    /**
     * Hashmap of states where the rospec should go after it gets stopped. The
     * key is the ROSpecID and the value is the state
     */
    private HashMap<Integer, Integer> afterStopStateList;

    private static final int STATE_DISABLED = 0;

    private static final int STATE_INACTIVE = 1;

    private static final int STATE_ACTIVE = 2;

    private static final int STATE_DELETED = 3;

    /**
     * Private constructor
     */
    ROSpecController() {
        this.allROSpecs = new HashMap<Integer, _ROSpec>();
        this.disabledList = new HashMap<Integer, _ROSpec>();
        this.activeList = new ArrayList<_ROSpec>();
        this.inactiveList = new HashMap<Integer, _ROSpec>();
        this.executionStateList = new HashMap<Integer, TriggerObservable>();
        this.afterStopStateList = new HashMap<Integer, Integer>();
    }

    /**
     * Adds a rospec to the disabled queue. Puts the ROSpec in the disabled
     * state.
     * 
     * @param rospecToAdd
     *            ROSpec object to add
     * @return
     */
    public boolean addROSpec(_ROSpec rospecToAdd) {
        if (allROSpecs.containsKey(rospecToAdd.getId())) {
            return false;
        }
        TriggerObservable ss = new TriggerObservable(false, rospecToAdd.getId());
        ss.addObserver(this);
        this.executionStateList.put(rospecToAdd.getId(), ss);
        this.allROSpecs.put(rospecToAdd.getId(), rospecToAdd);
        this.disabledList.put(rospecToAdd.getId(), rospecToAdd);

        rospecToAdd.setCurrentState(STATE_DISABLED);

        if (rospecToAdd.getStartTrigger() instanceof PeriodicTrigger) {
            PeriodicTrigger pt = (PeriodicTrigger) rospecToAdd.getStartTrigger();
            pt.setTriggerObservable(ss);
            // pt.startTimer();
        }
        if (rospecToAdd.getStartTrigger() instanceof ImmediateTrigger) {
            rospecToAdd.getStartTrigger().setTriggerObservable(ss);
        }
        if (rospecToAdd.getStartTrigger() instanceof GPIWithTimeoutTrigger) {
            rospecToAdd.getStartTrigger().setTriggerObservable(ss);
        }

        logger.info("ROSpec Added: " + rospecToAdd.getId());

        return true;
    }

    /**
     * Moves a ROSpec from the disabled to the inactive queue. Returns false if
     * the ROSpec does not exist or if the ROSpec is not in the inactive queue.
     * In other words, this method puts the ROSpec into the enabled state
     * 
     * @param rospecToEnable
     *            ID of ROspec
     * @return
     */
    public boolean enableROSpec(int rospecToEnable) {
        if (!disabledList.containsKey(rospecToEnable)) {
            return false;
        }
        _ROSpec spec = this.allROSpecs.get(rospecToEnable);

        this.inactiveList.put(rospecToEnable, allROSpecs.get(rospecToEnable));
        this.disabledList.remove(rospecToEnable);
        spec.setCurrentState(STATE_INACTIVE);

        logger.info("ROSpec enabled: " + rospecToEnable);

        /* If the rospec has an Immediate trigger, start it now */
        if (spec.getStartTrigger() instanceof ImmediateTrigger) {
            return startROSpec(rospecToEnable);

        }

        if (spec.getStartTrigger() instanceof PeriodicTrigger) {
            ((PeriodicTrigger) spec.getStartTrigger()).startTimer();
        }

        return true;

    }

    /**
     * Starts the rospec with the given integer ID. If the ROSpec is in the
     * inactive state and no other ROSpecs are currently running, it will return
     * true. If there is a ROSpec running or the current ROSpec is not in the
     * inactive queue, it will return false.
     * 
     * Currently only one ROSpec can run at a time
     * 
     * @param rospecToStart
     *            ID of ROSPec to start
     * @return
     */
    public boolean startROSpec(int rospecToStart) {
        if (!inactiveList.containsKey(rospecToStart) || !this.activeList.isEmpty()) {
            return false;
        }
        _ROSpec rs = allROSpecs.get(rospecToStart);
        TriggerObservable ss = executionStateList.get(rospecToStart);

        /*
         * Check to see if the spec state is true. If it is not, fire the start
         * trigger, which in turn will notify this object and will immediatly
         * call this method again. The next time this method is called however,
         * the state will already be true, and it will actually start the ROSpec
         */

        if (!ss.getState()) {
            ss.fireStartTrigger(this.getClass());
            return true;
        } else {

            this.activeList.add(allROSpecs.get(rospecToStart));
            this.inactiveList.remove(rospecToStart);
            rs.setCurrentState(STATE_ACTIVE);

            /*
             * By default, this rospec should go to the inactive state when
             * stopped
             */
            this.afterStopStateList.put(rospecToStart, STATE_INACTIVE);

            /* Set up stop triggers */
            if (rs.getStopTrigger() instanceof DurationTrigger) {
                DurationTrigger trig = (DurationTrigger) rs.getStopTrigger();
                trig.setTriggerObservable(ss);
            } else if (rs.getStopTrigger() instanceof GPIWithTimeoutTrigger) {
                GPIWithTimeoutTrigger trig = (GPIWithTimeoutTrigger) rs.getStopTrigger();
                trig.setTriggerObservable(ss);
            }

            rs.execute();
            if (logger.isDebugEnabled()) {
                logger.debug("ROSpec started: " + rospecToStart);
            }
            return true;
        }
    }

    /**
     * Stops the given ROSpec. Returns true if the ROSpec was currently running
     * and is stopped correctly. Returns false if the ROSpec was not running or
     * has a problem stopping.
     * 
     * After a rospec is stopped it returns to the inactive state. Then, this
     * method checks the afterStopState list to see if it is supposed to make an
     * additional transition (either to the disabled or deleted state).
     * 
     * @param rospecToStop
     *            ID of ROSpec to stop
     * @return
     */

    public boolean stopROSpec(int rospecToStop) {
        if (!allROSpecs.containsKey(rospecToStop)) {
            return false;
        }
        if (!activeList.contains(allROSpecs.get(rospecToStop))) {
            return false;
        }

        _ROSpec stopSpec = activeList.get(0);

        TriggerObservable ss = executionStateList.get(stopSpec.getId());

        /*
         * Make sure that the control signal is false. If it is true, by setting
         * it to false, it will notify the observer (which is this class), and
         * this method will be called again, except this time the else part of
         * this construct will be run because the control variable will have
         * been set to false.
         */
        if (ss.getState()) {
            ss.fireStopTrigger(this.getClass());
            return true;
        } else {
            stopSpec.stop();

            if (stopSpec.getStopTrigger() instanceof TimerTrigger) {
                ((TimerTrigger) stopSpec.getStopTrigger()).stopTimer();
            }

            activeList.remove(stopSpec);

            inactiveList.put(stopSpec.getId(), stopSpec);
            stopSpec.setCurrentState(STATE_INACTIVE);

            int nextState = this.afterStopStateList.get(rospecToStop);

            if (logger.isDebugEnabled()) {
                logger.debug("ROSpec stopped: " + stopSpec.getId());
            }

            /*
             * If the rospec should make an additional transition, as noted by
             * the nextState variable, do it here
             */
            if (nextState == STATE_DISABLED) {
                return disableROSpec(stopSpec.getId());
            } else if (nextState == STATE_DELETED) {
                return deleteROSpec(stopSpec.getId());
            }

            return true;
        }
    }

    /**
     * Disables the ROSpec, moving it to the disabled state. If the rospec is in
     * the active state, the rosoc must be stopped first. Because stopping
     * happens asyncronously, the afterStopState hashmap is updated for this
     * rospec, and the rospec is stopped using the stopROSpec() method. The
     * stopROSpec() method will then call this method again and the rospec will
     * be disabled
     * 
     * @param rospecToDisable
     *            ID of ROSpec to disable
     * @return
     */
    public boolean disableROSpec(int rospecToDisable) {

        if (!allROSpecs.containsKey(rospecToDisable)) {
            return false;
        }
        _ROSpec rs = this.allROSpecs.get(rospecToDisable);
        /* Make sure RoSpec is either in inactive or active queue */
        if (!inactiveList.containsKey(rospecToDisable)) {
            if (!activeList.contains(rs)) {
                return false;
            }
        }

        /*
         * If the rospec has an Periodic start trigger, stop it so that it
         * doesn't fire anymore
         */
        if (rs.getStartTrigger() instanceof PeriodicTrigger) {
            ((PeriodicTrigger) rs.getStartTrigger()).stopTimer();
        }

        /* If inactive list contains rospec */
        if (inactiveList.containsKey(rospecToDisable)) {

            this.inactiveList.remove(rospecToDisable);
            this.disabledList.put(rospecToDisable, allROSpecs.get(rospecToDisable));
            rs.setCurrentState(STATE_DISABLED);

            logger.info("ROSpec Disabled: " + rs.getId());
            return true;
        }
        /* Else active list contains rospec. Must stop it and then disable it. */
        else {
            this.afterStopStateList.put(rospecToDisable, STATE_DISABLED);
            return stopROSpec(rospecToDisable);
        }

    }

    /**
     * Deletes a ROSpec. Returns false if the ROSpec does not exist. If the
     * ROSpec is in the active state, it must be stopped first. Because stopping
     * happens asyncronoulsy, the afterStopState hashmap is update for this
     * rospec. Then the stopROSpec() method is called. After it stops the
     * ROspec, it will call this method again, which will delete it.
     * 
     * @param rospecToDelete
     *            ID of ROSpec to delete
     * @return
     */
    public boolean deleteROSpec(int rospecToDelete) {
        logger.debug("Trying to delete ROSpec of ID: " + rospecToDelete);
        if (!allROSpecs.containsKey(rospecToDelete)) {
            return false;
        }

        _ROSpec rs = allROSpecs.get(rospecToDelete);

        /*
         * If rospec is active, stop it first, then delete it
         */
        if (this.activeList.contains(rs)) {
            afterStopStateList.put(rospecToDelete, STATE_DELETED);
            return stopROSpec(rospecToDelete);
        }

        if (this.disabledList.containsKey(rospecToDelete)) {
            this.disabledList.remove(rospecToDelete);
        }
        if (this.inactiveList.containsKey(rospecToDelete)) {
            this.inactiveList.remove(rospecToDelete);
        }
        this.allROSpecs.get(rospecToDelete).cleanUp();
        this.allROSpecs.remove(rospecToDelete);
        this.executionStateList.remove(rospecToDelete);
        this.afterStopStateList.remove(rospecToDelete);

        logger.info("ROSpec deleted: " + rospecToDelete);
        return true;
    }

    public void cleanUp() {
        Iterator<_ROSpec> iter = allROSpecs.values().iterator();
        while (iter.hasNext()) {
            _ROSpec current = iter.next();
            this.deleteROSpec(current.getId());
        }

    }

    /**
     * This is the logic for what happens when a specState that is being
     * observed by this object changes. This class should only be notified by
     * TriggerObservable objects
     */
    @SuppressWarnings("unchecked")
    public void update(Observable o, Object arg) {

        ArrayList<Object> extraInfo;
        boolean newState;
        Class callingClass;
        int rospecID;

        try {
            extraInfo = (ArrayList<Object>) arg;
            newState = (Boolean) extraInfo.get(0);
            callingClass = (Class) extraInfo.get(1);
            rospecID = (Integer) extraInfo.get(2);

            // If rospec stop trigger
            if (newState == false) {
                this.stopROSpec(rospecID);
                if (this.allROSpecs.get(rospecID).getStartTrigger() instanceof ImmediateTrigger) {
                    ImmediateTrigger trig = (ImmediateTrigger) this.allROSpecs.get(rospecID).getStartTrigger();
                    trig.restartRoSpec();
                }
            }
            // if rospec start trigger
            else {
                this.startROSpec(rospecID);
            }

        } catch (Exception e) {
            logger.debug("There was an error when trying to update "
                    + "ROSPec.  Check to make sure the TriggerObservable's "
                    + "extra informaiton was formed correctly");
        }

    }

    public HashMap<Integer, _ROSpec> getROSpecs() {
        return this.allROSpecs;
    }
}