/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package simulator.elevatorcontrol;


import jSimPack.SimTime;
import simulator.elevatormodules.DoorClosedCanPayloadTranslator;
import simulator.framework.Controller;
import simulator.framework.Direction;
import simulator.framework.Hallway;
import simulator.framework.ReplicationComputer;
import simulator.framework.Side;
import simulator.payloads.CanMailbox;
import simulator.payloads.HallCallPayload;
import simulator.payloads.HallLightPayload;
import simulator.payloads.translators.BooleanCanPayloadTranslator;


/**
 * This testlight uses hall calls and hall lights, so it can be instantiated for 
 * any [floor,hallway,direction] combination.
 @author Justin Ray
 */
public class TestLight extends Controller {

    // local physical state
    private HallCallPayload localHallCall;
    private HallLightPayload localHallLight;
    
    // network interface
    // receive hall call from the other button
    private CanMailbox networkHallLightOut;
    // translator for the hall light message -- this is a generic translator
    private BooleanCanPayloadTranslator mHallLight;

    // received door closed message
    private CanMailbox networkDoorClosedFrontLeft;
    // translator for the doorClosed message -- this translator is specific
    // to this messages, and is provided the elevatormodules package
    private DoorClosedCanPayloadTranslator mDoorClosedFrontLeft;
    
    // these variables keep track of which instance this is.
    private final Hallway hallway;
    private final Direction direction;
    private final int floor;
    
    // store the period for the controller
    private SimTime period;
    
    // additional internal state variables
    private SimTime counter = SimTime.ZERO;

    // internal constant declarations
    // if the flash is one period, then each on/off portion should be 500ms
    private final static SimTime flashHalfPeriod = new SimTime(500,
            SimTime.SimTimeUnit.MILLISECOND);

    // enumerate states
    private enum State {
        STATE_FLASH_OFF, STATE_FLASH_ON, STATE_DOOR_NOT_CLOSED,
    }
    // state variable initialized to the initial state FLASH_OFF
    private State state = State.STATE_FLASH_OFF;

    /*
     * The arguments listed in the .cf configuration file should corrsepond to 
     * the constructor arguments.
     */
    public TestLight(SimTime period, int floor, Hallway hallway, Direction direction, boolean verbose) {
        // call to the Controller superclass constructor is required
        super("TestLight[" + floor + "," + hallway + "," + direction + "]",
                verbose);
        
        // stored the constructor arguments in internal state
        this.period = period;
        this.hallway = hallway;
        this.direction = direction;
        this.floor = floor;
        
        /*
         * The log() method is inherited from the Controller class.  It takes an
         * array of objects which will be converted to strings and concatenated
         * only if the log message is actually written.  
         
         * For performance reasons, call with comma-separated lists, e.g.:
         *   log("object=",object);
         * Do NOT call with concatenated objects like:
         *   log("object=" + object);
         */
        log("Created testlight with period = ", period);

        // initialize physical state
        // create a payload object for this floor,hallway,direction
        localHallCall = new HallCallPayload(floor, hallway, direction);
        // register the payload with the physical interface -- it will be updated
        // periodically when the hall call button state is modified.
        physicalInterface.registerTimeTriggered(localHallCall);
        // create a payload object for this floor,hallway,direction
        localHallLight = new HallLightPayload(floor, hallway, direction);
        // register the payload to be sent periodically -- whatever value is stored
        // in the localHallLight object will be sent out periodically with the period
        // specified by the period parameter.
        physicalInterface.sendTimeTriggered(localHallLight, period);

        // initialize network interface        
        // create a can mailbox
        networkHallLightOut = new CanMailbox(
                MessageDictionary.HALL_LIGHT_BASE_CAN_ID
                        + ReplicationComputer.computeReplicationId(floor,
                        hallway, direction));

        /*
         * Create a translator with a reference to the CanMailbox.  Use the 
         * translator to read and write values to the mailbox
         
         * Note the use of the BooleanCanPayloadTranslator.  This translator, along with
         * IntegerCanPayloadTranslator, are provided for your use.  They are not
         * very bandwidth efficient, but they will be adequate for the first part
         * of the course.  When we get to network scheduling, you may wish to write
         * your own translators.
         */
        mHallLight = new BooleanCanPayloadTranslator(networkHallLightOut);
        // register the mailbox to have its value broadcast on the network periodically
        // with a period specified by the period parameter.
        canInterface.sendTimeTriggered(networkHallLightOut, period);

        /*
         * Registration for the DoorClosed message is similar to the mHallLight message
         
         * To register for network messages from the smart sensors or other objects
         * defined in elevator modules, use the translators already defined in
         * elevatormodules package.  These translators are specific to one type
         * of message.
         */
        networkDoorClosedFrontLeft = new CanMailbox(
                MessageDictionary.DOOR_CLOSED_SENSOR_BASE_CAN_ID
                        + ReplicationComputer.computeReplicationId(Hallway.FRONT,
                        Side.LEFT));
        mDoorClosedFrontLeft = new DoorClosedCanPayloadTranslator(
                networkDoorClosedFrontLeft, Hallway.FRONT, Side.LEFT);
        // register to receive periodic updates to the mailbox via the CAN network
        // the period of updates will be determined by the sender of the message
        canInterface.registerTimeTriggered(networkDoorClosedFrontLeft);

        /* issuing the timer start method with no callback data means a NULL value 
         * will be passed to the callback later.  Use the callback data to distinguish
         * callbacks from multiple calls to timer.start() (e.g. if you have multiple
         * timers.
         */
        timer.start(period);
    }

