001package jmri.jmrix.can.cbus; 002 003import jmri.Sensor; 004import jmri.implementation.AbstractSensor; 005import jmri.jmrix.can.CanListener; 006import jmri.jmrix.can.CanMessage; 007import jmri.jmrix.can.CanReply; 008import jmri.jmrix.can.TrafficController; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * Extend jmri.AbstractSensor for CBUS controls. 014 * 015 * @author Bob Jacobsen Copyright (C) 2008 016 */ 017public class CbusSensor extends AbstractSensor implements CanListener, CbusEventInterface { 018 019 private CbusAddress addrActive; // go to active state 020 private CbusAddress addrInactive; // go to inactive state 021 022 /** 023 * Create a new CbusSensor. 024 * @param prefix Hardware connection system prefix, excluding the S for Sensor.. 025 * @param address String form of {@link CbusAddress} 026 * @param tc System Traffic Controller. 027 */ 028 public CbusSensor(String prefix, String address, TrafficController tc) { 029 super(prefix + "S" + address); 030 this.tc = tc; 031 init(address); 032 } 033 034 private final TrafficController tc; 035 036 /** 037 * Common initialization for constructors. 038 */ 039 private void init(String address) { 040 // build local addresses 041 CbusAddress a = new CbusAddress(address); 042 CbusAddress[] v = a.split(); 043 switch (v.length) { 044 case 1: 045 addrActive = v[0]; 046 // need to complement here for addr 1 047 // so address _must_ start with address + or - 048 if (address.startsWith("+")) { 049 addrInactive = new CbusAddress("-" + address.substring(1)); 050 } else if (address.startsWith("-")) { 051 addrInactive = new CbusAddress("+" + address.substring(1)); 052 } else { 053 log.error("can't make 2nd event from systemname {}", address); 054 return; 055 } 056 break; 057 case 2: 058 addrActive = v[0]; 059 addrInactive = v[1]; 060 break; 061 default: 062 log.error("Can't parse CbusSensor system name: {}", address); 063 return; 064 } 065 // connect 066 addTc(tc); 067 } 068 069 /** 070 * Request an update on status by sending CBUS request message to active address. 071 * Sends a query message using the active Sensor address. 072 * e.g. for a CBUS address "-7;+5", the query will go to event 7. 073 * {@inheritDoc} 074 */ 075 @Override 076 public void requestUpdateFromLayout() { 077 CanMessage m = addrActive.makeMessage(tc.getCanid()); 078 int opc = CbusMessage.getOpcode(m); 079 if (CbusOpCodes.isShortEvent(opc)) { 080 m.setOpCode(CbusConstants.CBUS_ASRQ); 081 } 082 else { 083 m.setOpCode(CbusConstants.CBUS_AREQ); 084 } 085 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 086 tc.sendCanMessage(m, this); 087 } 088 089 /** 090 * User request to set the state. 091 * We broadcast that to all listeners by putting it out on CBUS. 092 * In turn, the code in this class 093 * should use setOwnState to handle internal sets and bean notifies. 094 * Unknown / Inconsistent states do not send a message to CBUS, 095 * but do update sensor state. 096 * {@inheritDoc} 097 */ 098 @Override 099 public void setKnownState(int s) throws jmri.JmriException { 100 setOwnState(s); 101 CanMessage m; 102 switch (s) { 103 case Sensor.ACTIVE: 104 m = ( getInverted() ? addrInactive.makeMessage(tc.getCanid()) : 105 addrActive.makeMessage(tc.getCanid())); 106 break; 107 case Sensor.INACTIVE: 108 m = ( !getInverted() ? addrInactive.makeMessage(tc.getCanid()) : 109 addrActive.makeMessage(tc.getCanid())); 110 break; 111 default: 112 return; 113 } 114 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 115 tc.sendCanMessage(m, this); 116 } 117 118 /** 119 * Returns true, can invert. 120 * {@inheritDoc} 121 */ 122 @Override 123 public boolean canInvert() { 124 return true; 125 } 126 127 /** 128 * Package method returning CanMessage for the Active Sensor Address 129 * @return CanMessage with the Active CBUS Address 130 */ 131 public CanMessage getAddrActive(){ 132 CanMessage m; 133 if (getInverted()){ 134 m = addrInactive.makeMessage(tc.getCanid()); 135 } else { 136 m = addrActive.makeMessage(tc.getCanid()); 137 } 138 return m; 139 } 140 141 /** 142 * Package method returning CanMessage for the Inactive Sensor Address 143 * @return CanMessage with the InActive CBUS Address 144 */ 145 public CanMessage getAddrInactive(){ 146 CanMessage m; 147 if (getInverted()){ 148 m = addrActive.makeMessage(tc.getCanid()); 149 } else { 150 m = addrInactive.makeMessage(tc.getCanid()); 151 } 152 return m; 153 } 154 155 /** 156 * {@inheritDoc} 157 */ 158 @Override 159 public CanMessage getBeanOnMessage(){ 160 return checkEvent(getAddrActive()); 161 } 162 163 /** 164 * {@inheritDoc} 165 */ 166 @Override 167 public CanMessage getBeanOffMessage(){ 168 return checkEvent(getAddrInactive()); 169 } 170 171 /** 172 * Track layout status from messages being sent to CAN 173 * {@inheritDoc} 174 */ 175 @Override 176 public void message(CanMessage f) { 177 if ( f.extendedOrRtr() ) { 178 return; 179 } 180 if (addrActive.match(f)) { 181 setOwnState(!getInverted() ? Sensor.ACTIVE : Sensor.INACTIVE); 182 } else if (addrInactive.match(f)) { 183 setOwnState(!getInverted() ? Sensor.INACTIVE : Sensor.ACTIVE); 184 } 185 } 186 187 /** 188 * Event status from messages being received from CAN 189 * {@inheritDoc} 190 */ 191 @Override 192 public void reply(CanReply f) { 193 if ( f.extendedOrRtr() ) { 194 return; 195 } 196 // convert response events to normal 197 CanReply opcf = CbusMessage.opcRangeToStl(f); 198 if (addrActive.match(opcf)) { 199 setOwnState(!getInverted() ? Sensor.ACTIVE : Sensor.INACTIVE); 200 } else if (addrInactive.match(opcf)) { 201 setOwnState(!getInverted() ? Sensor.INACTIVE : Sensor.ACTIVE); 202 } 203 } 204 205 /** 206 * {@inheritDoc} 207 */ 208 @Override 209 public void dispose() { 210 tc.removeCanListener(this); 211 super.dispose(); 212 } 213 214 private final static Logger log = LoggerFactory.getLogger(CbusSensor.class); 215 216}