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