001package jmri.jmrix.lenz;
002
003import jmri.Sensor;
004import jmri.implementation.AbstractSensor;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008/**
009 * Extend jmri.AbstractSensor for XpressNet layouts.
010 *
011 * @author Paul Bender Copyright (C) 2003-2010
012 */
013public class XNetSensor extends AbstractSensor implements XNetListener {
014
015    private boolean statusRequested = false;
016
017    private int address;
018    private int baseaddress; /* The result of integer division of the 
019     sensor address by 8 */
020
021    private int nibble;      /* Is this sensor in the upper or lower 
022     nibble for the feedback encoder */
023
024    private String systemName;
025
026    protected XNetTrafficController tc;
027
028    public XNetSensor(String systemName, String userName, XNetTrafficController controller, String prefix) {
029        super(systemName, userName);
030        tc = controller;
031        init(systemName, prefix);
032    }
033
034    public XNetSensor(String systemName, XNetTrafficController controller, String prefix) {
035        super(systemName);
036        tc = controller;
037        init(systemName, prefix);
038    }
039
040    /**
041     * Common initialization for all constructors.
042     * @param id System ID
043     * @param prefix System name prefix
044     */
045    private void init(String id, String prefix) {
046        // store address
047        systemName = id;
048        address = XNetAddress.getBitFromSystemName(systemName, prefix);
049        // calculate the base address, the nibble, and the bit to examine
050        baseaddress = ((address - 1) / 8);
051        int temp = (address - 1) % 8;
052        if (temp < 4) {
053            // This address is in the lower nibble
054            nibble = 0x00;
055        } else {
056            nibble = 0x10;
057        }
058        if (log.isDebugEnabled()) {
059            log.debug("Created Sensor {} (Address {},  position {})",
060                    systemName, baseaddress,
061                    (((address - 1) % 8) + 1)
062            );
063        }
064        // Finally, request the current state from the layout.
065        tc.getFeedbackMessageCache().requestCachedStateFromLayout(this);
066    }
067
068    /**
069     * Request an update on status by sending an XpressNet message.
070     */
071    @Override
072    public void requestUpdateFromLayout() {
073        // To do this, we send an XpressNet Accessory Decoder Information
074        // Request.
075        // The generated message works for Feedback modules and turnouts 
076        // with feedback, but the address passed is translated as though it 
077        // is a turnout address.  As a result, we substitute our base 
078        // address in for the address. after the message is returned.
079        XNetMessage msg = XNetMessage.getFeedbackRequestMsg(baseaddress,
080                (nibble == 0x00));
081        msg.setElement(1, baseaddress);
082        msg.setParity();
083        synchronized (this) {
084            statusRequested = true;
085        }
086        tc.sendXNetMessage(msg, null); // The reply is treated as a broadcast
087        // and is returned using the manager.
088    }
089
090    /**
091     * initmessage is a package protected class which allows the Manger to send
092     * a feedback message at initialization without changing the state of the
093     * sensor with respect to whether or not a feedback request was sent. This
094     * is used only when the sensor is created by on layout feedback.
095     * @param l Reply message
096     */
097    synchronized void initmessage(XNetReply l) {
098        boolean oldState = statusRequested;
099        message(l);
100        statusRequested = oldState;
101    }
102
103    /**
104     * Implementing classes will typically have a function/listener to get
105     * updates from the layout, which will then call public void
106     * firePropertyChange(String propertyName, Object oldValue, Object newValue)
107     * _once_ if anything has changed state (or set the commanded state
108     * directly)
109     * @param l Reply message
110     */
111    @Override
112    public synchronized void message(XNetReply l) {
113        if (log.isDebugEnabled()) {
114            log.debug("received message: {}", l);
115        }
116        Boolean opt = l.selectModuleFeedback(address);
117        if (opt != null) {
118            if (log.isDebugEnabled()) {
119                        log.debug("Message for sensor {} (Address {} position {})", systemName, baseaddress, address - (baseaddress * 8));
120            }
121            if (opt ^ _inverted) {
122                setOwnState(Sensor.ACTIVE);
123            } else {
124                setOwnState(Sensor.INACTIVE);
125            }
126        }
127    }
128
129    /**
130     * Listen for the messages to the LI100/LI101.
131     * @param l message to process
132     */
133    @Override
134    public void message(XNetMessage l) {
135        // not currently listening for outgoing messages.
136    }
137
138    /**
139     * Handle a timeout notification.
140     * @param msg The message that timed out
141     */
142    @Override
143    public void notifyTimeout(XNetMessage msg) {
144        if (log.isDebugEnabled()) {
145            log.debug("Notified of timeout on message: {}", msg);
146        }
147    }
148
149    /**
150     * Package protected routine to get the Sensor Number.
151     * @return current Sensor address number
152     */
153    int getNumber() {
154        return address;
155    }
156
157    /**
158     * Package protected routine to get the Sensor Base Address.
159     * @return the Sensor base address
160     */
161    int getBaseAddress() {
162        return baseaddress;
163    }
164
165    /**
166     * Package protected routine to get the Sensor Nibble.
167     * @return contents of sensor nibble
168     */
169    int getNibble() {
170        return nibble;
171    }
172
173    private static final Logger log = LoggerFactory.getLogger(XNetSensor.class);
174
175}