001package jmri.jmrix.can.cbus; 002 003import jmri.Sensor; 004import jmri.Turnout; 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 * Turnout for CBUS connections. 014 * 015 * @author Bob Jacobsen Copyright (C) 2001 016 */ 017public class CbusTurnout extends jmri.implementation.AbstractTurnout 018 implements CanListener, CbusEventInterface { 019 020 private CbusAddress addrThrown; // go to thrown state 021 private CbusAddress addrClosed; // go to closed state 022 023 protected CbusTurnout(String prefix, String address, TrafficController tc) { 024 super(prefix + "T" + address); 025 this.tc = tc; 026 init(address); 027 028 } 029 030 private final TrafficController tc; 031 032 /** 033 * Common initialization for both constructors. 034 */ 035 private void init(String address) { 036 // build local addresses 037 CbusAddress a = new CbusAddress(address); 038 CbusAddress[] v = a.split(); 039 switch (v.length) { 040 case 1: 041 addrThrown = v[0]; 042 // need to complement here for addr 1 043 // so address _must_ start with address + or - 044 if (address.startsWith("+")) { 045 addrClosed = new CbusAddress("-" + address.substring(1)); 046 } else if (address.startsWith("-")) { 047 addrClosed = new CbusAddress("+" + address.substring(1)); 048 } else { 049 log.error("can't make 2nd event from systemname {}", address); 050 return; 051 } 052 break; 053 case 2: 054 addrThrown = v[0]; 055 addrClosed = v[1]; 056 break; 057 default: 058 log.error("Can't parse CbusTurnout system name: {}", address); 059 return; 060 } 061 // connect 062 addTc(tc); 063 } 064 065 /** 066 * Request an update on status by sending CBUS request message to thrown address. 067 */ 068 @Override 069 public void requestUpdateFromLayout() { 070 CanMessage m; 071 m = addrThrown.makeMessage(tc.getCanid()); 072 if (CbusOpCodes.isShortEvent(CbusMessage.getOpcode(m))) { 073 m.setOpCode(CbusConstants.CBUS_ASRQ); 074 } 075 else { 076 m.setOpCode(CbusConstants.CBUS_AREQ); 077 } 078 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 079 tc.sendCanMessage(m, this); 080 081 if (getFeedbackMode() == ONESENSOR || getFeedbackMode() == TWOSENSOR) { 082 Sensor s1 = getFirstSensor(); 083 if (s1 != null) s1.requestUpdateFromLayout(); 084 } 085 if (getFeedbackMode() == TWOSENSOR) { 086 Sensor s2 = getSecondSensor(); 087 if (s2 != null) s2.requestUpdateFromLayout(); 088 } 089 } 090 091 /** 092 * {@inheritDoc} 093 * Sends a CBUS event. 094 */ 095 @Override 096 protected void forwardCommandChangeToLayout(int newState) { 097 CanMessage m; 098 if (newState == Turnout.THROWN) { 099 m = getAddrThrown(); 100 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 101 tc.sendCanMessage(m, this); 102 } 103 if (newState == Turnout.CLOSED) { 104 m = getAddrClosed(); 105 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 106 tc.sendCanMessage(m, this); 107 } 108 } 109 110 /** 111 * Package method returning CanMessage for the Thrown Turnout Address. 112 * 113 * @return CanMessage with the Thrown Address 114 */ 115 public CanMessage getAddrThrown(){ 116 CanMessage m; 117 if (getInverted()){ 118 m = addrClosed.makeMessage(tc.getCanid()); 119 } else { 120 m = addrThrown.makeMessage(tc.getCanid()); 121 } 122 return m; 123 } 124 125 /** 126 * Package method returning CanMessage for the Closed Turnout Address 127 * @return CanReply with the Closed Address 128 */ 129 public CanMessage getAddrClosed(){ 130 CanMessage m; 131 if (getInverted()){ 132 m = addrThrown.makeMessage(tc.getCanid()); 133 } else { 134 m = addrClosed.makeMessage(tc.getCanid()); 135 } 136 return m; 137 } 138 139 // note we only respond to addrThrown matches, 140 // ie the left hand side of split of address "+5;+7" 141 // there's a response expected from 5 142 private void sendResponseToQuery(){ 143 CanMessage m = null; 144 if (getCommandedState() == Turnout.THROWN) { 145 m=getAddrThrown(); // has already had inverted status considered 146 if (CbusMessage.isShort(m)) { 147 if (CbusMessage.getEventType(m)==CbusConstants.EVENT_ON) { 148 m.setOpCode(CbusConstants.CBUS_ARSON); 149 } 150 else { 151 m.setOpCode(CbusConstants.CBUS_ARSOF); 152 } 153 } else { 154 if (CbusMessage.getEventType(m)==CbusConstants.EVENT_ON) { 155 m.setOpCode(CbusConstants.CBUS_ARON); 156 } 157 else { 158 m.setOpCode(CbusConstants.CBUS_AROF); 159 } 160 } 161 } else if (getCommandedState() == Turnout.CLOSED){ 162 m=getAddrThrown(); // has already had inverted status considered 163 if (CbusMessage.isShort(m)) { 164 if (CbusMessage.getEventType(m)==CbusConstants.EVENT_ON) { 165 m.setOpCode(CbusConstants.CBUS_ARSOF); 166 } 167 else { 168 m.setOpCode(CbusConstants.CBUS_ARSON); 169 } 170 } else { 171 if (CbusMessage.getEventType(m)==CbusConstants.EVENT_ON) { 172 m.setOpCode(CbusConstants.CBUS_AROF); 173 } 174 else { 175 m.setOpCode(CbusConstants.CBUS_ARON); 176 } 177 } 178 } 179 if (m!=null) { 180 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 181 tc.sendCanMessage(m, this); 182 } 183 } 184 185 /** 186 * {@inheritDoc} 187 * 188 * @see jmri.jmrix.can.CanListener#message(jmri.jmrix.can.CanMessage) 189 */ 190 @Override 191 public void message(CanMessage f) { 192 if ( f.isExtended() || f.isRtr() ) { 193 return; 194 } 195 if (addrThrown.match(f)) { 196 int state = (!getInverted() ? THROWN : CLOSED); 197 newCommandedState(state); 198 if (_activeFeedbackType == DIRECT) { 199 newKnownState(state); 200 } else if (_activeFeedbackType == DELAYED) { 201 newKnownState(INCONSISTENT); 202 jmri.util.ThreadingUtil.runOnLayoutDelayed( () -> { newKnownState(state); },DELAYED_FEEDBACK_INTERVAL ); 203 } 204 } else if (addrClosed.match(f)) { 205 int state = (!getInverted() ? CLOSED : THROWN); 206 newCommandedState(state); 207 if (_activeFeedbackType == DIRECT) { 208 newKnownState(state); 209 } else if (_activeFeedbackType == DELAYED) { 210 newKnownState(INCONSISTENT); 211 jmri.util.ThreadingUtil.runOnLayoutDelayed( () -> { newKnownState(state); },DELAYED_FEEDBACK_INTERVAL ); 212 } 213 } 214 } 215 216 /** 217 * {@inheritDoc} 218 * 219 * @see jmri.jmrix.can.CanListener#reply(jmri.jmrix.can.CanReply) 220 */ 221 @Override 222 public void reply(CanReply origf) { 223 if ( origf.extendedOrRtr()) { 224 return; 225 } 226 // convert response events to normal 227 CanReply f = CbusMessage.opcRangeToStl(origf); 228 if (addrThrown.match(f)) { 229 int state = (!getInverted() ? THROWN : CLOSED); 230 newCommandedState(state); 231 if (_activeFeedbackType == DIRECT) { 232 newKnownState(state); 233 } else if (_activeFeedbackType == DELAYED) { 234 newKnownState(INCONSISTENT); 235 jmri.util.ThreadingUtil.runOnLayoutDelayed( () -> { newKnownState(state); },DELAYED_FEEDBACK_INTERVAL ); 236 } 237 } else if (addrClosed.match(f)) { 238 int state = (!getInverted() ? CLOSED : THROWN); 239 newCommandedState(state); 240 if (_activeFeedbackType == DIRECT) { 241 newKnownState(state); 242 } else if (_activeFeedbackType == DELAYED) { 243 newKnownState(INCONSISTENT); 244 jmri.util.ThreadingUtil.runOnLayoutDelayed( () -> { newKnownState(state); },DELAYED_FEEDBACK_INTERVAL ); 245 } 246 } else if (addrThrown.matchRequest(f)) { 247 sendResponseToQuery(); 248 } 249 } 250 251 /** 252 * {@inheritDoc} 253 */ 254 @Override 255 protected void turnoutPushbuttonLockout(boolean locked) { 256 } 257 258 /** 259 * {@inheritDoc} 260 */ 261 @Override 262 public boolean canInvert() { 263 return true; 264 } 265 266 /** 267 * {@inheritDoc} 268 */ 269 @Override 270 public CanMessage getBeanOnMessage(){ 271 return checkEvent(getAddrClosed()); 272 } 273 274 /** 275 * {@inheritDoc} 276 */ 277 @Override 278 public CanMessage getBeanOffMessage(){ 279 return checkEvent(getAddrThrown()); 280 } 281 282 /** 283 * {@inheritDoc} 284 */ 285 @Override 286 public void dispose() { 287 tc.removeCanListener(this); 288 super.dispose(); 289 } 290 291 292 private final static Logger log = LoggerFactory.getLogger(CbusTurnout.class); 293}