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