001package jmri.jmrix.cmri.serial; 002 003import java.util.Arrays; 004import jmri.JmriException; 005import jmri.Sensor; 006import jmri.jmrix.AbstractMRListener; 007import jmri.jmrix.AbstractMRMessage; 008import jmri.jmrix.AbstractNode; 009import jmri.jmrix.cmri.serial.serialmon.SerialFilterFrame; 010 011/** 012 * Models a serial C/MRI node, consisting of a (S)USIC and attached cards. 013 * <p> 014 * Nodes are numbered ala the UA number, from 1 to 63. Node number 1 carries 015 * sensors 1 to 999, node 2 1001 to 1999 etc. 016 * <p> 017 * The array of sensor states is used to update sensor known state only when 018 * there's a change on the serial bus. This allows for the sensor state to be 019 * updated within the program, keeping this updated state until the next change 020 * on the serial bus. E.g. you can manually change a state via an icon, and not 021 * have it change back the next time that node is polled. 022 * <p> 023 * The SMINI is defined as having 1 input and 2 outputs cards.<br> 024 * USIC/SUSIC nodes can have 0-63 inputs and 0-63 output cards, but no more than 025 * 64 total cards. 026 * 027 * A CPNODE (Control Point Node) is defined as having 2 inputs and 2 outputs //c2 028 * on the node board and 0-128 bits of input or output (in 8 bit increments) 029 * for added I/O extender cards IOX16,IOX32. 030 * 031 * A CPMEGA (Open Source Node) is defined as having 8 bytes of input or output //c2 032 * on the node board and 0-128 bits of input or output (in 8 bit increments) 033 * for added I/O extender cards IOX16,IOX32. 034 * 035 * @author Bob Jacobsen Copyright (C) 2003, 2008 036 * @author Bob Jacobsen, Dave Duchamp, multiNode extensions, 2004 037 * @author Chuck Catania, cpNode Extensions 2013, 2014, 2015, 2016 038*/ 039public class SerialNode extends AbstractNode { 040 041 /** 042 * Maximum number of sensors a node can carry. 043 * <p> 044 * Note this is less than a current SUSIC motherboard can have, but should 045 * be sufficient for all reasonable layouts. 046 * <p> 047 * Must be less than, and is general one less than, 048 * {@link SerialSensorManager#SENSORSPERUA} 049 */ 050 static final int MAXSENSORS = 999; 051 052 public static final int MAXSEARCHLIGHTBYTES = 48; 053 public static final int MAXCARDLOCATIONBYTES = 64; 054 055 // class constants 056 public static final int SMINI = 1; // SMINI node type 057 public static final int USIC_SUSIC = 2; // USIC/SUSIC node type 058 public static final int CPNODE = 3; // cpNode Control Point (Arduino) node type c2 059 public static final int CPMEGA = 4; // Open Source Node (OSN) e.g Mega2560 R3 c2 060 public static final int ESP32NODE = 5; // ESP32Node: self-contained ESP32 CMRI-over-WiFi node, 061 // 8 MCP23017 I2C expanders (0x20-0x27), 16 bytes of 062 // output/input, no onboard I/O of its own -- unlike 063 // CPNODE/CPMEGA, its cardTypeLocation[] array has no 064 // reserved onboard-byte slots (index 0 IS the first real 065 // card), but user-facing "Card N" numbers displayed in 066 // DiagnosticFrame/NodeConfigManagerFrame are still 1-based 067 // (Card 1 = 0x20/A) via a small +1 display offset there, 068 // matching how people naturally count cards -- a smaller, 069 // separate offset from CPNODE's +2 (2 onboard output bytes). 070 071 public static final int NDP_USICSUSIC24 = 78; // 'N' USIC/SUSIC 24 bit cards 072 public static final int NDP_USICSUSIC32 = 88; // 'X' USIC/SUSIC 32 bit cards 073 public static final int NDP_SMINI = 77; // 'M' SMINI 24 bit cards 074 public static final int NDP_CPNODE = 67; // 'C' CPNODE 8 bit cards 075 public static final int NDP_CPMEGA = 79; // 'O' CPMEGA 8 bit cards 076 public static final int NDP_ESP32NODE = 69; // 'E' ESP32Node 8 bit cards 077 078 public static final byte INPUT_CARD = 1; // USIC/SUSIC input card type for specifying location 079 public static final byte OUTPUT_CARD = 2; // USIC/SUSIC output card type for specifying location 080 public static final byte NO_CARD = 0; // USIC/SUSIC unused location 081 082 // node definition instance variables (must persist between runs) 083 protected int nodeType = SMINI; // See above 084 protected int bitsPerCard = 24; // 24 for SMINI and USIC, 24 or 32 for SUSIC 085 protected int transmissionDelay = 0; // DL, delay between bytes on Receive (units of 10 microsec.) 086 protected int pulseWidth = 500; // Pulse width for pulsed turnout control (milliseconds) 087 protected int num2LSearchLights = 0; // SMINI only, 'NS' number of two lead bicolor signals 088 protected byte[] locSearchLightBits = new byte[MAXSEARCHLIGHTBYTES]; // SMINI only, 0 = not searchlight LED, 089 // 1 = searchlight LED, 2*NS bits must be set to 1 090 protected byte[] cardTypeLocation = new byte[MAXCARDLOCATIONBYTES]; // Varys on USIC/SUSIC. There must numInputCards bytes set to 091 // INPUT_CARD, and numOutputCards set to OUTPUT_CARD, with 092 // the remaining locations set to NO_CARD. All 093 // NO_CARD locations must be at the end of the array. The 094 // array is indexed by card address. 095 // operational instance variables (should not be preserved between runs) 096 097 // cpNode/Open Source Node variables c2 098 public static final int INITMSGLEN = 12; 099 public static final int NUMCMRINETOPTS = 16; 100 public static final int NUMCPNODEOPTS = 16; 101 protected int cmrinetOptions[] = new int[NUMCMRINETOPTS]; // CMRInet options stored as 16 binary digits 102 protected int cpnodeOptions[] = new int[NUMCPNODEOPTS]; // cpNode options stored as 16 binary digits 103 104 protected String cmriNodeDesc = ""; // CMRI node name for display 105 protected int pollListPosition = 0; 106 107 public int pollStatus = 1; 108 public static final int POLLSTATUS_ERROR = 0; 109 public static final int POLLSTATUS_IDLE = 1; 110 public static final int POLLSTATUS_POLLING = 2; 111 public static final int POLLSTATUS_TIMEOUT = 3; 112 public static final int POLLSTATUS_INIT = 4; 113 114 // CMRInet options stored in XML 115 public static final int optbitNet_AUTOPOLL = 0; 116 public static final int optbitNet_USECMRIX = 1; 117 public static final int optbitNet_USEBCC = 2; 118 public static final int optbitNet_BIT8 = 8; 119 public static final int optbitNet_BIT15 = 15; 120 121 // cpNode/osNode options in initialization message 122 public static final int optbitNode_USECMRIX = 0; 123 public static final int optbitNode_SENDEOT = 1; 124 public static final int optbitNode_USEBCC = 2; 125 public static final int optbitNode_BIT8 = 8; 126 public static final int optbitNode_BIT15 = 15; 127 128 protected byte[] outputArray = new byte[256]; // current values of the output bits for this node 129 protected boolean hasActiveSensors = false; // 'true' if there are active Sensors for this node 130 protected int lastUsedSensor = 0; // grows as sensors defined 131 protected Sensor[] sensorArray = new Sensor[MAXSENSORS + 1]; 132 protected int[] sensorLastSetting = new int[MAXSENSORS + 1]; 133 protected int[] sensorTempSetting = new int[MAXSENSORS + 1]; 134 135 protected boolean monitorNodePackets = true; 136 protected boolean[] monitorPacketBits = new boolean[SerialFilterFrame.numMonPkts]; 137 138 /** 139 * Assumes a node address of 0, and a node type of SMINI. 140 * If this constructor 141 * is used, actual node address must be set using setNodeAddress, and actual 142 * node type using 'setNodeType' 143 * @param tc system connection traffic controller. 144 */ 145 public SerialNode(SerialTrafficController tc) { 146 this(0, SMINI,tc); 147 } 148 149 /** 150 * Creates a new SerialNode and initialize default instance variables. 151 * @param address Address of node on CMRI serial bus (0-127). 152 * @param type Node type, e.g. SMINI or USIC_SUSIC. 153 * @param tc system connection traffic controller. 154 */ 155 public SerialNode(int address, int type, SerialTrafficController tc) { 156 // set address and type and check validity 157 setNodeAddress(address); 158 setNodeType(type); 159 // set default values for other instance variables 160 bitsPerCard = 24; 161 transmissionDelay = 0; 162 num2LSearchLights = 0; 163 for (int i = 0; i < MAXSEARCHLIGHTBYTES; i++) { 164 locSearchLightBits[i] = 0; 165 } 166 // note: setNodeType initialized cardTypeLocation[]; 167 // clear the Sensor arrays 168 for (int i = 0; i < MAXSENSORS + 1; i++) { 169 sensorArray[i] = null; 170 sensorLastSetting[i] = Sensor.UNKNOWN; 171 sensorTempSetting[i] = Sensor.UNKNOWN; 172 } 173 // clear all output bits 174 for (int i = 0; i < 256; i++) { 175 outputArray[i] = 0; 176 } 177 // initialize other operational instance variables 178 setMustSend(); 179 setOptNet_AUTOPOLL(1); // always start with polling enabled 180 hasActiveSensors = false; 181 // register this node 182 tc.registerNode(this); 183 } 184 185 public int getNum2LSearchLights() { 186 return num2LSearchLights; 187 } 188 189 public void setNum2LSearchLights(int n) { 190 num2LSearchLights = n; 191 } 192 193 public byte[] getLocSearchLightBits() { 194 return Arrays.copyOf(locSearchLightBits, locSearchLightBits.length); 195 } 196 197 public void setLocSearchLightBits(int num, int value) { 198 locSearchLightBits[num] = (byte) (value & 0xFF); 199 } 200 201 public byte[] getCardTypeLocation() { 202 return Arrays.copyOf(cardTypeLocation, cardTypeLocation.length); 203 } 204 205 public void setCardTypeLocation(int num, int value) { 206 // Validate the input 207 if ((num < 0) || (num >= MAXCARDLOCATIONBYTES)) { 208 log.error("setCardTypeLocation - invalid num (index) - {}", num); 209 return; 210 } 211 int val = value & 0xFF; 212 if ((val != NO_CARD) && (val != INPUT_CARD) && (val != OUTPUT_CARD)) { 213 log.error("setCardTypeLocation - invalid value - {}", val); 214 return; 215 } 216 // Set the card type 217 cardTypeLocation[num] = (byte) (val); 218 } 219 220 /** 221 * Set a single output bit. 222 * @param bitNumber bit number, bits are numbered from 1 (not 0). 223 * @param state true for 0, false for 1. 224 */ 225 public void setOutputBit(int bitNumber, boolean state) { 226 // locate in the outputArray 227 int byteNumber = (bitNumber - 1) / 8; 228 // validate that this byte number is defined 229 if (byteNumber > (numOutputCards() * (bitsPerCard / 8))) { 230 warn("Output bit out-of-range for defined node"); 231 } 232 if (byteNumber >= 256) { 233 byteNumber = 255; 234 } 235 // update the byte 236 byte bit = (byte) (1 << ((bitNumber - 1) % 8)); 237 byte oldByte = outputArray[byteNumber]; 238 if (state) { 239 outputArray[byteNumber] &= (~bit); 240 } else { 241 outputArray[byteNumber] |= bit; 242 } 243 // check for change, necessitating a send 244 if (oldByte != outputArray[byteNumber]) { 245 setMustSend(); 246 } 247 } 248 249 /** 250 * Get the current state of a single output bit. 251 * @param bitNumber bit number, bits are numbered from 1 (not 0). 252 * @return true for 0, false for 1. 253 */ 254 public boolean getOutputBit(int bitNumber) { 255 // locate in the outputArray 256 int byteNumber = (bitNumber - 1) / 8; 257 // validate that this byte number is defined 258 if (byteNumber > (numOutputCards() * (bitsPerCard / 8))) { 259 warn("Output bit out-of-range for defined node"); 260 } 261 if (byteNumber >= 256) { 262 byteNumber = 255; 263 } 264 // update the byte 265 byte bit = (byte) (1 << ((bitNumber - 1) % 8)); 266 byte testByte = outputArray[byteNumber]; 267 testByte &= bit; 268 if (testByte == 0) { 269 return (true); 270 } else { 271 return (false); 272 } 273 } 274 275 /** 276 * Get state of Sensor polling. Note: returns 'true' if at least one sensor 277 * is active for this node 278 */ 279 @Override 280 public boolean getSensorsActive() { 281 return hasActiveSensors; 282 } 283 284 /** 285 * Set state of Sensor polling. 286 * Used to disable polling for test purposes only. 287 * @param flag true to set active flag, else false. 288 */ 289 public void setSensorsActive(boolean flag) { 290 hasActiveSensors = flag; 291 } 292 293 /** 294 * Get number of input cards. 295 * @return number of input cards. 296 */ 297 public int numInputCards() { 298 int result = 0; 299 for (int i = 0; i < cardTypeLocation.length; i++) { 300 if (cardTypeLocation[i] == INPUT_CARD) { 301 result++; 302 } 303 } 304 305/* 306 // check consistency 307 if (nodeType == SMINI && result != 1) { 308 warn("C/MRI SMINI node with " + result + " input cards"); 309 } 310 if (nodeType == USIC_SUSIC && result >= MAXCARDLOCATIONBYTES) { 311 warn("C/MRI USIC/SUSIC node with " + result + " input cards"); 312*/ 313 switch (nodeType) //c2 314 { 315 case SMINI: if (result!=1) 316 { 317 warn("SMINI with "+result+" INPUT cards"); 318 } 319 break; 320 case USIC_SUSIC: if(result>=MAXCARDLOCATIONBYTES) 321 warn("USIC/SUSIC node with "+result+" INPUT cards"); 322 break; 323 case CPNODE: if(result<2) //c2 324 warn("CPNODE node with "+result+" INPUT cards"); 325 break; 326 case CPMEGA: if(result<1) //c2 327 warn("CPMEGA node with "+result+" INPUT cards"); 328 break; 329 case ESP32NODE: // no minimum -- all 16 ports default to OUTPUT and can be 330 // reconfigured to INPUT (or NOT CONNECTED) individually, 331 // unlike CPNODE/CPMEGA which always have fixed onboard bytes 332 break; 333 default: 334 break; 335 } 336 337 338 return result; 339 } 340 341 /** 342 * Get number of output cards. 343 * @return number of output cards. 344 */ 345 public int numOutputCards() { 346 int result = 0; 347 for (int i = 0; i < cardTypeLocation.length; i++) { 348 if (cardTypeLocation[i] == OUTPUT_CARD) { 349 result++; 350 } 351 } 352/* 353 // check consistency 354 if (nodeType == SMINI && result != 2) { 355 warn("C/MRI SMINI node with " + result + " output cards"); 356 } 357 if (nodeType == USIC_SUSIC && result >= MAXCARDLOCATIONBYTES) { 358 warn("C/MRI USIC/SUSIC node with " + result + " output cards"); 359 } 360*/ 361 switch (nodeType) //c2 362 { 363 case SMINI: if (result!=2) 364 { 365 warn("SMINI with "+result+" OUTPUT cards"); 366 } 367 break; 368 case USIC_SUSIC: 369 if(result>=MAXCARDLOCATIONBYTES) 370 warn("USIC/SUSIC node with "+result+" OUTPUT cards"); 371 break; 372 case CPNODE: //c2 373 if(result<2) 374 warn("CPNODE node with "+result+" OUTPUT cards"); 375 break; 376 case CPMEGA: //c2 377 if(result<1) 378 warn("CPMEGA node with "+result+" OUTPUT cards"); 379 break; 380 case ESP32NODE: 381 if(result<1) 382 warn("ESP32Node with "+result+" OUTPUT cards"); 383 break; 384 default: 385 } 386 387 return result; 388 } 389 390 /** 391 * Get node type Current types are: SMINI, USIC_SUSIC, 392 * @return node type, e.g. USIC_SUSIC. 393 */ 394 public int getNodeType() { 395 return (nodeType); 396 } 397 398 /** 399 * Set node type. 400 * <p> 401 * Current types are: SMINI, USIC_SUSIC 402 * For SMINI, also sets cardTypeLocation[] and bitsPerCard. 403 * For USIC_SUSIC, also clears cardTypeLocation. 404 * @param type node type, e.g. USIC_SUSIC. 405 */ 406 public void setNodeType(int type) { 407 408/* if (type == SMINI) { 409 nodeType = type; 410 bitsPerCard = 24; 411 // set cardTypeLocation for SMINI 412 cardTypeLocation[0] = OUTPUT_CARD; 413 cardTypeLocation[1] = OUTPUT_CARD; 414 cardTypeLocation[2] = INPUT_CARD; 415 for (int i = 3; i < MAXCARDLOCATIONBYTES; i++) { 416 cardTypeLocation[i] = NO_CARD; 417 } 418 } else if (type == USIC_SUSIC) { 419 nodeType = type; 420 // clear cardTypeLocations 421 for (int i = 0; i < MAXCARDLOCATIONBYTES; i++) { 422 cardTypeLocation[i] = NO_CARD; 423 } 424 } // here recognize other node types 425 else { 426 log.error("Bad node type - " + Integer.toString(type)); 427 } 428*/ 429 switch(type) //c2 430 { 431 case SMINI: 432 nodeType = type; 433 bitsPerCard = 24; 434 // set cardTypeLocation for SMINI 435 cardTypeLocation[0] = OUTPUT_CARD; 436 cardTypeLocation[1] = OUTPUT_CARD; 437 cardTypeLocation[2] = INPUT_CARD; 438 for (int i=3;i<MAXCARDLOCATIONBYTES;i++) 439 { 440 cardTypeLocation[i] = NO_CARD; 441 } 442 break; 443 case USIC_SUSIC: 444 nodeType = type; 445 // clear cardTypeLocations 446 for (int i=0;i<MAXCARDLOCATIONBYTES;i++) 447 { 448 cardTypeLocation[i] = NO_CARD; 449 } 450 break; 451 case CPNODE: //c2 452 nodeType = type; 453 bitsPerCard = 8; 454 455 // set cardTypeLocation for CPNODE. First four bytes are onboard 456 cardTypeLocation[0] = INPUT_CARD; 457 cardTypeLocation[1] = INPUT_CARD; 458 cardTypeLocation[2] = OUTPUT_CARD; 459 cardTypeLocation[3] = OUTPUT_CARD; 460 for (int i=4;i<MAXCARDLOCATIONBYTES;i++) 461 { 462 cardTypeLocation[i] = NO_CARD; 463 } 464 break; 465 466 case CPMEGA: //c2 467 nodeType = type; 468 bitsPerCard = 8; 469 470 // set cardTypeLocation for CPMEGA. First eight bytes are onboard 471 cardTypeLocation[0] = INPUT_CARD; 472 cardTypeLocation[1] = NO_CARD; 473 cardTypeLocation[2] = NO_CARD; 474 cardTypeLocation[3] = NO_CARD; 475 cardTypeLocation[4] = NO_CARD; 476 cardTypeLocation[5] = NO_CARD; 477 cardTypeLocation[6] = NO_CARD; 478 cardTypeLocation[7] = NO_CARD; 479 for (int i=8;i<MAXCARDLOCATIONBYTES;i++) 480 { 481 cardTypeLocation[i] = NO_CARD; 482 } 483 break; 484 485 case ESP32NODE: 486 nodeType = type; 487 bitsPerCard = 8; 488 489 // ESP32Node has NO onboard I/O of its own (unlike CPNODE's 2 input + 490 // 2 output onboard bytes, or CPMEGA's 1 onboard input byte) -- it is a 491 // self-contained ESP32 board whose only I/O comes from up to 8 external 492 // MCP23017 I2C expanders at addresses 0x20-0x27, 2 bytes (ports A/B) 493 // each = 16 real cards. All 16 default to OUTPUT here (matching the 494 // firmware's own default of every IOX port starting as OUTPUT); the 495 // node-config UI lets the user change individual cards to INPUT or 496 // NOT CONNECTED afterward, same as CPNODE/CPMEGA's IOX expansion cards. 497 // Because there's no reserved onboard offset, card 0 in this array IS 498 // the first real card (chip at 0x20, port A) -- no "+2"-style skip is 499 // needed anywhere this type is handled (see DiagnosticFrame, 500 // NodeConfigManagerFrame, which special-case CPNODE's onboard offset 501 // and must NOT apply it to ESP32NODE). 502 for (int i=0;i<16;i++) 503 { 504 cardTypeLocation[i] = OUTPUT_CARD; 505 } 506 for (int i=16;i<MAXCARDLOCATIONBYTES;i++) 507 { 508 cardTypeLocation[i] = NO_CARD; 509 } 510 break; 511 512// here recognize other node types 513 default: 514 log.error("Bad node type - {}", Integer.toString(type)); 515 } 516 517 } 518 519 /** 520 * Get number of bits per card. 521 * @return number of bits per card. 522 */ 523 public int getNumBitsPerCard() { 524 return (bitsPerCard); 525 } 526 527 /** 528 * Set number of bits per card. 529 * @param bits number of bits. 530 */ 531 public void setNumBitsPerCard(int bits) { 532 if ((bits == 24) || (bits == 32) || (bits == 16) || (bits == 8)) { 533 bitsPerCard = bits; 534 } else { 535 log.warn("unexpected number of bits per card: {}", Integer.toString(bits)); 536 bitsPerCard = bits; 537 } 538 } 539 540 /** 541 * Get CMRInet options. 542 * @param optionbit option index. 543 * @return option value: meaning depends on option 544 */ 545 public int getCMRInetOpts(int optionbit) { return (cmrinetOptions[optionbit]); } 546 public void setCMRInetOpts(int optionbit,int val) { cmrinetOptions[optionbit] = (byte)val; } 547 public boolean isCMRInetBit(int optionbit) { return (cmrinetOptions[optionbit] == 1); } 548 549 /** 550 * Get cpNode options. 551 * @param optionbit option index. 552 * @return option value: meaning depends on option 553 */ 554 public int getcpnodeOpts(int optionbit) { return (cpnodeOptions[optionbit]); } 555 public void setcpnodeOpts(int optionbit,int val) { cpnodeOptions[optionbit] = (byte)val; } 556 public boolean iscpnodeBit(int optionbit) { return (cpnodeOptions[optionbit] == 1); } 557 558 /* 559 * get and set specific option bits. 560 * Network Option Bits 561 */ 562 563 /** 564 * Get if Autopoll bit set. 565 * @return true if set, else false. 566 */ 567 public boolean getOptNet_AUTOPOLL() { 568 var retval = cmrinetOptions[optbitNet_AUTOPOLL] == 1; 569 log.trace("getOptNet_AUTOPOLL() is {}", retval); 570 return (retval); 571 } 572 573 public boolean getOptNet_USECMRIX() { return (cmrinetOptions[optbitNet_USECMRIX] == 1); } 574 public boolean getOptNet_USEBCC() { return (cmrinetOptions[optbitNet_USEBCC] == 1); } 575 public boolean getOptNet_BIT8() { return (cmrinetOptions[optbitNet_BIT8] == 1); } 576 public boolean getOptNet_BIT15() { return (cmrinetOptions[optbitNet_BIT15] == 1); } 577 578 /** 579 * update Autopoll bit 580 * @param val 1 sets autopoll on, 0 sets it off 581 */ 582 public void setOptNet_AUTOPOLL(int val) { 583 log.trace("setOptNet_AUTOPOLL({})", val); 584 cmrinetOptions[optbitNet_AUTOPOLL] = (byte)val; 585 } 586 587 public void setOptNet_USECMRIX(int val) { cmrinetOptions[optbitNet_USECMRIX] = (byte)val; } 588 public void setOptNet_USEBCC(int val) { cmrinetOptions[optbitNet_USEBCC] = (byte)val; } 589 public void setOptNet_BIT8(int val) { cmrinetOptions[optbitNet_BIT8] = (byte)val; } 590 public void setOptNet_BIT15(int val) { cmrinetOptions[optbitNet_BIT15] = (byte)val; } 591 592 public int getOptNet_byte0() {return cmrinetOptions[0];} 593 public int getOptNet_byte1() {return cmrinetOptions[1];} 594 595 /* 596 * Node Option Bits. 597 */ 598 599 /** 600 * Get Node Option SENDEOT. 601 * @return true if SENDEOT, else false. 602 */ 603 public boolean getOptNode_SENDEOT() { return (cpnodeOptions[optbitNode_SENDEOT] == 1); } 604 public boolean getOptNode_USECMRIX() { return (cpnodeOptions[optbitNode_USECMRIX] == 1); } 605 public boolean getOptNode_USEBCC() { return (cpnodeOptions[optbitNode_USEBCC] == 1); } 606 public boolean getOptNode_BIT8() { return (cpnodeOptions[optbitNode_BIT8] == 1); } 607 public boolean getOptNode_BIT15() { return (cpnodeOptions[optbitNode_BIT15] == 1); } 608 609 public void setOptNode_SENDEOT(int val) { cpnodeOptions[optbitNode_SENDEOT] = (byte)val; } 610 public void setOptNode_USECMRIX(int val) { cpnodeOptions[optbitNode_USECMRIX] = (byte)val; } 611 public void setOptNode_USEBCC(int val) { cpnodeOptions[optbitNode_USEBCC] = (byte)val; } 612 public void setOptNode_BIT8(int val) { cpnodeOptions[optbitNode_BIT8] = (byte)val; } 613 public void setOptNode_BIT15(int val) { cpnodeOptions[optbitNode_BIT15] = (byte)val; } 614 615 public int getOptNode_byte0() {return cpnodeOptions[0];} 616 public int getOptNode_byte1() {return cpnodeOptions[1];} 617 618 /** 619 * Get node description. 620 * @return node description. 621 */ 622 public String getcmriNodeDesc() { return cmriNodeDesc; } 623 624 public void setcmriNodeDesc(String nodeDesc) { cmriNodeDesc = nodeDesc; } 625 626 /** 627 * Get cpNode poll list position. 628 * @return poll list position. 629 */ 630 public int getPollListPosition() { return pollListPosition; } 631 632 public void setPollListPosition(int pos) { pollListPosition = pos; } 633 634 /** 635 * Get cpNode polling status. 636 * @return true if polling status flag set, else false. 637 */ 638 public int getPollStatus() { return pollStatus; } 639 640 public void setPollStatus(int status) { pollStatus = status; } 641 642 /** 643 * Check cpNode polling enabled state. 644 * @return true if polling is enabled. 645 */ 646 public boolean getPollingEnabled() { return (cmrinetOptions[optbitNet_AUTOPOLL] == 1); } 647 648 public void setPollingEnabled(boolean isEnabled) 649 { 650 if(isEnabled) 651 cmrinetOptions[optbitNet_AUTOPOLL] = 1; 652 else 653 cmrinetOptions[optbitNet_AUTOPOLL] = 0; 654 } 655 656 /** 657 * Get packet monitoring for the node . 658 * @return true if packet monitoring flag set true, else false. 659 */ 660 public boolean getMonitorNodePackets() { return monitorNodePackets; } 661 662 public void setMonitorNodePackets(boolean onoff) { monitorNodePackets = onoff; } 663 664 /** 665 * Set the specific packet monitoring enable bit. 666 * @param pktTypeBit index. 667 * @param onoff true enables, false disabled. 668 */ 669 public void setMonitorPacketBit(int pktTypeBit, boolean onoff) { 670 monitorPacketBits[pktTypeBit] = onoff; 671 } 672 673 public boolean getMonitorPacketBit(int pktTypeBit) 674 { 675 return monitorPacketBits[pktTypeBit]; 676 } 677 678 /** 679 * Check valid node address, must match value in dip switches (0 - 127). 680 * {@inheritDoc} 681 */ 682 @Override 683 protected boolean checkNodeAddress(int address) { 684 return (address >= 0) && (address < 128); 685 } 686 687 /** 688 * Get transmission delay. 689 * @return delay, ms. 690 */ 691 public int getTransmissionDelay() { 692 return (transmissionDelay); 693 } 694 695 /** 696 * Set transmission delay. 697 * <p> 698 * two bytes are used, so range is 0-65,535. If delay is 699 * out of range, it is restricted to the allowable range. 700 * @param delay - delay between bytes on receive (units of 701 * 10 microsec.) 702 */ 703 public void setTransmissionDelay(int delay) { 704 if ((delay < 0) || (delay > 65535)) { 705 log.warn("transmission delay out of 0-65535 range: {}", Integer.toString(delay)); 706 if (delay < 0) { 707 delay = 0; 708 } 709 if (delay > 65535) { 710 delay = 65535; 711 } 712 } 713 transmissionDelay = delay; 714 } 715 716 /** 717 * Get pulse width. 718 * Used with pulsed turnout control. 719 * @return pulse width, ms. 720 */ 721 public int getPulseWidth() { 722 return (pulseWidth); 723 } 724 725 /** 726 * Set pulse width. 727 * @param width width of pulse used for pulse controlled turnout 728 * control (millisec.) Note: Pulse width must be between 100 and 10000 729 * milliseconds. If width is out of range, it is restricted to the allowable 730 * range 731 */ 732 public void setPulseWidth(int width) { 733 if ((width < 100) || (width > 10000)) { 734 log.warn("pulse width out of 100 - 10000 range: {}", Integer.toString(width)); 735 if (width < 100) { 736 width = 100; 737 } 738 if (width > 10000) { 739 width = 10000; 740 } 741 } 742 pulseWidth = width; 743 } 744 745 /** 746 * Set the type of one card. 747 * 748 * @param address address recognized for this card by 749 * the node hardware. for USIC_SUSIC address set in card's dip switches (0 - 750 * 63) 751 * @param type INPUT_CARD, OUTPUT_CARD, or NO_CARD 752 */ 753 public void setCardTypeByAddress(int address, int type) { 754 // validate address 755 if ((address < 0) || (address > 63)) { 756 log.error("illegal card address: {}", Integer.toString(address)); 757 return; 758 } 759 // validate type 760 if ((type != OUTPUT_CARD) && (type != INPUT_CARD) && (type != NO_CARD)) { 761 log.error("illegal card type: {}", Integer.toString(type)); 762 cardTypeLocation[address] = NO_CARD; 763 return; 764 } 765 // check node type/location restrictions 766 if ((nodeType == SMINI) && (((address > 2) && (type != NO_CARD)) 767 || ((address == 2) && (type != INPUT_CARD)) 768 || ((address < 2) && (type != OUTPUT_CARD)))) { 769 log.error("illegal card type/address specification for SMINI"); 770 return; 771 } 772// here add type/location restrictions for other types of card 773 cardTypeLocation[address] = (byte) type; 774 } 775 776 /** 777 * Test for OUTPUT_CARD type. 778 * 779 * @param cardNum index number. 780 * @return true if card with 'cardNum' is an output card. false if card 781 * is not an output card, or if 'cardNum' is out of range. 782 */ 783 public boolean isOutputCard(int cardNum) { 784 if (cardNum > 63) { 785 warn("isOutputCard - cardNum out of range"); 786 return (false); 787 } 788 if (nodeType == SMINI) { 789 if ((cardNum == 0) || (cardNum == 1)) { 790 return (true); 791 } else { 792 return (false); 793 } 794 } 795 return (cardTypeLocation[cardNum] == OUTPUT_CARD); 796 } 797 798 /** 799 * Test for INPUT_CARD type. 800 * @param cardNum index number. 801 * @return true if card with 'cardNum' is an input card, 802 * false if card is not an input card, or if 'cardNum' is out 803 * of range. 804 */ 805 public boolean isInputCard(int cardNum) { 806 if (cardNum > 63) { 807 warn("isInputCard - cardNum out of range"); 808 return (false); 809 } 810 if (nodeType == SMINI) { 811 if (cardNum == 2) { 812 return (true); 813 } else { 814 return (false); 815 } 816 } 817 return (cardTypeLocation[cardNum] == INPUT_CARD); 818 } 819 820 /** 821 * Get 'Output Card Index'. 822 * <p> 823 * Can be used to locate this card's 824 * bytes in an output message. Array is ordered by increasing node address. 825 * @param cardNum index number. 826 * @return the index this output card would have in 827 * an array of output cards for this node. 828 */ 829 public int getOutputCardIndex(int cardNum) { 830 if (nodeType == SMINI) { 831 if ((cardNum == 0) || (cardNum == 1)) { 832 return (cardNum); 833 } 834 } else { 835 int index = 0; 836 for (int i = 0; i < cardTypeLocation.length; i++) { 837 if (cardTypeLocation[i] == OUTPUT_CARD) { 838 if (i == cardNum) { 839 return (index); 840 } else { 841 index++; 842 } 843 } 844 } 845 } 846 // Here if error - cardNum is not an 847 warn("input card to getOutputCardIndex is not an Output Card"); 848 return (0); 849 } 850 851 /** 852 * Get 'Input Card Index'. 853 * <p> 854 * Can be used to locate this card's bytes in an receive message. 855 * Array is ordered by increasing node address. 856 * @param cardNum index number. 857 * @return the index this input card would have in an 858 * array of input cards for this node. 859 * 860 */ 861 public int getInputCardIndex(int cardNum) { 862 if (nodeType == SMINI) { 863 if (cardNum == 2) { 864 return (0); 865 } 866 } else { 867 int index = 0; 868 for (int i = 0; i < cardTypeLocation.length; i++) { 869 if (cardTypeLocation[i] == INPUT_CARD) { 870 if (i == cardNum) { 871 return (index); 872 } else { 873 index++; 874 } 875 } 876 } 877 } 878 // Here if error - cardNum is not an 879 warn("input card to getOutputCardIndex is not an Output Card"); 880 return (0); 881 } 882 883 /** 884 * Set location of SearchLightBits (SMINI only). 885 * @param bit - bitNumber of the low 886 * bit of an oscillating search light bit pair 887 * <p> 888 * Bits are numbered from 0. 889 * Two bits are set by each call - bit and bit + 1. If either bit is 890 * already set, an error is logged and no bits are set. 891 */ 892 public void set2LeadSearchLight(int bit) { 893 // check for SMINI 894// if other types of CMRI nodes allow oscillating search lights, modify this method 895 if (nodeType != SMINI) { 896 log.error("Invalid setting of Searchlights bits - not SMINI node"); 897 return; 898 } 899 // validate bit number range 900 if ((bit < 0) || (bit > 46)) { 901 log.error("Invalid bit number when setting SMINI Searchlights bits: {}", Integer.toString(bit)); 902 return; 903 } 904 // validate that bits are not already set 905 if ((locSearchLightBits[bit] != 0) || (locSearchLightBits[bit + 1] != 0)) { 906 log.error("bit number for SMINI Searchlights bits already set: {}", Integer.toString(bit)); 907 return; 908 } 909 // set the bits 910 locSearchLightBits[bit] = 1; 911 locSearchLightBits[bit + 1] = 1; 912 num2LSearchLights++; 913 } 914 915 /** 916 * Clear location of SearchLightBits (SMINI only). 917 * @param bit - bitNumber of the low 918 * bit of an oscillating search light bit pair 919 * <p> 920 * Notes: Bits are numbered from 921 * 0 Two bits are cleared by each call - bit and bit + 1. If either bit is 922 * already clear, an error is logged and no bits are set. 923 */ 924 public void clear2LeadSearchLight(int bit) { 925 // check for SMINI 926// if other types of CMRI nodes allow oscillating search lights, modify this method 927 if (nodeType != SMINI) { 928 log.error("Invalid setting of Searchlights bits - not SMINI node"); 929 return; 930 } 931 // validate bit number range 932 if ((bit < 0) || (bit > 46)) { 933 log.error("Invalid bit number when setting SMINI Searchlights bits: {}", Integer.toString(bit)); 934 return; 935 } 936 // validate that bits are not already clear 937 if ((locSearchLightBits[bit] != 1) || (locSearchLightBits[bit + 1] != 1)) { 938 log.error("bit number for SMINI Searchlights bits already clear: {}", Integer.toString(bit)); 939 return; 940 } 941 // set the bits 942 locSearchLightBits[bit] = 0; 943 locSearchLightBits[bit + 1] = 0; 944 num2LSearchLights--; 945 } 946 947 /** 948 * Query SearchLightBits by bit number (SMINI only). 949 * @param bit bitNumber of the either bit of an oscillating search light bit pair. 950 * @return true if bit is an oscillating SearchLightBit, otherwise false. 951 */ 952 public boolean isSearchLightBit(int bit) { 953 // check for SMINI 954// if other types of CMRI nodes allow oscillating search lights, modify this method 955 if (nodeType != SMINI) { 956 log.error("Invalid query of Searchlights bits - not SMINI node"); 957 return (false); 958 } 959 // validate bit number range 960 if ((bit < 0) || (bit > 47)) { 961 log.error("Invalid bit number in query of SMINI Searchlights bits: {}", Integer.toString(bit)); 962 return (false); 963 } 964 if (locSearchLightBits[bit] == 1) { 965 return (true); 966 } 967 return (false); 968 } 969 970 /** 971 * Create an Initialization packet (SerialMessage) for this node 972 */ 973 @Override 974 public AbstractMRMessage createInitPacket() { 975 // Assemble initialization byte array from node information 976 int nInitBytes = 4; 977 byte[] initBytes = new byte[20]; 978 int code = 0; 979 // set node definition parameter 980/* 981 if (nodeType == SMINI) { 982 initBytes[0] = 77; // 'M' 983 } else if (nodeType == USIC_SUSIC) { 984 if (bitsPerCard == 24) { 985 initBytes[0] = 78; // 'N' 986 } else if (bitsPerCard == 32) { 987 initBytes[0] = 88; // 'X' 988 } 989 } 990*/ 991 switch(nodeType) //c2 992 { 993 case SMINI: initBytes[0] = NDP_SMINI; // 'M' 994 break; 995 996 case USIC_SUSIC: if (bitsPerCard==24) initBytes[0] = NDP_USICSUSIC24; // 'N' 997 else 998 if (bitsPerCard==32) initBytes[0] = NDP_USICSUSIC32; // 'X' 999 break; 1000 case CPNODE: initBytes[0] = NDP_CPNODE; // 'C' c2 1001 break; 1002 case CPMEGA: initBytes[0] = NDP_CPMEGA; // 'O' c2 1003 break; 1004 case ESP32NODE: initBytes[0] = NDP_ESP32NODE; // 'E' 1005 break; 1006 1007 default: 1008 } 1009 1010// Here add code for other type of card 1011 // add Transmission Delay bytes (same for SMINI and USIC/SUSIC) 1012 int firstByte = transmissionDelay / 256; 1013 int secondByte = transmissionDelay - (firstByte * 256); 1014 if (firstByte > 255) { 1015 firstByte = 255; 1016 } 1017 initBytes[1] = (byte) firstByte; 1018 initBytes[2] = (byte) secondByte; 1019/* 1020 // SMINI specific part of initialization byte array 1021 if (nodeType == SMINI) { 1022 initBytes[3] = (byte) num2LSearchLights; 1023 if (num2LSearchLights > 0) { 1024 // Set up searchlight LED bit codes 1025 for (int i = 0, j = 0; i < 6; i++, j += 8) { 1026 code = locSearchLightBits[j]; 1027 code = code + (locSearchLightBits[j + 1] * 2); 1028 code = code + (locSearchLightBits[j + 2] * 4); 1029 code = code + (locSearchLightBits[j + 3] * 8); 1030 code = code + (locSearchLightBits[j + 4] * 16); 1031 code = code + (locSearchLightBits[j + 5] * 32); 1032 code = code + (locSearchLightBits[j + 6] * 64); 1033 code = code + (locSearchLightBits[j + 7] * 128); 1034 initBytes[nInitBytes] = (byte) code; 1035 nInitBytes++; 1036 } 1037 } 1038 } // USIC/SUSIC specific part of initialization byte array 1039 else if (nodeType == USIC_SUSIC) { 1040 int numCards = numInputCards() + numOutputCards(); 1041 int numFours = numCards / 4; 1042 if ((numCards - (numFours * 4)) > 0) { 1043 numFours++; // Round up if not even multiple 1044 } 1045 initBytes[3] = (byte) numFours; 1046 for (int i = 0, j = 0; i < numFours; i++, j += 4) { 1047 code = cardTypeLocation[j]; 1048 code = code + (cardTypeLocation[j + 1] * 4); 1049 code = code + (cardTypeLocation[j + 2] * 16); 1050 code = code + (cardTypeLocation[j + 3] * 64); 1051 initBytes[nInitBytes] = (byte) code; 1052 nInitBytes++; 1053 } 1054 } 1055*/ 1056 // SMINI specific part of initialization byte array 1057 switch (nodeType) //c2 1058 { 1059 case SMINI: 1060 initBytes[3] = (byte)num2LSearchLights; 1061 if (num2LSearchLights>0) 1062 { 1063 // Set up searchlight LED bit codes 1064 for (int i=0,j=0;i<6;i++,j+=8) 1065 { 1066 code = locSearchLightBits[j]; 1067 code = code + (locSearchLightBits[j+1]*2); 1068 code = code + (locSearchLightBits[j+2]*4); 1069 code = code + (locSearchLightBits[j+3]*8); 1070 code = code + (locSearchLightBits[j+4]*16); 1071 code = code + (locSearchLightBits[j+5]*32); 1072 code = code + (locSearchLightBits[j+6]*64); 1073 code = code + (locSearchLightBits[j+7]*128); 1074 initBytes[nInitBytes] = (byte)code; 1075 nInitBytes ++; 1076 } 1077 } 1078 break; 1079 1080 // USIC/SUSIC specific part of initialization byte array 1081 case USIC_SUSIC: 1082 int numCards = numInputCards() + numOutputCards(); 1083 int numFours = numCards/4; 1084 if ( (numCards-(numFours*4)) > 0) numFours ++; // Round up if not even multiple 1085 initBytes[3] = (byte)numFours; 1086 for (int i=0,j=0;i<numFours;i++,j+=4) 1087 { 1088 code = cardTypeLocation[j]; 1089 code = code + (cardTypeLocation[j+1] * 4); 1090 code = code + (cardTypeLocation[j+2] * 16); 1091 code = code + (cardTypeLocation[j+3] * 64); 1092 initBytes[nInitBytes] = (byte)code; 1093 nInitBytes ++; 1094 } 1095 break; 1096 1097 /* CPNODE specific part of initialization byte array 1098 * The I message has the node configuration options following the 1099 * DL bytes, followed by the defined number of I/O cards. 1100 * 0 1 2 3 4 5 6 7 - 12 1101 * <NDP><dH><dL><cpOPTS1><cpOPTS2><cpNI><cpNO> <rfe 8> 1102 */ 1103 case CPNODE: 1104 nInitBytes = 3; 1105 // ------------------------- 1106 // Pack the two option bytes 1107 // ------------------------- 1108 for (int i=0,j=0;i<2;i++,j+=8) 1109 { 1110 code = cpnodeOptions[j]; 1111 code = code + (cpnodeOptions[j+1]*2); 1112 code = code + (cpnodeOptions[j+2]*4); 1113 code = code + (cpnodeOptions[j+3]*8); 1114 code = code + (cpnodeOptions[j+4]*16); 1115 code = code + (cpnodeOptions[j+5]*32); 1116 code = code + (cpnodeOptions[j+6]*64); 1117 code = code + (cpnodeOptions[j+7]*128); 1118 initBytes[nInitBytes] = (byte)code; 1119 nInitBytes++; 1120 } 1121 // ------------------------------------- 1122 // Configured input and output byte count 1123 // ------------------------------------- 1124 initBytes[nInitBytes++] = (byte)numInputCards(); 1125 initBytes[nInitBytes++] = (byte)numOutputCards(); 1126 1127 // -------------------------- 1128 // future to be defined bytes 1129 // -------------------------- 1130 for (int i=nInitBytes; i<INITMSGLEN+1; i++) 1131 { 1132 initBytes[i] = (byte)0xFF; 1133 nInitBytes++; 1134 } 1135 1136 break; 1137 1138 /* CPMEGA specific part of initialization byte array 1139 * The I message has the node configuration options following the 1140 * DL bytes, followed by the defined number of I/O cards. 1141 * 0 1 2 3 4 5 6 7 - 12 1142 * <NDP><dH><dL><cpOPTS1><cpOPTS2><cpNI><cpNO> <rfe 8> 1143 */ 1144 case CPMEGA: 1145 nInitBytes = 3; 1146 // ------------------------- 1147 // Pack the two option bytes 1148 // ------------------------- 1149 for (int i=0,j=0;i<2;i++,j+=8) 1150 { 1151 code = cpnodeOptions[j]; 1152 code = code + (cpnodeOptions[j+1]*2); 1153 code = code + (cpnodeOptions[j+2]*4); 1154 code = code + (cpnodeOptions[j+3]*8); 1155 code = code + (cpnodeOptions[j+4]*16); 1156 code = code + (cpnodeOptions[j+5]*32); 1157 code = code + (cpnodeOptions[j+6]*64); 1158 code = code + (cpnodeOptions[j+7]*128); 1159 initBytes[nInitBytes] = (byte)code; 1160 nInitBytes++; 1161 } 1162 // ------------------------------------- 1163 // Configured input and output byte count 1164 // ------------------------------------- 1165 initBytes[nInitBytes++] = (byte)numInputCards(); 1166 initBytes[nInitBytes++] = (byte)numOutputCards(); 1167 1168 // -------------------------- 1169 // future to be defined bytes 1170 // -------------------------- 1171 for (int i=nInitBytes; i<INITMSGLEN+1; i++) 1172 { 1173 initBytes[i] = (byte)0xFF; 1174 nInitBytes++; 1175 } 1176 1177 break; 1178 1179 /* ESP32Node specific part of initialization byte array -- same layout as 1180 * CPNODE/CPMEGA (options bytes + I/O counts); ESP32Node just has no 1181 * onboard-reserved cards, so numInputCards()/numOutputCards() report 1182 * purely user-configured MCP23017 ports. 1183 * 0 1 2 3 4 5 6 7 - 12 1184 * <NDP><dH><dL><cpOPTS1><cpOPTS2><cpNI><cpNO> <rfe 8> 1185 */ 1186 case ESP32NODE: 1187 nInitBytes = 3; 1188 // ------------------------- 1189 // Pack the two option bytes 1190 // ------------------------- 1191 for (int i=0,j=0;i<2;i++,j+=8) 1192 { 1193 code = cpnodeOptions[j]; 1194 code = code + (cpnodeOptions[j+1]*2); 1195 code = code + (cpnodeOptions[j+2]*4); 1196 code = code + (cpnodeOptions[j+3]*8); 1197 code = code + (cpnodeOptions[j+4]*16); 1198 code = code + (cpnodeOptions[j+5]*32); 1199 code = code + (cpnodeOptions[j+6]*64); 1200 code = code + (cpnodeOptions[j+7]*128); 1201 initBytes[nInitBytes] = (byte)code; 1202 nInitBytes++; 1203 } 1204 // ------------------------------------- 1205 // Configured input and output byte count 1206 // ------------------------------------- 1207 initBytes[nInitBytes++] = (byte)numInputCards(); 1208 initBytes[nInitBytes++] = (byte)numOutputCards(); 1209 1210 // -------------------------- 1211 // future to be defined bytes 1212 // -------------------------- 1213 for (int i=nInitBytes; i<INITMSGLEN+1; i++) 1214 { 1215 initBytes[i] = (byte)0xFF; 1216 nInitBytes++; 1217 } 1218 1219 break; 1220 1221 default: 1222 log.error("Invalid node type ({}) in SerialNode Init Message", nodeType); 1223 1224 } 1225 1226// here add specific initialization for other type of card 1227 1228 // count the number of DLE's to be inserted 1229 int nDLE = 0; 1230 for (int i = 1; i < nInitBytes; i++) { 1231 if ((initBytes[i] == 2) || (initBytes[i] == 3) || (initBytes[i] == 16)) { 1232 nDLE++; 1233 } 1234 } 1235 1236 // create a Serial message and add initialization bytes 1237 SerialMessage m = new SerialMessage(nInitBytes + nDLE + 2); 1238 m.setElement(0, getNodeAddress() + 65); // node address 1239 m.setElement(1, 73); // 'I' 1240 // add initialization bytes 1241 int k = 2; 1242 for (int i = 0; i < nInitBytes; i++) { 1243 // perform C/MRI required DLE processing 1244 if ((initBytes[i] == 2) || (initBytes[i] == 3) || (initBytes[i] == 16)) { 1245 m.setElement(k, 16); // DLE 1246 k++; 1247 } 1248 // add initialization byte 1249 m.setElement(k, initBytes[i]); 1250 k++; 1251 } 1252 return m; 1253 } 1254 1255 /** 1256 * Create an Transmit packet (SerialMessage) 1257 */ 1258 @Override 1259 public AbstractMRMessage createOutPacket() { 1260 // Count the number of DLE's to be inserted 1261 int nOutBytes = numOutputCards() * (bitsPerCard / 8); 1262 int nDLE = 0; 1263 byte[] oA; // current values of the output bits for this node 1264 1265 oA = outputArray.clone(); 1266 1267 for (int i = 0; i < nOutBytes; i++) { 1268 if ((oA[i] == 2) || (oA[i] == 3) || (oA[i] == 16)) { 1269 nDLE++; 1270 } 1271 } 1272 // Create a Serial message and add initial bytes 1273 SerialMessage m = new SerialMessage(nOutBytes + nDLE + 2); 1274 m.setElement(0, getNodeAddress() + 65); // node address 1275 m.setElement(1, 84); // 'T' 1276 // Add output bytes 1277 int k = 2; 1278 for (int i = 0; i < nOutBytes; i++) { 1279 // perform C/MRI required DLE processing 1280 if ((oA[i] == 2) || (oA[i] == 3) || (oA[i] == 16)) { 1281 m.setElement(k, 16); // DLE 1282 k++; 1283 } 1284 // add output byte 1285 m.setElement(k, oA[i]); 1286 k++; 1287 } 1288 return m; 1289 } 1290 boolean warned = false; 1291 1292 void warn(String s) { 1293 if (warned) { 1294 return; 1295 } 1296 warned = true; 1297 log.warn("C/MRI - {}", s); 1298 } 1299 1300 /** 1301 * Use the contents of the poll reply to mark changes 1302 * 1303 * @param l Reply to a poll operation 1304 */ 1305 public void markChanges(SerialReply l) { 1306 try { 1307 for (int i = 0; i <= lastUsedSensor; i++) { 1308 if (sensorArray[i] == null) { 1309 continue; // skip ones that don't exist 1310 } 1311 int loc = i / 8; 1312 if (loc + 2 >= l.getNumDataElements()) { 1313 continue; // skip this one as not in data, so not changed 1314 } 1315 int bit = i % 8; 1316 boolean value = (((l.getElement(loc + 2) >> bit) & 0x01) == 1) ^ sensorArray[i].getInverted(); // byte 2 is first of data 1317 1318 // if (log.isDebugEnabled()) log.debug("markChanges loc="+loc+" bit="+bit+" is "+value+ 1319 // " tempSetting is "+((sensorTempSetting[i] == Sensor.ACTIVE)?"active ":"inactive ")+ 1320 // "lastSetting is "+((sensorLastSetting[i] == Sensor.ACTIVE)?"active ":"inactive ") 1321 // ); 1322 if (value) { 1323 // considered ACTIVE 1324 if (((sensorTempSetting[i] == Sensor.ACTIVE) 1325 || (sensorTempSetting[i] == Sensor.UNKNOWN)) 1326 && (sensorLastSetting[i] != Sensor.ACTIVE)) { // see comment at top; allows persistent local changes 1327 sensorLastSetting[i] = Sensor.ACTIVE; 1328 sensorArray[i].setKnownState(Sensor.ACTIVE); 1329 // log.debug("set active"); 1330 } 1331 // save for next time 1332 sensorTempSetting[i] = Sensor.ACTIVE; 1333 ((SerialSensor)sensorArray[i]).lastStateFromLayout = Sensor.ACTIVE; 1334 } else { 1335 // considered INACTIVE 1336 if (((sensorTempSetting[i] == Sensor.INACTIVE) 1337 || (sensorTempSetting[i] == Sensor.UNKNOWN)) 1338 && (sensorLastSetting[i] != Sensor.INACTIVE)) { // see comment at top; allows persistent local changes 1339 sensorLastSetting[i] = Sensor.INACTIVE; 1340 sensorArray[i].setKnownState(Sensor.INACTIVE); 1341 // log.debug("set inactive"); 1342 } 1343 // save for next time 1344 sensorTempSetting[i] = Sensor.INACTIVE; 1345 ((SerialSensor)sensorArray[i]).lastStateFromLayout = Sensor.INACTIVE; 1346 } 1347 } 1348 } catch (JmriException e) { 1349 log.error("exception in markChanges", e); 1350 } 1351 } 1352 1353 /** 1354 * The numbers here are 0 to MAXSENSORS, not 1 to MAXSENSORS. 1355 * 1356 * @param s Sensor object 1357 * @param i 0 to MAXSENSORS number of sensor's input bit on this node 1358 */ 1359 public void registerSensor(Sensor s, int i) { 1360 // validate the sensor ordinal 1361 if ((i < 0) || (i > ((numInputCards() * bitsPerCard) - 1)) || (i > MAXSENSORS)) { 1362 log.error("Unexpected sensor ordinal in registerSensor: {}", Integer.toString(i + 1)); 1363 return; 1364 } 1365 hasActiveSensors = true; 1366 if (sensorArray[i] == null) { 1367 sensorArray[i] = s; 1368 if (lastUsedSensor < i) { 1369 lastUsedSensor = i; 1370 } 1371 } else { 1372 // multiple registration of the same sensor 1373 log.warn("multiple registration of same sensor: CS{}", Integer.toString((getNodeAddress() * SerialSensorManager.SENSORSPERUA) + i + 1)); // TODO multichar prefix 1374 } 1375 } 1376 1377 int timeout = 0; 1378 1379 /** 1380 * @return true if polling active and currently OK 1381 */ 1382 public boolean isPollingOK() { 1383 return timeout == 0; 1384 } 1385 1386 /** 1387 * 1388 * @return true if initialization required 1389 */ 1390 @Override 1391 public boolean handleTimeout(AbstractMRMessage m, AbstractMRListener l) { 1392 timeout++; 1393 // normal to timeout in response to init, output 1394 if (m.getElement(1) != 0x50) { 1395 return false; 1396 } 1397 1398 // see how many polls missed 1399 if (log.isDebugEnabled()) { 1400 log.warn("Timeout to poll for UA={}: consecutive timeouts: {}", getNodeAddress(), timeout); 1401 } 1402 1403 if (timeout > 5) { // enough, reinit 1404 // reset timeout count to one to give polls another try 1405 // but not zero because that means operating OK 1406 timeout = 1; 1407 // reset poll and send control so will retry initialization 1408 setMustSend(); 1409 1410 // force sensors to UNKNOWN, including callbacks; might take some time 1411 for (int i = 0; i <= lastUsedSensor; i++) { 1412 if (sensorArray[i] != null) { 1413 sensorLastSetting[i] = Sensor.UNKNOWN; 1414 sensorTempSetting[i] = Sensor.UNKNOWN; 1415 try { 1416 sensorArray[i].setKnownState(Sensor.UNKNOWN); 1417 } catch (jmri.JmriException e) { 1418 log.error("unexpected exception setting sensor i={} on node {}", i, getNodeAddress(), e); 1419 } 1420 } 1421 } 1422 return true; // tells caller to force init 1423 } else { 1424 return false; 1425 } 1426 } 1427 1428 @Override 1429 public void resetTimeout(AbstractMRMessage m) { 1430 if (timeout > 0) { 1431 log.debug("Reset {} timeout count", timeout); 1432 } 1433 timeout = 0; 1434 } 1435 1436 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SerialNode.class); 1437}