001package jmri.jmrix.dccpp;
002
003import jmri.Sensor;
004import jmri.implementation.AbstractSensor;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008import javax.annotation.concurrent.GuardedBy;
009
010/**
011 * Extend jmri.AbstractSensor for DCC++ layouts.
012 *
013 * @author Paul Bender Copyright (C) 2003-2010
014 * @author Mark Underwood Copyright (C) 2015
015 *
016 * Based on XNetSensor
017 */
018public class DCCppSensor extends AbstractSensor implements DCCppListener {
019
020    private boolean statusRequested = false;
021
022    private int address; // NOTE: For DCC++ this is the Base Station index #
023    //private int baseaddress; /* The result of integer division of the 
024    // sensor address by 8 */
025
026    @GuardedBy("this")
027    private int pin;
028    @GuardedBy("this")
029    private boolean pullup;
030
031    //private int nibble;      /* Is this sensor in the upper or lower 
032    //nibble for the feedback encoder */
033
034    private String systemName;
035
036    protected DCCppTrafficController tc;
037    
038    public DCCppSensor(String systemName, String userName, DCCppTrafficController controller) {
039        super(systemName, userName);
040        tc = controller;
041        init(systemName);
042    }
043
044    public DCCppSensor(String systemName, DCCppTrafficController controller) {
045        super(systemName);
046        tc = controller;
047        init(systemName);
048    }
049    
050    public synchronized boolean getPullup() { return(pullup); }
051    public synchronized int getPin() { return(pin); }
052    public int getIndex() { return(address); }
053
054    /**
055     * Common initialization for both constructors
056     */
057    private void init(String id) {
058        // store address
059        systemName = id;
060        //prefix = jmri.InstanceManager.getDefault(jmri.jmrix.dccpp.DCCppSensorManager.class).getSystemPrefix();
061        address = Integer.parseInt(id.substring(id.lastIndexOf('S') + 1));
062        log.debug("New sensor system name {} address {}", this.getSystemName(), address);
063        log.debug("Created Sensor {}", systemName);
064        // Finally, request the current state from the layout.
065        //this.requestUpdateFromLayout();
066        //tc.getFeedbackMessageCache().requestCachedStateFromLayout(this);
067
068    }
069
070    /**
071     * request an update on status by sending a DCC++ message
072     */
073    @Override
074    public void requestUpdateFromLayout() {
075        // Yeah... this isn't really supported.  Yet.
076        //
077        // To do this, we send an DCC++ Accessory Decoder Information 
078        // Request.
079        // The generated message works for Feedback modules and turnouts 
080        // with feedback, but the address passed is translated as though it 
081        // is a turnout address.  As a result, we substitute our base 
082        // address in for the address. after the message is returned.
083 /*
084        DCCppMessage msg = DCCppMessage.getFeedbackRequestMsg(baseaddress,
085                (nibble == 0x00));
086        msg.setElement(1, baseaddress);
087        msg.setParity();
088        synchronized (this) {
089            statusRequested = true;
090        }
091        tc.sendDCCppMessage(msg, null); // The reply is treated as a broadcast
092        // and is returned using the manager.
093 */
094    }
095
096    /**
097     * initmessage is a package protected class which allows the Manger to send
098     * a feedback message at initialization without changing the state of the
099     * sensor with respect to whether or not a feedback request was sent. This
100     * is used only when the sensor is created by on layout feedback.
101     * @param l Init message
102     */
103    synchronized void initmessage(DCCppReply l) {
104        boolean oldState = statusRequested;
105        message(l);
106        statusRequested = oldState;
107    }
108
109    /**
110     * {@inheritDoc}
111     * implementing classes will typically have a function/listener to get
112     * updates from the layout, which will then call public void
113     * firePropertyChange(String propertyName, Object oldValue, Object newValue)
114     * _once_ if anything has changed state (or set the commanded state
115     * directly)
116     */
117    @Override
118    public synchronized void message(DCCppReply l) {
119         if (l.isSensorDefReply()) {
120            log.debug("Sensor Def Reply received: '{}'", l);
121            if (l.getSensorDefNumInt() == address) {
122                log.debug("Def Message for sensor {} (Pin {})", systemName, address);
123                setOwnState(Sensor.UNKNOWN);
124                l.getProperties().forEach((key, value) -> {
125                    this.setProperty(key, value); //copy the defining properties from message to sensor
126                });
127            }
128        } else if (l.isSensorReply() && (l.getSensorNumInt() == address)) {
129                log.debug("Message for sensor {} (Pin {})", systemName, address);
130            if (l.getSensorIsActive()) {
131                setOwnState(_inverted ? Sensor.INACTIVE : Sensor.ACTIVE);
132            } else if (l.getSensorIsInactive()){
133                setOwnState(_inverted ? Sensor.ACTIVE : Sensor.INACTIVE);
134            } else {
135                setOwnState(Sensor.UNKNOWN);
136            }
137        }
138    }
139
140    /**
141     * {@inheritDoc}
142     * Listen for the messages to the Base Station... but ignore them.
143     *
144     * @param l the message heard
145     */
146    @Override
147    public void message(DCCppMessage l) {
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    @Override
157    public void dispose() {
158        super.dispose();
159    }
160
161    // package protected routine to get the Sensor Number
162    int getNumber() {
163        return address;
164    }
165
166    // package protected routine to get the Sensor Base Address
167    int getBaseAddress() {
168        //return baseaddress;
169        return(address);
170    }
171
172    // package protected routine to get the Sensor Nibble
173    int getNibble() {
174        //return nibble;
175        return(0);
176    }
177
178    private final static Logger log = LoggerFactory.getLogger(DCCppSensor.class);
179
180}