001package jmri.jmrix.roco.z21;
002
003import jmri.Sensor;
004import org.slf4j.Logger;
005import org.slf4j.LoggerFactory;
006
007/**
008 * Z21CanSensor implements the Sensor interface
009 * for Can connected sensors on Roco Z21 systems.
010 *
011 * @author Paul Bender Copyright (C) 2018
012 */
013public class Z21CanSensor extends jmri.implementation.AbstractSensor implements Z21Listener {
014
015    private Z21SystemConnectionMemo _memo;
016
017    private int networkID=0; // CAN network ID associated with this reporter's module.
018    private int moduleAddress=-1; // User assigned address associated with this reporter's module.
019    private int port; // module port (0-7) associated with this reporter.
020
021    /**  
022     * Create a new Z21CanSensor.
023     *
024     * @param systemName the system name of the new reporter.
025     * @param userName the user name of the new reporter.
026     * @param memo an instance of Z21SystemConnectionMemo this reporter 
027     *             is associated with.
028     *
029     */
030    public Z21CanSensor(String systemName,String userName,Z21SystemConnectionMemo memo){
031        super(systemName,userName);
032        _memo = memo;
033        // register for messages
034        _memo.getTrafficController().addz21Listener(this);
035        //Address format passed is in the form of moduleAddress:pin 
036        try {
037            setIdentifiersFromSystemName(systemName);
038        } catch (NumberFormatException ex) {
039           log.debug("Unable to convert {} into the cab and input format of nn:xx",systemName);
040           throw new IllegalArgumentException("requires mm:pp format address.");
041        }
042    }
043
044    private void setIdentifiersFromSystemName(String systemName) {
045        String moduleAddressText = Z21CanBusAddress.getEncoderAddressString(systemName, _memo.getSystemPrefix());
046        try {
047            moduleAddress = Integer.parseInt(moduleAddressText);
048        } catch (NumberFormatException ex) {
049            // didn't parse as a decimal, check to see if network ID
050            // was used instead.
051            networkID = Integer.parseInt(moduleAddressText, 16);
052        }
053        port = Z21CanBusAddress.getBitFromSystemName(systemName, _memo.getSystemPrefix());
054    }
055
056    /**
057     *     request an update from the layout.
058     */
059    @Override
060    public void requestUpdateFromLayout(){
061       if(networkID==0){
062          return; // no networkID has been set yet.
063       }
064       _memo.getTrafficController().sendz21Message(Z21Message.getLanCanDetector(networkID),this);
065    }
066
067    // the Z21 Listener interface
068
069    /**
070     * Member function that will be invoked by a z21Interface implementation to
071     * forward a z21 message from the layout.
072     *
073     * @param msg The received z21 reply. Note that this same object may be
074     * presented to multiple users. It should not be modified here.
075     */
076    @Override
077    public void reply(Z21Reply msg){
078         // for incoming messages all the reporter cares about is
079         // LAN_CAN_DETECTOR messages.
080         if(msg.isCanSensorMessage()){
081            int netID = ( msg.getElement(4)&0xFF) + ((msg.getElement(5)&0xFF) << 8);
082            int address = ( msg.getElement(6)&0xFF) + ((msg.getElement(7)&0xFF) << 8);
083            int msgPort = ( msg.getElement(8) & 0xFF);
084            if(!messageForSensor(address,netID,msgPort)){
085                return;
086            }
087            // status message, use to set state.
088            int value1 = (msg.getElement(10)&0xFF) + ((msg.getElement(11)&0xFF) << 8);
089            log.debug("value {}",value1);
090            if(value1 == 0x0000) {
091                log.debug("Free without voltage");
092                setOwnState(Sensor.INACTIVE);
093            } else if(value1 == 0x0100) {
094                log.debug("Free with voltage");
095                setOwnState(Sensor.INACTIVE);
096            } else if(value1 == 0x1000) {
097                log.debug("Busy without voltage");
098                setOwnState(Sensor.ACTIVE);
099            } else if(value1 == 0x1100) {
100                log.debug("Busy with voltage");
101                setOwnState(Sensor.ACTIVE);
102            } else if(value1 == 0x1201) {
103                log.debug("Busy Overload 1");
104                setOwnState(Sensor.ACTIVE);
105            } else if(value1 == 0x1202) {
106                log.debug("Busy Overload 2");
107                setOwnState(Sensor.ACTIVE);
108            } else if(value1 == 0x1203) {
109                log.debug("Busy Overload 3");
110                setOwnState(Sensor.ACTIVE);
111            }
112         }
113    }
114
115    private boolean messageForSensor(int address,int netId,int msgPort){
116        return (address == moduleAddress || netId == networkID) &&  msgPort == port;
117    }
118
119    /**
120     * {@inheritDoc}
121     */
122    @Override
123    public void message(Z21Message msg){
124         // we don't need to handle outgoing messages, so just ignore them.
125    }
126
127    @Override
128    public void dispose(){
129        _memo.getTrafficController().removez21Listener(this);
130        super.dispose();
131    }
132
133    private static final Logger log = LoggerFactory.getLogger(Z21CanSensor.class);
134
135}