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 newKnownStateDelayed(state); 191 } 192 } else if (addrClosed.match(f)) { 193 int state = (!getInverted() ? CLOSED : THROWN); 194 newCommandedState(state); 195 if (_activeFeedbackType == DIRECT) { 196 newKnownState(state); 197 } else if (_activeFeedbackType == DELAYED) { 198 newKnownStateDelayed(state); 199 } 200 } 201 } 202 203 /** 204 * {@inheritDoc} 205 * 206 * @see jmri.jmrix.can.CanListener#reply(jmri.jmrix.can.CanReply) 207 */ 208 @Override 209 public void reply(CanReply origf) { 210 if ( origf.extendedOrRtr()) { 211 return; 212 } 213 // convert response events to normal 214 CanReply f = CbusMessage.opcRangeToStl(origf); 215 if (addrThrown.match(f)) { 216 int state = (!getInverted() ? THROWN : CLOSED); 217 newCommandedState(state); 218 if (_activeFeedbackType == DIRECT) { 219 newKnownState(state); 220 } else if (_activeFeedbackType == DELAYED) { 221 newKnownStateDelayed(state); 222 } 223 } else if (addrClosed.match(f)) { 224 int state = (!getInverted() ? CLOSED : THROWN); 225 newCommandedState(state); 226 if (_activeFeedbackType == DIRECT) { 227 newKnownState(state); 228 } else if (_activeFeedbackType == DELAYED) { 229 newKnownStateDelayed(state); 230 } 231 } else if (addrThrown.matchRequest(f)) { 232 sendResponseToQuery(); 233 } 234 } 235 236 /** 237 * {@inheritDoc} 238 */ 239 @Override 240 protected void turnoutPushbuttonLockout(boolean locked) { 241 } 242 243 /** 244 * {@inheritDoc} 245 */ 246 @Override 247 public boolean canInvert() { 248 return true; 249 } 250 251 /** 252 * {@inheritDoc} 253 */ 254 @Override 255 public CanMessage getBeanOnMessage(){ 256 return checkEvent(getAddrClosed()); 257 } 258 259 /** 260 * {@inheritDoc} 261 */ 262 @Override 263 public CanMessage getBeanOffMessage(){ 264 return checkEvent(getAddrThrown()); 265 } 266 267 /** 268 * {@inheritDoc} 269 */ 270 @Override 271 public void dispose() { 272 tc.removeCanListener(this); 273 super.dispose(); 274 } 275 276 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusTurnout.class); 277 278}