001package jmri.jmrix.dccpp;
002
003import jmri.implementation.AbstractLight;
004import org.slf4j.Logger;
005import org.slf4j.LoggerFactory;
006
007/**
008 * Implementation of the Light Object for DCC++
009 * <p>
010 * NOTE: This is a simplification of the DCCppTurnout class.
011 * <p>
012 * Based in part on SerialLight.java
013 *
014 * @author Paul Bender Copyright (C) 2008-2010
015 * @author Mark Underwood Copyright (C) 2015
016 */
017public class DCCppLight extends AbstractLight implements DCCppListener {
018
019    private DCCppTrafficController tc = null;
020    private DCCppLightManager lm = null;
021
022    /**
023     * Create a Light object, with only system name.
024     * <p>
025     * 'systemName' was previously validated in DCCppLightManager
026     *
027     * @param tc         the traffic controller for the connection
028     * @param lm         the managing LightManager for this Light
029     * @param systemName the system name for this Light
030     */
031    public DCCppLight(DCCppTrafficController tc, DCCppLightManager lm, String systemName) {
032        super(systemName);
033        this.tc = tc;
034        this.lm = lm;
035        // Initialize the Light
036        initializeLight(systemName);
037    }
038
039    /**
040     * Create a Light object, with both system and user names.
041     * <p>
042     * 'systemName' was previously validated in DCCppLightManager
043     *
044     * @param tc         the traffic controller for the connection
045     * @param lm         the managing LightManager for this Light
046     * @param systemName the system name for this Light
047     * @param userName   the user name for this Light
048     */
049    public DCCppLight(DCCppTrafficController tc, DCCppLightManager lm, String systemName, String userName) {
050        super(systemName, userName);
051        this.tc = tc;
052        this.lm = lm;
053        // Initialize the Light
054        initializeLight(systemName);
055    }
056
057    /**
058     * Dispose of the light object.
059     */
060    @Override
061    public void dispose() {
062        tc.removeDCCppListener(DCCppInterface.FEEDBACK | DCCppInterface.COMMINFO | DCCppInterface.CS_INFO, this);
063        super.dispose();
064    }
065
066    /**
067     * Initialize the light object's parameters.
068     */
069    private synchronized void initializeLight(String systemName) {
070        // Extract the Bit from the name
071        mAddress = lm.getBitFromSystemName(systemName);
072        // Set initial state
073        setState(OFF);
074        // At construction, register for messages
075        tc.addDCCppListener(DCCppInterface.FEEDBACK | DCCppInterface.COMMINFO | DCCppInterface.CS_INFO, this);
076    }
077
078    /**
079     * Sets up system dependent instance variables and set system independent
080     * instance variables to default values.
081     * <p>
082     * Note: most instance variables are in AbstractLight.java
083     */
084
085    /**
086     * System dependent instance variables
087     */
088    //protected int mState = OFF;  // current state of this light
089    //private int mOldState =mState; // save the old state
090    int mAddress = 0;            // accessory output address
091
092    /* Internal State Machine states. */
093    static final int OFFSENT = 1;
094    static final int COMMANDSENT = 2;
095    static final int IDLE = 0;
096    //private int InternalState = IDLE;
097
098    /**
099     * Set the current state of this Light.
100     * This routine requests the hardware to change.
101     */
102    @Override
103    synchronized public void setState(int newState) {
104        if (newState != ON && newState != OFF) {
105            // Unsupported state
106            log.warn("Unsupported state {} requested for light {}", newState, getSystemName());
107            return;
108        }
109
110        log.debug("Light Set State: mstate = {} newstate = {}", mState, newState);
111
112        // get the right packet
113        if (mAddress > 0) {
114            boolean state = (newState == jmri.Light.ON);
115            DCCppMessage msg = DCCppMessage.makeAccessoryDecoderMsg(mAddress, state);
116            //InternalState = COMMANDSENT;
117            tc.sendDCCppMessage(msg, this);
118
119            if (newState != mState) {
120                int oldState = mState;
121                mState = newState;
122                // notify listeners, if any
123                firePropertyChange("KnownState", oldState, newState);
124            }
125        }
126    }
127
128    /**
129     * {@inheritDoc}
130     * Handle an incoming message from the DCC++ Base Station.
131     * <p>
132     * NOTE: We aren't registered as a listener, so this is only triggered
133     * when we send out a message
134     */
135    @Override
136    synchronized public void message(DCCppReply l) {
137        log.debug("received message: {}", l);
138        // We don't expect a reply, so we don't do anything with replies.
139    }
140
141    /**
142     * {@inheritDoc}
143     * Listen for messages to the DCC++ Base Station.
144     */
145    @Override
146    public void message(DCCppMessage l) {
147        // messages not handled by DCCpp lights
148    }
149
150    // Handle a timeout notification
151    @Override
152    public void notifyTimeout(DCCppMessage msg) {
153        log.debug("Notified of timeout on message '{}'", msg);
154    }
155
156    private final static Logger log = LoggerFactory.getLogger(DCCppLight.class);
157
158}