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