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