001package jmri.jmrix.powerline.cm11;
002
003import jmri.Sensor;
004import jmri.jmrix.powerline.SerialReply;
005import jmri.jmrix.powerline.SerialTrafficController;
006import jmri.jmrix.powerline.X10Sequence;
007import org.slf4j.Logger;
008import org.slf4j.LoggerFactory;
009
010/**
011 * Manage the system-specific Sensor implementation.
012 * <p>
013 * System names are "PSann", where a is the unit id, nn is the unit number
014 * without padding.
015 * <p>
016 * Sensors are numbered from 1.
017 *
018 * @author Bob Jacobsen Copyright (C) 2003, 2006, 2007, 2008
019 * @author Ken Cameron, (C) 2009, sensors from poll replies Converted to
020 * multiple connection
021 * @author kcameron Copyright (C) 2011
022 */
023public class SpecificSensorManager extends jmri.jmrix.powerline.SerialSensorManager {
024
025    public SpecificSensorManager(SerialTrafficController tc) {
026        super(tc);
027        this.tc = tc;
028    }
029
030    SerialTrafficController tc = null;
031
032    /**
033     * Process a reply to a poll of Sensors of one node
034     */
035    @Override
036    public synchronized void reply(SerialReply r) {
037        // process for updates
038        processForPollReq(r);
039    }
040
041    /**
042     * These values need to persist between calls as the address is a different
043     * reply from the command packet and timing might have them in separate
044     * reads
045     */
046    private String newHouseCode = null;
047    private int newCmdCode = -1;
048    private int newAddrCode = -1;
049
050    private void processForPollReq(SerialReply l) {
051        // process the POLL_REQ and update/create sensors as needed
052        if ((l.getElement(0) & 0xFF) == Constants.POLL_REQ) {
053            // must be received data
054            int last = (l.getElement(1) & 0xFF) + 1;
055            int bits = (l.getElement(2) & 0xFF);
056            Sensor sensor = null;
057            for (int i = 3; i <= last; i++) {
058                int dat = l.getElement(i) & 0xFF;
059                if ((bits & 0x01) != 0) {
060                    // this is a function byte, so the address came from prior pass
061                    newHouseCode = X10Sequence.houseValueToText(X10Sequence.decode((dat >> 4) & 0x0F));
062                    newCmdCode = dat & 0x0f;
063
064                    if (newHouseCode != null && (newCmdCode == X10Sequence.FUNCTION_ALL_LIGHTS_OFF || newCmdCode == X10Sequence.FUNCTION_ALL_UNITS_OFF || newCmdCode == X10Sequence.FUNCTION_ALL_LIGHTS_ON)) {
065                        // some sort of 'global' command, process for all matching the house code
066                        getNamedBeanSet().forEach(sensorInSet -> {
067                            String sName = sensorInSet.getSystemName();
068                            if (newHouseCode.compareTo(tc.getAdapterMemo().getSerialAddress().houseCodeFromSystemName(sName)) == 0) {
069                                try {
070                                    if (newCmdCode == X10Sequence.FUNCTION_ALL_LIGHTS_OFF || newCmdCode == X10Sequence.FUNCTION_ALL_UNITS_OFF) {
071                                        sensorInSet.setKnownState(Sensor.INACTIVE);
072                                    } else {
073                                        sensorInSet.setKnownState(Sensor.ACTIVE);
074                                    }
075                                } catch (jmri.JmriException e) {
076                                    if (newCmdCode == X10Sequence.FUNCTION_ALL_LIGHTS_OFF || newCmdCode == X10Sequence.FUNCTION_ALL_UNITS_OFF) {
077                                        log.error("Exception setting {} sensor INACTIVE", sName, e);
078                                    } else {
079                                        log.error("Exception setting {} sensor ACTIVE", sName, e);
080                                    }
081                                }
082                            }
083                        });
084                    } else {
085                        // was not a global command, so might be a sensor
086                        if (newAddrCode > 0) {
087                            String sysName = getSystemPrefix() + "S" + newHouseCode + newAddrCode;
088                            try {
089                                sensor = provideSensor(sysName);
090                            } catch(java.lang.IllegalArgumentException iae){
091                                // if provideSensor fails, it will throw an IllegalArgumentException, so catch that,log it if debugging is enabled, and then re-throw it.
092                                log.debug("Attempt access sensor {} failed", sysName);
093                                throw iae;
094                            }
095                            if (newCmdCode == X10Sequence.FUNCTION_ON || newCmdCode == X10Sequence.FUNCTION_BRIGHT || newCmdCode == X10Sequence.FUNCTION_STATUS_ON) {
096                                try {
097                                    sensor.setKnownState(Sensor.ACTIVE);
098                                } catch (jmri.JmriException e) {
099                                    log.error("Exception setting {} sensor ACTIVE", sysName, e);
100                                }
101                            }
102                            if (newCmdCode == X10Sequence.FUNCTION_OFF || newCmdCode == X10Sequence.FUNCTION_DIM || newCmdCode == X10Sequence.FUNCTION_STATUS_OFF) {
103                                try {
104                                    sensor.setKnownState(Sensor.INACTIVE);
105                                } catch (jmri.JmriException e) {
106                                    log.error("Exception setting {} sensor INACTIVE", sysName, e);
107                                }
108                            }
109
110                            // if we decide we want to add sensors automatically when seen on the wire, this is the place
111                        }
112                    }
113                    // used the pair of address/function, so clear them
114                    newHouseCode = null;
115                    newCmdCode = -1;
116                    newAddrCode = -1;
117                } else {
118                    // this is an address byte, so just save it
119                    newHouseCode = X10Sequence.houseValueToText(X10Sequence.decode((dat >> 4) & 0x0F));
120                    newAddrCode = X10Sequence.decode(dat & 0x0f);
121                }
122                bits = bits >> 1;  // shift over before next byte
123            }
124        }
125    }
126
127    private final static Logger log = LoggerFactory.getLogger(SpecificSensorManager.class);
128
129}