001package jmri.jmrix.bidib; 002 003import jmri.NamedBean; 004import jmri.Sensor; 005import jmri.implementation.AbstractSensor; 006import org.bidib.jbidibc.messages.enums.LcOutputType; 007import org.bidib.jbidibc.messages.enums.OccupationState; 008import org.bidib.jbidibc.messages.utils.NodeUtils; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * Extend jmri.AbstractSensor for BiDiB systems 014 * 015 * @author Bob Jacobsen Copyright (C) 2003 016 * @author Eckart Meyer Copyright (C) 2019-2023 017 */ 018 019public class BiDiBSensor extends AbstractSensor implements BiDiBNamedBeanInterface { 020 021 private BiDiBAddress addr; 022 private final char typeLetter; 023 private BiDiBTrafficController tc = null; 024 //MessageListener messageListener = null; 025 private BiDiBOutputMessageHandler messageHandler = null; 026 027 // for LC Input Sensors 028 LcOutputType lcType; //cached type from portConfigX or fixed drin type based address 029 030 /** 031 * Create a Sensor object from system name. 032 * 033 * @param systemName name of added Sensor 034 * @param mgr Sensor Manager, we get the memo object and the type letter (S) from the manager 035 */ 036 public BiDiBSensor(String systemName, BiDiBSensorManager mgr) { 037 super(systemName); 038 tc = mgr.getMemo().getBiDiBTrafficController(); 039 log.debug("New Sensor: {}", systemName); 040 addr = new BiDiBAddress(systemName, mgr.typeLetter(), mgr.getMemo()); 041 log.info("New SENSOR created: {} -> {}", systemName, addr); 042 typeLetter = mgr.typeLetter(); 043 044 createSensorListener(); 045 046 messageHandler.sendQueryConfig(); 047 } 048 049 /** 050 * {@inheritDoc} 051 */ 052 @Override 053 public BiDiBAddress getAddr() { 054 return addr; 055 } 056 057 /** 058 * {@inheritDoc} 059 */ 060 @Override 061 public void finishLoad() { 062 messageHandler.sendQuery(); 063 } 064 065 /** 066 * {@inheritDoc} 067 */ 068 @Override 069 public void nodeNew() { 070 //create a new BiDiBAddress 071 addr = new BiDiBAddress(getSystemName(), typeLetter, tc.getSystemConnectionMemo()); 072 if (addr.isValid()) { 073 log.info("new sensor address created: {} -> {}", getSystemName(), addr); 074 if (addr.isPortAddr()) { 075 messageHandler.sendQueryConfig(); 076 messageHandler.waitQueryConfig(); 077 } 078 if (!addr.isFeedbackAddr()) { 079 // sensor is not a feedback (BiDiB BM), may be a port input - feedback will be queried in a bulk reqeust from sensor manager 080 messageHandler.sendQuery(); 081 } 082 } 083 } 084 085 /** 086 * {@inheritDoc} 087 */ 088 @Override 089 public void nodeLost() { 090 setOwnState(NamedBean.UNKNOWN); 091 } 092 093 /** 094 * Request an update on status. 095 */ 096 @Override 097 public void requestUpdateFromLayout() { 098 if (addr.isValid()) { 099 log.info("Query sensor status from BiDiB: addr: {}", addr); 100 messageHandler.sendQuery(); 101 } 102 } 103 104 /** 105 * Dispose of the sensor object. 106 * 107 * Remove the Message Handler for this sensor object 108 */ 109 @Override 110 public void dispose() { 111 if (messageHandler != null) { 112 tc.removeMessageListener(messageHandler); 113 messageHandler = null; 114 } 115 super.dispose(); 116 } 117 118 119 private void createSensorListener() { 120 // create message listener 121 messageHandler = new BiDiBOutputMessageHandler(this, "SENSOR", tc) { 122 123 @Override 124 public void occupation(byte[] address, int messageNum, int detectorNumber, OccupationState occupationState, Integer timestamp) { 125 //log.trace("occupation: node UID: {}, node addr: {}, address: {}, detectorNumber: {}, occ state: {}, timestamp: {}", addr.getNodeUID(), addr.getNodeAddr(), address, detectorNumber, occupationState, timestamp); 126 if (NodeUtils.isAddressEqual(addr.getNodeAddr(), address) && !addr.isPortAddr() && addr.getAddr() == detectorNumber) { 127 log.info("SENSOR occupation was signalled, state: {}, BM Number: {}, node: {}", occupationState, detectorNumber, addr); 128 if (occupationState == OccupationState.OCCUPIED) { 129 setOwnState(Sensor.ACTIVE); 130 } 131 else { 132 setOwnState(Sensor.INACTIVE); 133 } 134 } 135 } 136 @Override 137 public void occupancyMultiple(byte[] address, int messageNum, int baseAddress, int detectorCount, byte[] detectorData) { 138 //log.trace("occupation: node UID: {}, node addr: {}, address: {}, baseAddress: {}, detectorCount: {}, occ states: {}, timestamp: {}", addr.getNodeUID(), addr.getNodeAddr(), address, baseAddress, detectorCount, ByteUtils.bytesToHex(detectorData)); 139 if (NodeUtils.isAddressEqual(addr.getNodeAddr(), address) && !addr.isPortAddr() && addr.getAddr() >= baseAddress && addr.getAddr() < (baseAddress + detectorCount)) { 140 // TODO: This is very inefficent, since this function is called for each sensor! We should place the listener at a more central instance like the sensor manager 141 // our address is in the data bytes. Check which byte and then check, if the correspondent bit is set. 142 //log.trace("multiple occupation was signalled, states: {}, BM base Number: {}, BM count: {}, node: {}", ByteUtils.bytesToHex(detectorData), baseAddress, detectorCount, addr); 143 int relAddr = addr.getAddr() - baseAddress; 144 byte b = detectorData[ relAddr / 8]; 145 boolean isOccupied = (b & (1 << (relAddr % 8))) != 0; 146 log.info("SENSOR multi occupation was signalled, state: {}, BM addr: {}, node: {}", isOccupied ? "OCCUPIED" : "FREE", addr.getAddr(), addr); 147 if (isOccupied) { 148 setOwnState(Sensor.ACTIVE); 149 } 150 else { 151 setOwnState(Sensor.INACTIVE); 152 } 153 } 154 } 155 @Override 156 public void newOutputState(int state) { 157 int newState = (state == 0) ? Sensor.INACTIVE : Sensor.ACTIVE; 158 log.debug("SENSOR new state: {}", newState); 159 setOwnState(newState); 160 } 161 @Override 162 public void outputWait(int time) { 163 log.debug("SENSOR wait: {}", time); 164 } 165 @Override 166 public void errorState(int err) { 167 log.warn("SENSOR error: {} addr: {}", err, addr); 168 setOwnState(INCONSISTENT); 169 } 170 }; 171 tc.addMessageListener(messageHandler); 172 } 173 174 // initialize logging 175 private final static Logger log = LoggerFactory.getLogger(BiDiBSensor.class); 176 177}