    /*
     * The timer callback is where the main controller code is executed.  For time
     * triggered design, this consists mainly of a switch block with a case blcok for
     * each state.  Each case block executes actions for that state, then executes
     * a transition to the next state if the transition conditions are met.
     */
    public void timerExpired(Object callbackData) {
        State newState = state;

        switch (state) {
        case STATE_FLASH_OFF:
            // state actions for 'FLASH OFF'
            localHallLight.set(false);
            mHallLight.set(false);
            counter = SimTime.add(counter, period);
                
            // transitions -- note that transition conditions are mutually exclusive
            // #transition 'TL.T.1'
            if (counter.isGreaterThanOrEqual(flashHalfPeriod)
                    && mDoorClosedFrontLeft.getValue() == true) {
                newState = State.STATE_FLASH_ON;
                // #transition 'TL.T.4'
            else if (mDoorClosedFrontLeft.getValue() == false) {
                newState = State.STATE_DOOR_NOT_CLOSED;
            else {
                newState = state;
            }
            break;

        case STATE_FLASH_ON:
            // state actions for 'FLASH ON'
            localHallLight.set(true);
            mHallLight.set(true);
            counter = SimTime.subtract(counter, period);
                
            // transitions -- note that transition conditions are mutually exclusive
            // #transition 'TL.T.2'
            if (counter.isLessThanOrEqual(SimTime.ZERO)
                    && mDoorClosedFrontLeft.getValue() == true) {
                newState = State.STATE_FLASH_OFF;
                // #transition 'TL.T.3'
            else if (mDoorClosedFrontLeft.getValue() == false) {
                newState = State.STATE_DOOR_NOT_CLOSED;
            else {
                newState = state;
            }
            break;

        case STATE_DOOR_NOT_CLOSED:
            // state actions for 'DOOR NOT CLOSED'
            localHallLight.set(true);
            mHallLight.set(true);
            counter = SimTime.ZERO;
                
            // transitions -- note that transition conditions are mutually exclusive
            // #transition 'TL.T.5'
            if (mDoorClosedFrontLeft.getValue() == true
                    && localHallCall.pressed == true) {
                newState = State.STATE_FLASH_OFF;
            else {
                newState = state;
            }
            break;

        default:
            throw new RuntimeException("State " + state + " was not recognized.");
        }
        
        // log the results of this iteration
        if (state == newState) {
            log("remains in state: ", state);
        else {
            log("Transition:", state, "->", newState);
        }

        // update the state variable
        state = newState;

        // schedule the next iteration of the controller
        timer.start(period);
    }
    
    public static void main(String[]args) {
        System.out.println(
                "car light Can ID:"
                        + Integer.toHexString(
                                MessageDictionary.HALL_LIGHT_BASE_CAN_ID
                                        + ReplicationComputer.computeReplicationId(
                                                4, Hallway.FRONT, Direction.DOWN)));
        System.out.println(
                "door sensor Can ID:"
                        + Integer.toHexString(
                                MessageDictionary.DOOR_CLOSED_SENSOR_BASE_CAN_ID
                                        + ReplicationComputer.computeReplicationId(
                                                Hallway.FRONT, Side.LEFT)));
    }
}
Java2html