001package jmri.jmrix.acela; 002 003import java.io.DataInputStream; 004import jmri.jmrix.AbstractMRListener; 005import jmri.jmrix.AbstractMRMessage; 006import jmri.jmrix.AbstractMRNodeTrafficController; 007import jmri.jmrix.AbstractMRReply; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011/** 012 * Converts Stream-based I/O to/from Acela messages. 013 * <p> 014 * The "SerialInterface" side sends/receives message objects. 015 * <p> 016 * The connection to an AcelaPortController is via a pair of Streams, which 017 * then carry sequences of characters for transmission. Note that this 018 * processing is handled in an independent thread. 019 * <p> 020 * This handles the state transitions, based on the necessary state in each 021 * message. 022 * <p> 023 * Handles initialization, polling, output, and input for multiple Serial Nodes. 024 * @see jmri.jmrix.AbstractMRNodeTrafficController 025 * 026 * @author Bob Jacobsen Copyright (C) 2003 027 * @author Bob Jacobsen, Dave Duchamp, multiNode extensions, 2004 028 * @author Bob Coleman Copyright (C) 2007. 2008 Based on CMRI serial example, 029 * modified to establish Acela support. 030 */ 031public class AcelaTrafficController extends AbstractMRNodeTrafficController implements AcelaInterface { 032 033 /** 034 * Create a new AcelaTrafficController instance. 035 */ 036 public AcelaTrafficController() { 037 super(); 038 039 // entirely poll driven, so reduce Polling interval 040 mWaitBeforePoll = 25; // default = 25 041 setAllowUnexpectedReply(true); 042 043 super.init(0, 1024); // 1024 is an artificial limit but economically reasonable maxNode upper limit 044 045 reallyReadyToPoll = false; // Need to not start polling until we are ready 046 needToPollNodes = true; // Need to poll and create corresponding nodes 047 needToInitAcelaNetwork = true; // Need to poll and create corresponding nodes 048 needToCreateNodesState = 0; // Need to initialize system and then poll 049 acelaTrafficControllerState = false; // Flag to indicate which state we are in: 050 // false == Initializing Acela Network 051 // true == Polling Sensors 052 } 053 054 // The methods to implement the AcelaInterface 055 056 @Override 057 public synchronized void addAcelaListener(AcelaListener l) { 058 this.addListener(l); 059 } 060 061 @Override 062 public synchronized void removeAcelaListener(AcelaListener l) { 063 this.removeListener(l); 064 } 065 066 transient int curAcelaNodeIndex = -1; // cycles over defined nodes when pollMessage is called 067 068 transient private int currentOutputAddress = -1; // Incremented as Acela Nodes are created and registered 069 // Corresponds to next available output address in nodeArray 070 // Start at -1 to avoid issues with bit address 0 071 transient private int currentSensorAddress = -1; // Incremented as Acela Nodes are created and registered 072 // Corresponds to next available sensor address in nodeArray 073 // Start at -1 to avoid issues with bit address 0 074 075 private boolean acelaTrafficControllerState = false; // Flag to indicate which state we are in: 076 // false == Initializing Acela Network 077 // true == Polling Sensors 078 private boolean reallyReadyToPoll = false; // Flag to indicate that we are really ready to poll nodes 079 transient private boolean needToPollNodes = true; // Flag to indicate that nodes have not yet been created 080 private boolean needToInitAcelaNetwork = true; // Flag to indicate that Acela network must be initialized 081 private int needToCreateNodesState = 0; // Need to do a few things: 082 // Reset Acela Network 083 // Set Acela Network Online 084 // Poll for Acela Nodes (and create and register the nodes) 085 086 private boolean acelaSensorsState = false; // Flag to indicate whether we have an active sensor and therefore need to poll: 087 // false == No active sensor 088 // true == Active sensor, need to poll sensors 089 090 private int acelaSensorInitCount = 0; // Need to count sensors initialized so we know when we can poll them 091 092 private static int SPECIALNODE = 0; // Needed to initialize system 093 094 /** 095 * Get minimum address of an Acela node as set on this TrafficController. 096 * @return minimum node address. 097 */ 098 public int getMinimumNodeAddress() { 099 return minNode; 100 } 101 102 /** 103 * Get maximum number of Acela nodes as set on this TrafficController. 104 * @return max number of nodes. 105 */ 106 public int getMaximumNumberOfNodes() { 107 return maxNode; 108 } 109 110 public boolean getAcelaTrafficControllerState() { 111 return acelaTrafficControllerState; 112 } 113 114 public void setAcelaTrafficControllerState(boolean newstate) { 115 acelaTrafficControllerState = newstate; 116 } 117 118 public synchronized void resetStartingAddresses() { 119 currentOutputAddress = -1; 120 currentSensorAddress = -1; 121 } 122 123 public boolean getAcelaSensorsState() { 124 return acelaSensorsState; 125 } 126 127 public void setAcelaSensorsState(boolean newstate) { 128 acelaSensorsState = newstate; 129 } 130 131 public void incrementAcelaSensorInitCount() { 132 acelaSensorInitCount++; 133 log.debug("Number of Acela sensors initialized: {}", getAcelaSensorInitCount()); 134 } 135 136 public int getAcelaSensorInitCount() { 137 return acelaSensorInitCount; 138 } 139 140 public synchronized boolean getNeedToPollNodes() { 141 return needToPollNodes; 142 } 143 144 public synchronized void setNeedToPollNodes(boolean newstate) { 145 needToPollNodes = newstate; 146 } 147 148 public boolean getReallyReadyToPoll() { 149 return reallyReadyToPoll; 150 } 151 152 public void setReallyReadyToPoll(boolean newstate) { 153 log.debug("setting really ready to poll (nodes): {}", newstate); 154 reallyReadyToPoll = newstate; 155 } 156 157 /** 158 * Reference to the system connection memo. 159 */ 160 AcelaSystemConnectionMemo mMemo = null; 161 162 /** 163 * Get access to the system connection memo associated with this traffic 164 * controller. 165 * 166 * @return associated systemConnectionMemo object 167 */ 168 public AcelaSystemConnectionMemo getSystemConnectionMemo() { 169 return (mMemo); 170 } 171 172 /** 173 * Set the system connection memo associated with this traffic controller. 174 * 175 * @param m associated systemConnectionMemo object 176 */ 177 public void setSystemConnectionMemo(AcelaSystemConnectionMemo m) { 178 mMemo = m; 179 } 180 181 /** 182 * Public method to register an Acela node. 183 * @param node which node to register. 184 */ 185 public void registerAcelaNode(AcelaNode node) { 186 synchronized (this) { 187 super.registerNode(node); 188 189 // no node validity checking because at this point the node may not be fully defined 190 setMustInit(node, false); // Do not normally need to init Acela nodes. 191 if (node.getNumOutputBitsPerCard() == 0) { 192 node.setStartingOutputAddress(-1); 193 node.setEndingOutputAddress(-1); 194 } else { 195 if (currentOutputAddress == -1) { // Need to use -1 to correctly identify bit address 0 196 currentOutputAddress = 0; 197 } 198 node.setStartingOutputAddress(currentOutputAddress); 199 currentOutputAddress = currentOutputAddress + node.getNumOutputBitsPerCard() - 1; 200 node.setEndingOutputAddress(currentOutputAddress); 201 currentOutputAddress = currentOutputAddress + 1; 202 } 203 if (node.getNumSensorBitsPerCard() == 0) { 204 node.setStartingSensorAddress(-1); 205 node.setEndingSensorAddress(-1); 206 } else { 207 if (currentSensorAddress == -1) { // Need to use -1 to correctly identify bit address 0 208 currentSensorAddress = 0; 209 } 210 node.setStartingSensorAddress(currentSensorAddress); 211 currentSensorAddress = currentSensorAddress + node.getNumSensorBitsPerCard() - 1; 212 node.setEndingSensorAddress(currentSensorAddress); 213 currentSensorAddress = currentSensorAddress + 1; 214 } 215 } 216 } 217 218 /** 219 * Public method to set up for initialization of an Acela node. 220 * @param node which node to initialize. 221 */ 222 public void initializeAcelaNode(AcelaNode node) { 223 synchronized (this) { 224 setMustInit(node, true); 225 node.initNode(); 226 } 227 } 228 229 /** 230 * Public method to identify an AcelaNode from its bit address. 231 * <p> 232 * Note: nodeAddress is numbered from 0 233 * 234 * @param bitAddress address to query. 235 * @param isSensor true to use start sensor address, false to use start output address. 236 * @return '-1' if an AcelaNode with the specified address was not found 237 */ 238 public int lookupAcelaNodeAddress(int bitAddress, boolean isSensor) { 239 for (int i = 0; i < getNumNodes(); i++) { 240 AcelaNode node = (AcelaNode) getNode(i); 241 if (isSensor) { 242 if ((bitAddress >= node.getStartingSensorAddress()) 243 && (bitAddress <= node.getEndingSensorAddress())) { 244 return (i); 245 } 246 } else { 247 if ((bitAddress >= node.getStartingOutputAddress()) 248 && (bitAddress <= node.getEndingOutputAddress())) { 249 return (i); 250 } 251 } 252 } 253 return (-1); 254 } 255 256 @Override 257 protected AbstractMRMessage enterProgMode() { 258 log.warn("enterProgMode does NOT make sense for Acela serial"); 259 return null; 260 } 261 262 @Override 263 protected AbstractMRMessage enterNormalMode() { 264 // can happen during error recovery, null is OK 265 return null; 266 } 267 268 /** 269 * Forward an AcelaMessage to all registered AcelaInterface listeners. 270 * {@inheritDoc} 271 */ 272 @Override 273 protected void forwardMessage(AbstractMRListener client, AbstractMRMessage m) { 274 ((AcelaListener) client).message((AcelaMessage) m); 275 } 276 277 /** 278 * Forward an AcelaReply to all registered AcelaInterface listeners. 279 * {@inheritDoc} 280 */ 281 @Override 282 protected void forwardReply(AbstractMRListener client, AbstractMRReply m) { 283 ((AcelaListener) client).reply((AcelaReply) m); 284 } 285 286 AcelaSensorManager mSensorManager = null; 287 288 public void setSensorManager(AcelaSensorManager m) { 289 mSensorManager = m; 290 } 291 292 AcelaTurnoutManager mTurnoutManager = null; 293 294 public void setTurnoutManager(AcelaTurnoutManager m) { 295 mTurnoutManager = m; 296 } 297 298 /** 299 * Handle initialization, output and polling for Acela Nodes from within 300 * the running thread. 301 */ 302 @Override 303 protected synchronized AbstractMRMessage pollMessage() { 304 // Need to wait until we have read config file 305 if (!reallyReadyToPoll) { 306 return null; 307 } 308 309 if (needToInitAcelaNetwork) { 310 if (needToCreateNodesState == 0) { 311 if (needToPollNodes) { 312 new AcelaNode(0, AcelaNode.AC,this); 313 log.info("Created a new Acela Node [0] in order to poll Acela network: " + AcelaNode.AC); 314 } 315 curAcelaNodeIndex = SPECIALNODE; 316 AcelaMessage m = AcelaMessage.getAcelaResetMsg(); 317 log.debug("send Acela reset (init step 1) message: {}", m); 318 m.setTimeout(1000); // wait for init to finish (milliseconds) 319 mCurrentMode = NORMALMODE; 320 needToCreateNodesState++; 321 return m; 322 } 323 if (needToCreateNodesState == 1) { 324 AcelaMessage m = AcelaMessage.getAcelaOnlineMsg(); 325 log.debug("send Acela Online (init step 2) message: {}", m); 326 m.setTimeout(1000); // wait for init to finish (milliseconds) 327 mCurrentMode = NORMALMODE; 328 needToCreateNodesState++; 329 return m; 330 } 331 if (needToPollNodes) { 332 if (needToCreateNodesState == 2) { 333 AcelaMessage m = AcelaMessage.getAcelaPollNodesMsg(); 334 log.debug("send Acela poll nodes message: {}", m); 335 m.setTimeout(100); // wait for init to finish (milliseconds) 336 mCurrentMode = NORMALMODE; 337 needToInitAcelaNetwork = false; 338 needToPollNodes = false; 339 return m; 340 } 341 } else { 342 needToInitAcelaNetwork = false; 343 setAcelaTrafficControllerState(true); 344 } 345 } 346 347 // ensure validity of call 348 if (getNumNodes() <= 0) { 349 return null; 350 } 351 352 // move to a new node 353 curAcelaNodeIndex++; 354 if (curAcelaNodeIndex >= getNumNodes()) { 355 curAcelaNodeIndex = 0; 356 } 357 358 // ensure that each node is initialized 359 AcelaNode node = (AcelaNode) getNode(curAcelaNodeIndex); 360 if (node.hasActiveSensors) { 361 for (int s = 0; s < node.sensorbitsPerCard; s++) { 362 if (node.sensorNeedInit[s] && !node.sensorHasBeenInit[s]) { 363 AcelaMessage m = AcelaMessage.getAcelaConfigSensorMsg(); 364 int tempiaddr = s + node.getStartingSensorAddress(); 365 byte tempbaddr = (byte) (tempiaddr); 366 m.setElement(2, tempbaddr); 367 m.setElement(3, node.sensorConfigArray[s]); 368 log.debug("send Acela Config Sensor message: {}", m); 369 incrementAcelaSensorInitCount(); 370 m.setTimeout(100); // wait for init to finish (milliseconds) 371 mCurrentMode = NORMALMODE; 372 node.sensorHasBeenInit[s] = true; 373 node.sensorNeedInit[s] = false; 374 return m; 375 } 376 } 377 } 378 379 // send Output packet if needed 380 if (getNode(curAcelaNodeIndex).mustSend()) { 381 getNode(curAcelaNodeIndex).resetMustSend(); 382 AbstractMRMessage m = getNode(curAcelaNodeIndex).createOutPacket(); 383 m.setTimeout(100); // no need to wait for output to answer 384 log.debug("request write command to send: {}", m); 385 mCurrentMode = NORMALMODE; 386 return m; 387 } 388 389 // Trying to serialize Acela initialization so system is stable 390 // So we will not poll sensors or send om/off commands until we have 391 // initialized all of the sensor modules -- this can take several seconds 392 // during a cold system startup. 393 if ((currentSensorAddress == 0) || (currentSensorAddress != getAcelaSensorInitCount())) { 394 return null; 395 } 396 397 if (acelaSensorsState) { // Flag to indicate whether we have an active sensor and therefore need to poll 398 AcelaMessage m = AcelaMessage.getAcelaPollSensorsMsg(); 399 log.debug("send Acela poll sensors message: {}", m); 400 m.setTimeout(100); // wait for init to finish (milliseconds) 401 mCurrentMode = NORMALMODE; 402 return m; 403 } else { 404 // no Sensors (inputs) are active for this node 405 return null; 406 } 407 } 408 409 @Override 410 protected synchronized void handleTimeout(AbstractMRMessage m, AbstractMRListener l) { 411 // don't use super behavior, as timeout to init, transmit message is normal 412 // inform node, and if it resets then reinitialize 413 if (getNode(curAcelaNodeIndex).handleTimeout(m, l)) { 414 setMustInit(curAcelaNodeIndex, true); 415 } 416 } 417 418 @Override 419 protected synchronized void resetTimeout(AbstractMRMessage m) { 420 // don't use super behavior, as timeout to init, transmit message is normal 421 // and inform node 422 getNode(curAcelaNodeIndex).resetTimeout(m); 423 } 424 425 @Override 426 protected AbstractMRListener pollReplyHandler() { 427 return mSensorManager; 428 } 429 430 /** 431 * Forward a pre-formatted message to the actual interface. 432 * {@inheritDoc} 433 */ 434 @Override 435 public void sendAcelaMessage(AcelaMessage m, AcelaListener reply) { 436 sendMessage(m, reply); 437 } 438 439 @Override 440 protected AbstractMRReply newReply() { 441 return new AcelaReply(); 442 } 443 444 @Override 445 protected boolean endOfMessage(AbstractMRReply msg) { 446 // our version of loadChars doesn't invoke this, so it shouldn't be called 447 return true; 448 } 449 450 @Override 451 protected void loadChars(AbstractMRReply msg, DataInputStream istream) throws java.io.IOException { 452 int char1 = readByteProtected(istream)&0xFF; 453 if (char1 == 0x00) { // 0x00 means command processed OK. 454 msg.setElement(0, char1); 455 // 0x01 means that the Acela network is offline 456 // 0x02 means that an illegal address was sent 457 // 0x03 means that an illegal command was sent 458 // For now we are not going to check for these 459 // three conditions since they will only catch 460 // programming errors (versus runtime errors) 461 // and the checking may mess up the polling replies. 462 463 } else { 464 if ((char1 == 0x81) || (char1 == 0x82)) { 465 // 0x81 means that a sensor has changed. 466 // 0x82 means that communications has been lost 467 // For now we will check for these two 468 // conditions since they do represent 469 // runtime errors at the risk that in a very very 470 // large Acela network the checking may mess 471 // up the polling replies. 472 msg.setElement(0, char1); 473 } else { 474 // We have a reply to a poll (either pollnodes 475 // or pollsensors). The first byte will be the 476 // length of the reply followed by the 477 // indicated number of bytes. 478 // 479 // For now we will send the reply to the sensor 480 // manager. In the future we should really have 481 // an Acela Network Manager and an Acela Sensor 482 // Manager -- but, for now, we 'know' which state 483 // we are in. 484 for (int i = 0; i < char1; i++) { 485 byte charn = readByteProtected(istream); 486 msg.setElement(i, charn); 487 } 488 } 489 } 490 } 491 492 @Override 493 protected void waitForStartOfReply(DataInputStream istream) throws java.io.IOException { 494 // Just return 495 } 496 497 /** 498 * For each sensor node call markChanges. 499 * @param r reply to use in sensor update. 500 */ 501 public void updateSensorsFromPoll(AcelaReply r) { 502 for (int i = 0; i < getNumNodes(); i++) { 503 AcelaNode node = (AcelaNode) getNode(i); 504 if (node.getSensorBitsPerCard() > 0) { 505 node.markChanges(r); 506 } 507 } 508 } 509 510 private final static Logger log = LoggerFactory.getLogger(AcelaTrafficController.class); 511 512}