001package jmri.jmrix.acela; 002 003import jmri.InstanceManager; 004import jmri.JmriException; 005import jmri.Sensor; 006import jmri.jmrix.AbstractMRListener; 007import jmri.jmrix.AbstractMRMessage; 008import jmri.jmrix.AbstractNode; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * Models an Acela node. 014 * <p> 015 * Nodes are numbered from 0. The first watchman node carries the first 8 016 * sensors 0 to 7, etc. 017 * <p> 018 * The array of sensor states is used to update sensor known state only when 019 * there's a change on the serial bus. This allows for the sensor state to be 020 * updated within the program, keeping this updated state until the next change 021 * on the serial bus. E.g. you can manually change a state via an icon, and not 022 * have it change back the next time that node is polled. 023 * <p> 024 * Same applies to the outputs (Dash-8s and Signalmen) 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, 2009 Based on CMRI serial 029 * example, modified to establish Acela support. 030 */ 031public class AcelaNode extends AbstractNode { 032 033 /** 034 * Maximum number of sensors/outputs any node of any type can carry. 035 */ 036 static final int MAXSENSORBITS = 16; // Used to initialize arrays 037 static final int MAXOUTPUTBITS = 16; // Used to initialize arrays 038 static final int MAXNODE = 1024; 039 private static int MAXDELAY = 65535; 040 041 // class constants 042 public static final byte AC = 0x00; // Acela Interface module (no inputs, no outputs) 043 // Does not really return a code of 0x00 044 public static final byte TB = 0x01; // TrainBrain (4 output bits and 4 input bits) 045 public static final byte D8 = 0x02; // Dash-8 (8 output bits) 046 public static final byte WM = 0x03; // Watchman (8 input bits) 047 public static final byte SM = 0x04; // SignalMan (16 output bits) 048 public static final byte SC = 0x05; // SmartCab (1 output bits. no input bits) 049 public static final byte SW = 0x06; // SwitchMan (16 output bits. no input bits) 050 public static final byte YM = 0x07; // YardMaster (16 output bits. no input bits) 051 public static final byte SY = 0x08; // Sentry (no output bits. 16 input bits) 052 public static final byte UN = 0x09; // Unidentified module -- should be FF 053 public static final String moduleTypes = "ACTBD8WMSMSCSWYMSYUN"; 054 055 static final String[] nodeNames = new String[]{"0", "1", "2", "3", "4", 056 "5", "6", "7", "8", "9", 057 "10", "11", "12", "13", "14", 058 "15", "16", "17", "18", "19" 059 }; 060 061 public static String[] getNodeNames() { 062 return nodeNames.clone(); 063 } 064 065 static final String[] moduleNames = new String[]{"Acela", 066 "TrainBrain", 067 "Dash-8", 068 "Watchman", 069 "SignalMan", 070 "SmartCab", 071 "SwitchMan", 072 "YardMaster", 073 "Sentry" 074 }; 075 076 public static String[] getModuleNames() { 077 return moduleNames.clone(); 078 } 079 080 static final String[] moduleTips = new String[]{"Acela", 081 "TrainBrain has 4 output circuits and 4 input circuits", 082 "Dash-8 has 8 output circuits and no input circuits", 083 "Watchman has no output circuits and 8 input circuits", 084 "SignalMan has 16 output circuits and no input circuits", 085 "SmartCab has 1 output circuit and no input circuits", 086 "SwitchMan has 16 output circuits and no input circuits", 087 "YardMaster has 16 output circuits and no input circuits", 088 "Sentry has no output circuits and 16 input circuits" 089 }; 090 091 // node definition instance variables (must persist between runs) 092 protected int nodeType = UN; // See above 093 protected int outputbitsPerCard = MAXOUTPUTBITS; // See above 094 protected int sensorbitsPerCard = MAXSENSORBITS; // See above 095 protected int transmissionDelay = 0; // Delay between bytes on Receive (units of 10 microsec.) 096 097 // operational instance variables (should not be preserved between runs) 098 protected boolean needInit = false; // 'true' if this module needs to be initialized 099 // used for sensors 100 protected byte[] outputArray = new byte[MAXOUTPUTBITS]; // current values of the output bits for this node 101 protected int[] outputSpecial = new int[MAXOUTPUTBITS]; // does the output circuit require special handling 102 protected int[] outputSignalHeadType = new int[MAXOUTPUTBITS]; // type of SignalHead 103 protected boolean hasActiveSensors = false; // 'true' if there are active Sensors for this node 104 protected int lastUsedSensor = -1; // grows as sensors defined 105 protected Sensor[] sensorArray = new Sensor[MAXSENSORBITS]; 106 protected boolean[] sensorNeedInit = new boolean[MAXSENSORBITS]; // used to indicate if sensor needs to be configured 107 protected boolean[] sensorHasBeenInit = new boolean[MAXSENSORBITS]; // used to indicate if sensor has been configured 108 protected int[] sensorLastSetting = new int[MAXSENSORBITS]; 109 protected int[] sensorType = new int[MAXSENSORBITS]; 110 protected int[] sensorPolarity = new int[MAXSENSORBITS]; 111 protected int[] sensorThreshold = new int[MAXSENSORBITS]; 112 protected byte[] sensorConfigArray = new byte[MAXSENSORBITS]; // configuration values of the sensor circuits for this node 113 protected int[] outputWired = new int[MAXOUTPUTBITS]; 114 protected int[] outputInit = new int[MAXOUTPUTBITS]; 115 protected int[] outputType = new int[MAXOUTPUTBITS]; 116 protected int[] outputLength = new int[MAXOUTPUTBITS]; 117 protected boolean[] outputNeedToSend = new boolean[MAXOUTPUTBITS]; 118 public static final String sensorTypes = "NFSBCGDT"; 119 public static final String sensorPolarities = "ACTINV"; 120 public static final String outputWireds = "NONC"; 121 public static final String outputInits = "OFFACT"; 122 public static final String outputTypes = "ONOFFPULSEBLINK"; 123 public static final int ONOFF = 0; 124 public static final int PULSE = 1; 125 public static final int BLINK = 2; 126 public static final String outputSignalHeadTypes = "UKNOWNDOUBLETRIPLEBPOLARWIGWAG"; 127 public static final int UKNOWN = 0; 128 public static final int DOUBLE = 1; 129 public static final int TRIPLE = 2; 130 public static final int BPOLAR = 3; 131 public static final int WIGWAG = 4; 132 // These can be removed in June 2010: 133 public static final String outputONOFF = "ONOFF"; // used to dump/restore config file. 134 public static final String outputLEN0 = "0"; // used to dump/restore config file. 135 public static final String outputNO = "N0"; // used to dump/restore config file. 136 protected int startingOutputAddress = -1; // used to aid linear address search 137 protected int endingOutputAddress = -1; // used to aid linear address search 138 protected int startingSensorAddress = -1; // used to aid linear address search 139 protected int endingSensorAddress = -1; // used to aid linear address search 140 141 /** 142 * Create a new AcelaNode instance on the TrafficController associated 143 * with the default AcelaSystemConnectionMemo. 144 * <p> 145 * Assumes a node address of 0, and a node type of NO_CARD. If this 146 * constructor is used, actual node address must be set using 147 * {@link jmri.jmrix.AbstractNode#setNodeAddress(int)} and actual 148 * node type using {@link #setNodeType(int)} 149 */ 150 public AcelaNode() { 151 this(0, UN, InstanceManager.getDefault(AcelaSystemConnectionMemo.class).getTrafficController()); 152 } 153 154 /** 155 * Create a new AcelaNode instance and initialize default instance variables. 156 * 157 * @param address the address of first bit on Acela bus (0-1023) type - D8, SM, WM 158 * @param type a type constant from the class 159 * @param tc the TrafficControllerfor this connection 160 */ 161 public AcelaNode(int address, int type, AcelaTrafficController tc) { 162 // set address and type and check validity 163 setNodeAddress(address); 164 setNodeType(type); 165 166 // set default values for other instance variables 167 transmissionDelay = 0; 168 169 // clear the Sensor arrays 170 for (int i = 0; i < MAXSENSORBITS; i++) { 171 sensorArray[i] = null; 172 sensorNeedInit[i] = false; 173 sensorHasBeenInit[i] = false; 174 sensorLastSetting[i] = Sensor.UNKNOWN; 175 sensorType[i] = 2; // Car Gap 176 sensorPolarity[i] = 1; // Inverse 177 sensorThreshold[i] = 4; // Normal -- 0010 0 178 sensorConfigArray[i] = 0x00; // Normal 179 } 180 181 // clear all output bits 182 for (int i = 0; i < MAXOUTPUTBITS; i++) { 183 outputArray[i] = 0; 184 outputSpecial[i] = 0; 185 outputSignalHeadType[i] = 0; 186 outputInit[i] = 0; // Off 187 outputWired[i] = 0; // NO (Normally Open) 188 outputType[i] = 0; // ONOFF 189 outputLength[i] = 10; // 10 tenths of a second 190 outputNeedToSend[i] = false; 191 } 192 193 // initialize other operational instance variables 194 resetMustSend(); 195 needInit = false; 196 hasActiveSensors = false; 197 198 // register this node 199 tc.registerAcelaNode(this); 200 } 201 202 public void initNode() { 203 if (outputbitsPerCard > 0) { 204 // check to see if we can use bulk mode 205 boolean bulk_message = true; 206 int c = 0; 207 while (c < outputbitsPerCard) { 208 if ((outputType[c] != AcelaNode.ONOFF) 209 || (outputSpecial[c] != 0)) { 210 bulk_message = false; 211 } 212 c++; 213 } 214 215 // Initialize all output circuits 216 for (int i = 0; i < MAXOUTPUTBITS; i++) { 217 outputArray[i] = (byte) outputInit[i]; 218 if (!bulk_message) { 219 outputNeedToSend[i] = true; 220 } 221 // outputWired is applied as the command is being constructed so all GUI views on as on and off as off. 222 } 223 setMustSend(); 224 } 225 if (sensorbitsPerCard > 0) { 226 // Initialize all sensor circuits 227 for (int i = 0; i < MAXSENSORBITS; i++) { 228 sensorConfigArray[i] = (byte) ((byte) (sensorThreshold[i] << 3) + (byte) (sensorType[i] << 1) + (byte) (sensorPolarity[i])); 229 sensorNeedInit[i] = true; 230 } 231 hasActiveSensors = true; 232 } 233 } 234 235 /** 236 * Set starting output address for range. 237 * Used to help linear address search. 238 * @param startingAddress starting output address for range. 239 */ 240 public void setStartingOutputAddress(int startingAddress) { 241 startingOutputAddress = startingAddress; 242 } 243 244 /** 245 * Get starting output address for range. 246 * Used to help linear address search. 247 * @return starting output address. 248 */ 249 public int getStartingOutputAddress() { 250 return startingOutputAddress; 251 } 252 253 /** 254 * Set ending output address for range. 255 * Used to help linear address search. 256 * @param endingAddress end output address for range. 257 */ 258 public void setEndingOutputAddress(int endingAddress) { 259 endingOutputAddress = endingAddress; 260 } 261 262 /** 263 * Get ending output address for range. 264 * Used to help linear address search. 265 * @return end output address for range. 266 */ 267 public int getEndingOutputAddress() { 268 return endingOutputAddress; 269 } 270 271 /** 272 * Set starting sensor address for range. 273 * Used to help linear address search. 274 * @param startingAddress start sensor address for range. 275 */ 276 public void setStartingSensorAddress(int startingAddress) { 277 startingSensorAddress = startingAddress; 278 } 279 280 /** 281 * Get starting sensor addresses for range. 282 * Used to help linear address search. 283 * @return starting sensor address for range. 284 */ 285 public int getStartingSensorAddress() { 286 return startingSensorAddress; 287 } 288 289 /** 290 * Set ending sensor addresses for range. 291 * Used to help linear address search. 292 * @param endingAddress end sensor address. 293 */ 294 public void setEndingSensorAddress(int endingAddress) { 295 endingSensorAddress = endingAddress; 296 } 297 298 /** 299 * Get ending sensor addresses for range. 300 * Used to help linear address search. 301 * @return end of range sensor address. 302 */ 303 public int getEndingSensorAddress() { 304 return endingSensorAddress; 305 } 306 307 /** 308 * Set an output bit on this node. 309 * 310 * @param bitNumber the bit to set 311 * @param state bit state to set: 'true' for 0, 'false' for 1 312 */ 313 public void setOutputBit(int bitNumber, boolean state) { 314 // Save old state 315 byte oldbyte = 0; 316 int newbitNumber = 0; 317 newbitNumber = bitNumber - startingOutputAddress; 318 oldbyte = outputArray[newbitNumber]; 319 320 if (state) { 321 outputArray[newbitNumber] = 1; 322 } else { 323 outputArray[newbitNumber] = 0; 324 } 325 326 // check for change, necessitating a send 327 boolean bulk_message = true; 328 int c = 0; 329 while (c < outputbitsPerCard) { 330 if ((outputType[c] != AcelaNode.ONOFF) 331 || (outputSpecial[c] != 0)) { 332 bulk_message = false; 333 } 334 c++; 335 } 336 if (bulk_message) { 337 // check for change, necessitating a send 338 if (oldbyte != outputArray[newbitNumber]) { 339 setMustSend(); 340 } 341 } else { 342 outputNeedToSend[newbitNumber] = true; 343 setMustSend(); 344 } 345 } 346 347 /** 348 * Get the current state of an output bit. 349 * 350 * @param bitNumber the bit. Bits are numbered from 0 for Acela 351 * @return 'true' for 0, 'false' for 1 352 */ 353 public boolean getOutputBit(int bitNumber) { 354 int newbitNumber = 0; 355 newbitNumber = bitNumber - startingOutputAddress; 356 byte testByte = outputArray[newbitNumber]; 357 return (testByte != 0); 358 } 359 360 /** 361 * {@inheritDoc} 362 */ 363 @Override 364 public boolean getSensorsActive() { 365 return hasActiveSensors; 366 } 367 368 /** 369 * Get Output configuration values. 370 * @param circuitnum wired output index number. 371 * @return configuration value. 372 */ 373 public int getOutputWired(int circuitnum) { 374 return outputWired[circuitnum]; 375 } 376 377 public String getOutputWiredString(int circuitnum) { 378 int sensortype = outputWired[circuitnum]; 379 return outputWireds.substring(sensortype * 2, sensortype * 2 + 2); 380 } 381 382 /** 383 * Set Output configuration values. 384 * @param circuitnum output index number. 385 * @param type output type. 386 */ 387 public void setOutputWired(int circuitnum, int type) { 388 outputWired[circuitnum] = type; 389 } 390 391 public void setOutputWiredString(int circuitnum, String stringtype) { 392 int type = outputWireds.lastIndexOf(stringtype) / 2; 393 outputWired[circuitnum] = type; 394 } 395 396 public int getOutputInit(int circuitnum) { 397 return outputInit[circuitnum]; 398 } 399 400 public String getOutputInitString(int circuitnum) { 401 int sensortype = outputInit[circuitnum]; 402 return outputInits.substring(sensortype * 3, sensortype * 3 + 3); 403 } 404 405 public void setOutputInit(int circuitnum, int type) { 406 outputInit[circuitnum] = type; 407 } 408 409 public void setOutputInitString(int circuitnum, String stringtype) { 410 int type = outputInits.lastIndexOf(stringtype) / 3; 411 outputInit[circuitnum] = type; 412 } 413 414 public int getOutputType(int circuitnum) { 415 return outputType[circuitnum]; 416 } 417 418 public String getOutputTypeString(int circuitnum) { 419 int outputtype = outputType[circuitnum]; 420 return outputTypes.substring(outputtype * 5, outputtype * 5 + 5); 421 } 422 423 public void setOutputType(int circuitnum, int type) { 424 outputType[circuitnum] = type; 425 } 426 427 public void setOutputTypeString(int circuitnum, String stringtype) { 428 int type = outputTypes.lastIndexOf(stringtype) / 5; 429 outputType[circuitnum] = type; 430 } 431 432 public int getOutputLength(int circuitnum) { 433 return outputLength[circuitnum]; 434 } 435 436 public void setOutputLength(int circuitnum, int newlength) { 437 outputLength[circuitnum] = newlength; 438 } 439 440 public int getOutputSpecial(int circuitnum) { 441 int newbitNumber = circuitnum - startingOutputAddress; 442 return outputSpecial[newbitNumber]; 443 } 444 445 public void setOutputSpecial(int circuitnum, int type) { 446 int newbitNumber = circuitnum - startingOutputAddress; 447 outputSpecial[newbitNumber] = type; 448 } 449 450 public int getOutputSignalHeadType(int circuitnum) { 451 int newbitNumber = circuitnum - startingOutputAddress; 452 return outputSignalHeadType[newbitNumber]; 453 } 454 455 public String getOutputSignalHeadTypeString(int circuitnum) { 456 int newbitNumber = circuitnum - startingOutputAddress; 457 int outputsignalheadtype = outputSignalHeadType[newbitNumber]; 458 return outputSignalHeadTypes.substring(outputsignalheadtype * 6, outputsignalheadtype * 6 + 6); 459 } 460 461 public void setOutputSignalHeadType(int circuitnum, int type) { 462 int newbitNumber = circuitnum - startingOutputAddress; 463 outputSignalHeadType[newbitNumber] = type; 464 } 465 466 public void setOutputSignalHeadTypeString(int circuitnum, String stringtype) { 467 int newbitNumber = circuitnum - startingOutputAddress; 468 int type = outputSignalHeadTypes.lastIndexOf(stringtype) / 6; 469 outputSignalHeadType[newbitNumber] = type; 470 } 471 472 /** 473 * Public method to set and return Sensor configuration values. 474 * @param circuitnum sensor type array index number. 475 * @return sensor index value. 476 */ 477 public int getSensorType(int circuitnum) { 478 return sensorType[circuitnum]; 479 } 480 481 public String getSensorTypeString(int circuitnum) { 482 int sensortype = sensorType[circuitnum]; 483 return sensorTypes.substring(sensortype * 2, sensortype * 2 + 2); 484 } 485 486 public void setSensorType(int circuitnum, int type) { 487 sensorType[circuitnum] = type; 488 } 489 490 public void setSensorTypeString(int circuitnum, String stringtype) { 491 int type = sensorTypes.lastIndexOf(stringtype) / 2; 492 sensorType[circuitnum] = type; 493 } 494 495 public int getSensorPolarity(int circuitnum) { 496 return sensorPolarity[circuitnum]; 497 } 498 499 public String getSensorPolarityString(int circuitnum) { 500 int sensorpolarity = sensorPolarity[circuitnum]; 501 return sensorPolarities.substring(sensorpolarity * 3, sensorpolarity * 3 + 3); 502 } 503 504 public void setSensorPolarity(int circuitnum, int polarity) { 505 sensorPolarity[circuitnum] = polarity; 506 } 507 508 public void setSensorPolarityString(int circuitnum, String stringpolarity) { 509 int polarity = sensorPolarities.lastIndexOf(stringpolarity) / 3; 510 sensorPolarity[circuitnum] = polarity; 511 } 512 513 public int getSensorThreshold(int circuitnum) { 514 return sensorThreshold[circuitnum]; 515 } 516 517 public void setSensorThreshold(int circuitnum, int threshold) { 518 sensorThreshold[circuitnum] = threshold; 519 } 520 521 /** 522 * Public method to return node type. 523 * @return node type number. 524 */ 525 public int getNodeType() { 526 return (nodeType); 527 } 528 529 public String getNodeTypeString() { 530 return moduleTypes.substring(nodeType * 2, nodeType * 2 + 2); 531 } 532 533 /** 534 * Public method to set node type. 535 * @param stringtype string form of node type. 536 */ 537 public void setNodeTypeString(String stringtype) { 538 int type = moduleTypes.lastIndexOf(stringtype) / 2; 539 setNodeType(type); 540 } 541 542 public void setNodeType(int type) { 543 nodeType = type; 544 // set default values for other instance variables 545 switch (type) { 546 case AC: 547 case UN: 548 outputbitsPerCard = 0; 549 sensorbitsPerCard = 0; 550 break; 551 case TB: 552 outputbitsPerCard = 4; 553 sensorbitsPerCard = 4; 554 break; 555 case D8: 556 outputbitsPerCard = 8; 557 sensorbitsPerCard = 0; 558 break; 559 case WM: 560 outputbitsPerCard = 0; 561 sensorbitsPerCard = 8; 562 break; 563 case SC: 564 outputbitsPerCard = 1; 565 sensorbitsPerCard = 0; 566 break; 567 case SM: 568 case SW: 569 case YM: 570 outputbitsPerCard = 16; 571 sensorbitsPerCard = 0; 572 break; 573 case SY: 574 outputbitsPerCard = 0; 575 sensorbitsPerCard = 16; 576 break; 577 default: 578 outputbitsPerCard = 0; 579 sensorbitsPerCard = 0; 580 log.error("Bad node type - {}", Integer.toString(type)); 581 } 582 } 583 584 /** 585 * Public method to return number of bits per card. 586 * @return number of output bits per card. 587 */ 588 public int getNumOutputBitsPerCard() { 589 return (outputbitsPerCard); 590 } 591 592 public int getNumSensorBitsPerCard() { 593 return (sensorbitsPerCard); 594 } 595 596 /** 597 * {@inheritDoc} 598 */ 599 @Override 600 public boolean checkNodeAddress(int address) { 601 return ((address >= 0) && (address < MAXNODE)); 602 } 603 604 /** 605 * Get the number of sensor bits per node. 606 * 607 * @return sensorbitsPerCard 608 */ 609 public int getSensorBitsPerCard() { 610 return (sensorbitsPerCard); 611 } 612 613 /** 614 * Get the transmission delay on this node. 615 * @return delay in 10s of microseconds. 616 */ 617 public int getTransmissionDelay() { 618 return (transmissionDelay); 619 } 620 621 /** 622 * Set transmission delay. 623 * <p> 624 * Note: two bytes are used, so range is 0-65,535. If delay is out of 625 * range, it is restricted to the allowable range. 626 * 627 * @param delay a delay between bytes on receive (units of 10 microsec.) 628 */ 629 public void setTransmissionDelay(int delay) { 630 if ((delay < 0) || (delay > MAXDELAY)) { 631 log.warn("transmission delay {} out of 0-65535 range", 632 Integer.toString(delay)); 633 if (delay < 0) { 634 delay = 0; 635 } 636 if (delay > MAXDELAY) { 637 delay = MAXDELAY; 638 } 639 } 640 transmissionDelay = delay; 641 } 642 643 /** 644 * {@inheritDoc} 645 */ 646 @Override 647 public AbstractMRMessage createInitPacket() { 648 return null; 649 } 650 651 /** 652 * Create a Transmit packet (SerialMessage) to send current state. 653 */ 654 @Override 655 public AbstractMRMessage createOutPacket() { 656 // Set up variables that will be used at the end to send the msg. 657 int cmdlen = 3; // Message length == 3, 4, or 5 658 byte cmdcode = 0x03; // Message command: default == activate output 659 byte addrhi = 0x00; // Used to address more than 255 nodes 660 byte addrlo = 0x00; // Address of node 661 byte settinghi = 0x00; // Used when setting 16 outputs 662 byte settinglo = 0x00; // Used to set output state 663 664 // If we can, we want to send one bulk message for the entire node 665 // We can only send a bulk message if all of the output circuits have 666 // outputType of ONOFF 667 boolean bulk_message = true; 668 int c = 0; 669 while (c < outputbitsPerCard) { 670 if ((outputType[c] != AcelaNode.ONOFF) || (outputSpecial[c] != 0)) { 671 bulk_message = false; 672 } 673 c++; 674 } 675 676 // For now, we are not going to support more than 255 nodes 677 // so we leave addrhi at 0x00. 678 // We need to see if there is a second output circuit for this 679 // node that we need to send. If there is then we need to set 680 // the mustsend flag for the node because the Traffic Controller 681 // reset it before calling this routine. 682 if (!bulk_message) { 683 c = 0; 684 boolean foundfirst = false; 685 boolean foundanother = false; 686 while (c < outputbitsPerCard) { 687 if (outputNeedToSend[c] && foundfirst) { 688 foundanother = true; 689 } 690 if (outputNeedToSend[c] && !foundfirst) { 691 foundfirst = true; 692 } 693 c++; 694 } 695 if (foundanother) { 696 setMustSend(); 697 } 698 } 699 700 // If we are going to do a bulk command then the address will be 701 // the starting address. If we are not going to do a bulk command 702 // then the address will start from the starting address. 703 Integer tempint = startingOutputAddress; 704 addrlo = tempint.byteValue(); 705 706 // For each nodetype set up variables that will end up in the msg 707 if (bulk_message) { 708 if (nodeType == TB) { 709 cmdlen = 4; 710 cmdcode = 0x07; // Set 4 outputs: bit on means output on 711 int tempsettings = (outputArray[3] ^ outputWired[3]) * 8 + (outputArray[2] ^ outputWired[2]) * 4 + (outputArray[1] ^ outputWired[1]) * 2 + (outputArray[0] ^ outputWired[0]) * 1; 712 settinglo = (byte) (tempsettings); 713 } 714 if (nodeType == D8) { 715 cmdlen = 4; 716 cmdcode = 0x08; // Set 8 outputs: bit on means output on 717 int tempsettings = (outputArray[3] ^ outputWired[3]) * 8 + (outputArray[2] ^ outputWired[2]) * 4 + (outputArray[1] ^ outputWired[1]) * 2 + (outputArray[0] ^ outputWired[0]) * 1; 718 tempsettings = (outputArray[7] ^ outputWired[7]) * 128 + (outputArray[6] ^ outputWired[6]) * 64 + (outputArray[5] ^ outputWired[5]) * 32 + (outputArray[4] ^ outputWired[4]) * 16 + tempsettings; 719 settinglo = (byte) (tempsettings); 720 } 721 if ((nodeType == WM) || (nodeType == SY)) { 722 //cmdlen = 3; 723 cmdcode = 0x01; // This really is an error case since these 724 // nodes do not have outputs 725 } 726 if (nodeType == SC) { 727 //cmdlen = 3; 728 cmdcode = 0x01; // This really is an error case since these 729 // nodes do not have outputs 730 } 731 if ((nodeType == SM) || (nodeType == SW) || (nodeType == YM)) { 732 cmdlen = 5; 733 cmdcode = 0x09; // Set 16 outputs: bit on means output on 734 int tempsettings = (outputArray[3] ^ outputWired[3]) * 8 + (outputArray[2] ^ outputWired[2]) * 4 + (outputArray[1] ^ outputWired[1]) * 2 + (outputArray[0] ^ outputWired[0]) * 1; 735 tempsettings = (outputArray[7] ^ outputWired[7]) * 128 + (outputArray[6] ^ outputWired[6]) * 64 + (outputArray[5] ^ outputWired[5]) * 32 + (outputArray[4] ^ outputWired[4]) * 16 + tempsettings; 736 settinglo = (byte) (tempsettings); 737 int tempsettings2 = (outputArray[11] ^ outputWired[11]) * 8 + (outputArray[10] ^ outputWired[10]) * 4 + (outputArray[9] ^ outputWired[9]) * 2 + (outputArray[8] ^ outputWired[8]) * 1; 738 tempsettings2 = (outputArray[15] ^ outputWired[15]) * 128 + (outputArray[14] ^ outputWired[14]) * 64 + (outputArray[13] ^ outputWired[13]) * 32 + (outputArray[12] ^ outputWired[12]) * 16 + tempsettings2; 739 settinghi = (byte) (tempsettings2); 740 } 741 } else { // Not bulk message 742 // For now, we will simply send the first output circuit that 743 // we encounter. In theory, this could starve a later output 744 // circuit on this node. If someone complains then we should 745 // implement a more complicated algorithm. 746 747 c = 0; 748 boolean foundsomething = false; 749 while ((c < outputbitsPerCard) && !foundsomething) { 750 if (outputNeedToSend[c]) { 751 // Need to adjust addr to address the actual output 752 // circuit rather than the starting output address 753 // That it currently points to. 754 Integer tempaddr = c + addrlo; 755 addrlo = tempaddr.byteValue(); 756 757 // Reset the needtosend flag for this output circuit 758 outputNeedToSend[c] = false; 759 760 // Reset the foundfirst flag 761 foundsomething = true; 762 763 // We are here because at least one output circuit on 764 // this node is not set to a type of ONOFF 765 // -- but some of the output circuits may still be 766 // of type ONOFF. 767 if (outputSpecial[c] == 0) { 768 if (outputType[c] == AcelaNode.ONOFF) { 769 // outputArray[c] tells us to to turn the output on 770 // or off. 771 // outputWired[c] tells us whether the relay is 772 // wired backwards. 773 // command 0x01 is activate 774 // command 0x02 is deactivate 775 int tempcommand = (outputArray[c] ^ outputWired[c]); 776 if (tempcommand == 0) { 777 tempcommand = 2; 778 } 779 cmdcode = (byte) (tempcommand); 780 cmdlen = 3; 781 } 782 783 if (outputType[c] == AcelaNode.BLINK) { 784 // outputArray[c] tells us to to turn the output on 785 // or off. 786 // outputWired[c] tells us whether the output 787 // should start on or start off. 788 // outputLength[c] tells us how long in tenths of 789 // a second to blink. 790 // output will continue to blink until turned off. 791 // command 0x02 is deactivate 792 // command 0x05 is blink 793 // command 0x06 is reverse blink 794 int tempcommand = outputArray[c]; 795 if ((tempcommand == 1) && (outputWired[c] == 1)) { 796 tempcommand = 5; 797 } 798 if ((tempcommand == 1) && (outputWired[c] == 0)) { 799 tempcommand = 6; 800 } 801 if (tempcommand == 0) { 802 tempcommand = 2; 803 } 804 cmdcode = (byte) (tempcommand); 805 if (cmdcode == 0x02) { 806 cmdlen = 3; 807 } else { 808 cmdlen = 4; 809 settinglo = (byte) outputLength[c]; 810 } 811 } 812 813 if (outputType[c] == AcelaNode.PULSE) { 814 // outputArray[c] tells us to to turn the output on 815 // or off. 816 // outputWired[c] tells us whether the output 817 // should start on or start off. 818 // outputLength[c] tells us how long in tenths of 819 // a second to pulse the output. 820 // output will actually return to off state after 821 // the pulse duration -- but we will never know. 822 // Program will need to fake this out. 823 // command 0x02 is deactivate 824 // command 0x03 is to pulse on 825 // command 0x04 is to pulse off 826 int tempcommand = outputArray[c]; 827 if ((tempcommand == 1) && (outputWired[c] == 1)) { 828 tempcommand = 4; 829 } 830 if ((tempcommand == 1) && (outputWired[c] == 0)) { 831 tempcommand = 3; 832 } 833 if (tempcommand == 0) { 834 tempcommand = 2; 835 } 836 cmdcode = (byte) (tempcommand); 837 if (cmdcode == 0x02) { 838 cmdlen = 3; 839 } else { 840 cmdlen = 4; 841 settinglo = (byte) outputLength[c]; 842 } 843 } 844 } else { 845 switch (outputSignalHeadType[c]) { 846 case DOUBLE: { 847 switch (outputSpecial[c]) { 848 case 1: 849 cmdcode = 0x0c; 850 settinglo = 0x01; 851 break; // Red 852 case 2: 853 cmdcode = 0x0c; 854 settinglo = 0x02; 855 break; // Flashing red 856 case 3: // Yellow 857 case 4: // Flashing Yellow 858 case 6: // Flashing Green 859 cmdcode = 0x0c; 860 settinglo = 0x08; 861 break; 862 case 5: 863 cmdcode = 0x0c; 864 settinglo = 0x04; 865 break; // Green 866 case 7: 867 cmdcode = 0x0c; 868 settinglo = 0x00; 869 break; // Dark 870 default: 871 cmdcode = 0x0c; 872 settinglo = 0x03; 873 break; // Flashing red 874 } 875 break; 876 } 877 case TRIPLE: { 878 switch (outputSpecial[c]) { 879 case 1: 880 cmdcode = 0x0d; 881 settinglo = 0x01; 882 break; // Red 883 case 2: 884 cmdcode = 0x0d; 885 settinglo = 0x02; 886 break; // Flashing red 887 case 3: 888 cmdcode = 0x0d; 889 settinglo = 0x04; 890 break; // Yellow 891 case 4: 892 cmdcode = 0x0d; 893 settinglo = 0x08; 894 break; // Flashing Yellow 895 case 5: 896 cmdcode = 0x0d; 897 settinglo = 0x10; 898 break; // Green 899 case 6: 900 cmdcode = 0x0d; 901 settinglo = 0x20; 902 break; // Flashing Green 903 case 7: 904 cmdcode = 0x0d; 905 settinglo = 0x00; 906 break; // Dark 907 default: 908 cmdcode = 0x0d; 909 settinglo = 0x03; 910 break; // Flashing red 911 } 912 break; 913 } 914 case BPOLAR: { 915 switch (outputSpecial[c]) { 916 case 1: 917 cmdcode = 0x0c; 918 settinglo = 0x01; 919 break; // Red 920 case 2: 921 cmdcode = 0x0c; 922 settinglo = 0x02; 923 break; // Flashing red 924 case 3: 925 cmdcode = 0x0c; 926 settinglo = 0x10; 927 break; // Yellow 928 case 4: 929 cmdcode = 0x0c; 930 settinglo = 0x20; 931 break; // Flashing Yellow 932 case 5: 933 cmdcode = 0x0c; 934 settinglo = 0x04; 935 break; // Green 936 case 6: 937 cmdcode = 0x0c; 938 settinglo = 0x08; 939 break; // Flashing Green 940 case 7: 941 cmdcode = 0x0c; 942 settinglo = 0x00; 943 break; // Dark 944 default: 945 cmdcode = 0x0c; 946 settinglo = 0x03; 947 break; // Flashing red 948 } 949 break; 950 } 951 case WIGWAG: { 952 switch (outputSpecial[c]) { 953 case 1: // Red 954 case 2: // Flashing red 955 case 3: 956 case 4: // Flashing Yellow 957 case 5: // Green 958 case 6: // Flashing Green 959 cmdcode = 0x0c; 960 settinglo = 0x0B; 961 break; 962 case 7: // Dark 963 cmdcode = 0x0c; 964 settinglo = 0x00; 965 break; 966 default: // Flashing red 967 cmdcode = 0x0c; 968 settinglo = 0x0F; 969 break; 970 } 971 break; 972 } 973 default: { 974 switch (outputSpecial[c]) { 975 case 1: 976 cmdcode = 0x0d; 977 settinglo = 0x01; 978 break; // Red 979 case 3: 980 cmdcode = 0x0d; 981 settinglo = 0x04; 982 break; // Yellow 983 case 4: 984 cmdcode = 0x0d; 985 settinglo = 0x08; 986 break; // Flashing Yellow 987 case 5: 988 cmdcode = 0x0d; 989 settinglo = 0x10; 990 break; // Green 991 case 6: 992 cmdcode = 0x0d; 993 settinglo = 0x30; 994 break; // Flashing Green 995 case 7: 996 cmdcode = 0x0d; 997 settinglo = 0x00; 998 break; // Dark 999 case 2: // Flashing red 1000 default: 1001 cmdcode = 0x0d; 1002 settinglo = 0x03; 1003 break; 1004 } 1005 } 1006 } 1007 cmdlen = 4; 1008 } 1009 } 1010 c++; 1011 } 1012 } 1013 1014 AcelaMessage m = new AcelaMessage(cmdlen); 1015 m.setElement(0, cmdcode); 1016 m.setElement(1, addrhi); 1017 m.setElement(2, addrlo); 1018 if (cmdlen > 3) { 1019 if (cmdlen > 4) { 1020 m.setElement(3, settinghi); 1021 } else { 1022 m.setElement(3, settinglo); 1023 } 1024 } 1025 if (cmdlen > 4) { 1026 m.setElement(4, settinglo); 1027 } 1028 m.setBinary(true); 1029 return m; 1030 } 1031 boolean warned = false; 1032 1033 /** 1034 * Use the contents of the poll reply to mark changes. 1035 * 1036 * @param l Reply to a poll operation 1037 */ 1038 public void markChanges(AcelaReply l) { 1039 1040 // We are going to get back 8 bits per byte from the poll. 1041 // We have three types of sensor modules: 1042 // TB with 4 sensor inputs, WM with 8 sensor inputs, SY with 16 sensor inputs 1043 // The TB causes two cases: either the bits we want start at bit 0 or bit 4. 1044 // The sensor bits we want for a TB will always be within one byte. 1045 // The sensor bits we want for a WM could be within one byte if we start at 0, 1046 // or spread across two bytes if we start at 4. 1047 // The sensor bits we want for a SY could be within two byte if we start at 0, 1048 // or spread across three bytes if we start at 4. 1049 int firstByteNum = startingSensorAddress / 8; 1050 int firstBitAt = startingSensorAddress % 8; // mod operator 1051 //int numBytes = 1; // For TB there are only 4 sensors so always 1 byte 1052 1053 log.debug("Sensor Parsing for module type: {}", moduleNames[nodeType]); 1054 log.debug("Sensor Parsing has startingSensorAddress: {}", startingSensorAddress); 1055 log.debug("Sensor Parsing has firstByteNum: {}", firstByteNum); 1056 log.debug("Sensor Parsing has firstBitAt: {}", firstBitAt); 1057 1058 // Using rawvalue may be unnecessary, but trying to minimize reads to getElement 1059 int rawvalue = l.getElement(firstByteNum); 1060 log.debug("Sensor Parsing has first rawvalue: {}", Integer.toHexString(rawvalue)); 1061 1062 int usingByteNum = 0; 1063 1064 try { 1065 for (int i = 0; i < sensorbitsPerCard; i++) { 1066 if (sensorArray[i] == null) { 1067 log.debug("Sensor Parsing for Sensor: {} + {} was skipped", startingSensorAddress, i); 1068 continue; 1069 } // skip ones that don't exist 1070 1071 log.debug("Sensor Parsing for Sensor: {} + {}", startingSensorAddress, i); 1072 1073 int relvalue = rawvalue; 1074 1075 // Need a temporary counter within the byte so we can shift 1076 int tempi = i; 1077 1078 // If necessary, shift by four before we start 1079 if (usingByteNum == 0) { 1080 if (firstBitAt == 4) { 1081 for (int j = 0; j < firstBitAt; j++) { 1082 relvalue = relvalue >> 1; 1083 } 1084 log.debug("Sensor Parsing for Sensor: {} + {} shifted by 4: {}", startingSensorAddress, i, Integer.toHexString(relvalue)); 1085 } 1086 } 1087 1088 // If necessary, get next byte 1089 if (firstBitAt == 4) { 1090 if (i >= 12) { // Will only get here if there are 16 sensors 1091 usingByteNum = 2; 1092 // Using rawvalue may be unnecessary, but trying to minimize reads to getElement 1093 rawvalue = l.getElement(usingByteNum + firstByteNum); 1094 log.debug("Sensor Parsing (1stat4) has third rawvalue: {}", Integer.toHexString(rawvalue)); 1095 relvalue = rawvalue; 1096 tempi = i - 12; // tempi needs to shift down by 12 1097 } else { 1098 if (i >= 4) { 1099 usingByteNum = 1; 1100 // Using rawvalue may be unnecessary, but trying to minimize reads to getElement 1101 rawvalue = l.getElement(usingByteNum + firstByteNum); 1102 log.debug("Sensor Parsing (1stat4) has second rawvalue: {}", Integer.toHexString(rawvalue)); 1103 relvalue = rawvalue; 1104 tempi = i - 4; // tempi needs to shift down by 4 1105 } 1106 } 1107 } else { 1108 if (i >= 8) { // Will only get here if there are 16 sensors 1109 usingByteNum = 1; 1110 // Using rawvalue may be unnecessary, but trying to minimize reads to getElement 1111 rawvalue = l.getElement(usingByteNum + firstByteNum); 1112 log.debug("Sensor Parsing has second rawvalue: {}", Integer.toHexString(rawvalue)); 1113 relvalue = rawvalue; 1114 tempi = i - 8; // tempi needs to shift down by 8 1115 } 1116 } 1117 1118 log.debug("Sensor Parsing for Sensor: {} + {} has tempi: {}", startingSensorAddress, i, tempi); 1119 1120 // Finally we can isolate the bit from the poll result 1121 for (int j = 0; j < tempi; j++) { 1122 relvalue = relvalue >> 1; 1123 } 1124 1125 log.debug("Sensor Parsing for Sensor: {} + {} has relvalue: {}", startingSensorAddress, i, Integer.toHexString(relvalue)); 1126 1127 // Now that we have the relvalue need to compare to last time 1128 boolean nooldstate = false; 1129 byte oldstate = 0x00; 1130 if (sensorLastSetting[i] == Sensor.ACTIVE) { 1131 oldstate = 0x01; 1132 } else { 1133 if (sensorLastSetting[i] == Sensor.INACTIVE) { 1134 oldstate = 0x00; 1135 } else { 1136 nooldstate = true; 1137 } 1138 } 1139 int newerstate = relvalue & 0x01; 1140 byte newstate = (byte) (newerstate); 1141 1142 if ((nooldstate) || (oldstate != newstate)) { 1143 if (nooldstate) { 1144 log.debug("Sensor Parsing for Sensor: {} + {} had no old state.", startingSensorAddress, i); 1145 } 1146 if (newstate == 0x00) { 1147 sensorLastSetting[i] = Sensor.INACTIVE; 1148 sensorArray[i].setKnownState(sensorLastSetting[i]); 1149 } else { 1150 sensorLastSetting[i] = Sensor.ACTIVE; 1151 sensorArray[i].setKnownState(sensorLastSetting[i]); 1152 } 1153 log.debug("Sensor Parsing for Sensor: {} + {} changed state: {} rawvalue: {}", startingSensorAddress, i, sensorLastSetting[i], Integer.toHexString(rawvalue)); 1154 } else { 1155 log.debug("Sensor Parsing for Sensor: {} + {} did NOT change state: {} rawvalue: {}", startingSensorAddress, i, sensorLastSetting[i], Integer.toHexString(rawvalue)); 1156 } 1157 } 1158 } catch (JmriException e) { 1159 log.error("exception in markChanges", e); 1160 } 1161 } 1162 1163 /** 1164 * Register a sensor on an Acela node. 1165 * The numbers here are 0 to MAXSENSORBITS, not 1 to MAXSENSORBITS. 1166 * 1167 * @param s Sensor object 1168 * @param rawaddr index number of sensor's input bit on this 1169 * node, valid range from 0 to MAXSENSORBITS 1170 */ 1171 public void registerSensor(Sensor s, int rawaddr) { 1172 // validate the sensor ordinal 1173 if ((rawaddr < 0) || (rawaddr >= MAXNODE)) { 1174 log.error("Unexpected sensor ordinal in registerSensor: {}", Integer.toString(rawaddr)); 1175 return; 1176 } 1177 1178 int addr = -1; 1179 addr = rawaddr - startingSensorAddress; 1180 1181 hasActiveSensors = true; 1182 InstanceManager.getDefault(AcelaSystemConnectionMemo.class).getTrafficController().setAcelaSensorsState(true); 1183 if (startingSensorAddress < 0) { 1184 log.info("Trying to register sensor too early: {}S{}", 1185 InstanceManager.getDefault(AcelaSystemConnectionMemo.class).getSystemPrefix(), // multichar prefix 1186 rawaddr); 1187 } else { 1188 1189 if (sensorArray[addr] == null) { 1190 sensorArray[addr] = s; 1191 if (!sensorHasBeenInit[addr]) { 1192 sensorNeedInit[addr] = true; 1193 } 1194 } else { 1195 // multiple registration of the same sensor 1196 log.warn("Multiple registration of same sensor: {}S{}", 1197 InstanceManager.getDefault(AcelaSystemConnectionMemo.class).getSystemPrefix(), // multichar prefix 1198 rawaddr); 1199 } 1200 } 1201 } 1202 int timeout = 0; 1203 1204 /** 1205 * {@inheritDoc} 1206 */ 1207 @Override 1208 public boolean handleTimeout(AbstractMRMessage m, AbstractMRListener l) { 1209 timeout++; 1210 if (log.isDebugEnabled()) { 1211 log.warn("Timeout to poll for UA={}: consecutive timeouts: {}", nodeAddress, timeout); 1212 } 1213 return false; // tells caller to NOT force init 1214 } 1215 1216 /** 1217 * {@inheritDoc} 1218 */ 1219 @Override 1220 public void resetTimeout(AbstractMRMessage m) { 1221 if (timeout > 0) { 1222 log.debug("Reset {} timeout count", timeout); 1223 } 1224 timeout = 0; 1225 } 1226 1227 private final static Logger log = LoggerFactory.getLogger(AcelaNode.class); 1228 1229}