001package jmri.jmrix.loconet.ds64;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004
005import java.awt.Color;
006import java.awt.Component;
007import java.awt.Container;
008import java.awt.FlowLayout;
009import java.awt.Window;
010import java.awt.event.ActionEvent;
011import java.awt.event.ActionListener;
012import java.awt.event.ItemEvent;
013import java.util.ArrayList;
014import java.util.Collections;
015
016import javax.swing.BoxLayout;
017import javax.swing.ButtonGroup;
018import javax.swing.JCheckBox;
019import javax.swing.JComboBox;
020import javax.swing.JComponent;
021import javax.swing.JLabel;
022import javax.swing.JPanel;
023import javax.swing.JRadioButton;
024import javax.swing.JScrollPane;
025import javax.swing.JSeparator;
026import javax.swing.JTabbedPane;
027import javax.swing.JToggleButton;
028import javax.swing.event.ChangeEvent;
029import javax.swing.event.ChangeListener;
030
031import jmri.jmrix.loconet.AbstractBoardProgPanel;
032import jmri.jmrix.loconet.LnConstants;
033import jmri.jmrix.loconet.LocoNetMessage;
034import jmri.jmrix.loconet.LocoNetSystemConnectionMemo;
035import jmri.util.swing.JmriJOptionPane;
036import jmri.util.swing.ValidatedTextField;
037
038/**
039 * A "tabbed" swing panel to display and modify Digitrax DS64 board
040 * configuration.
041 * <p>
042 * The read and write operations require a sequence of operations, which are
043 * handled with a state variable.
044 * <p>
045 * Programming of the DS64 is done via LocoNet configuration messages, so the
046 * DS64 should not be manually put into its programming mode via the DS64
047 * built-in pushbutton while this tool is in use.
048 * <p>
049 * Some of the message formats used in this class are Copyright Digitrax, Inc.
050 * and used with permission as part of the JMRI project. That permission does
051 * not extend to uses in other software products. If you wish to use this code,
052 * algorithm or these message formats outside of JMRI, please contact Digitrax
053 * Inc for separate permission.
054 * <p>
055 * Extensions to include read/write of turnout output addresses and routes are
056 * based on reverse-engineering of DS64 operating characteristics by B.
057 * Milhaupt. As such, this tool may not be compatible with all DS64 devices.
058 * <hr>
059 * This file is part of JMRI.
060 * <p>
061 * JMRI is free software; you can redistribute it and/or modify it under the
062 * terms of version 2 of the GNU General Public License as published by the Free
063 * Software Foundation. See the "COPYING" file for a copy of this license.
064 * <p>
065 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
066 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
067 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
068 * <p>
069 * Based on Revision 1.1 of DS64Panel.java by Bob Jacobsen
070 *
071 * @author Bob Jacobsen Copyright (C) 2002, 2004, 2005, 2007, 2010
072 * @author B. Milhaupt Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017
073 */
074public class Ds64TabbedPanel extends AbstractBoardProgPanel {
075
076    /**
077     * Ds64TabbedPanel constructor when the boardNum is already known. Allows
078     * the instantiating method to specify whether the basic feature
079     * configuration information will be read upon instantiation.
080     *
081     * @param boardNum   initial BoardID number
082     * @param readOnInit true to automatically read the basic configuration
083     *                   info.
084     */
085    public Ds64TabbedPanel(int boardNum, boolean readOnInit) {
086        super(boardNum, readOnInit, "DS64"); // NOI18N
087        origAccessBoardNum = boardNum;
088        boardNumsEntryValue.add(boardNum);
089    }
090
091    /**
092     * Ds64TabbedPanel constructor when the boardNum is not already known.
093     * <p>
094     * At instantiation, the object will automatically assume BoardID 1 and will
095     * not pre-read the basic board configuration information.
096     */
097    public Ds64TabbedPanel() {
098        // this is a constructor which is in-place to support legacy applications.
099        this(1, false);
100    }
101
102    /**
103     * Ds64TabbedPanel constructor when the boardNum is already known.
104     * <p>
105     * When instantiated, the object will not automatically read the basic
106     * configuration information.
107     *
108     * @param boardNum initial BoardID number
109     */
110    public Ds64TabbedPanel(int boardNum) {
111        this(boardNum, false);
112        origAccessBoardNum = boardNum;
113    }
114    int[] boardNumbers;
115    int origAccessBoardNum = 0;
116    ArrayList<Integer> boardNumsEntryValue = new ArrayList<>();
117
118    /**
119     * Ds64TabbedPanel constructor which may be used when the instantiating
120     * method already has an array of DS64 BoardID numbers; this array is used
121     * to pre-populate the GUI combobox showing BoardID numbers. The first
122     * BoardID number in the array will automatically be selected upon
123     * instantiation.
124     * <p>
125     * When instantiated, the object will automatically read the basic
126     * configuration information if readOnInit is true.
127     *
128     * @param readOnInit true to automatically read the basic configuration info
129     *                   from the DS64 with BoardID equal to the first value in
130     *                   the boardNums array
131     * @param boardNums  Array of known DS64 BoardID numbers
132     */
133    public Ds64TabbedPanel(boolean readOnInit, Integer[] boardNums) {
134        this(boardNums[0], readOnInit);
135        log.debug("into DS64 tabbed panel with list of boards of length {}", boardNums.length); // NOI18N
136        log.debug("boardNums[0] = {}", boardNums[0]); // NOI18N
137        origAccessBoardNum = boardNums[0];
138        boardNumsEntryValue.remove(0);  // remove the entry  added by Ds64TabbedPanel(int boardNum, boolean readOnInit)
139        for (int boardNum : boardNums) {
140            log.debug("board {}", boardNum); // NOI18N
141            boardNumsEntryValue.add(boardNum);
142        }
143        Collections.sort(boardNumsEntryValue);
144    }
145
146    /**
147     * Ds64TabbedPanel constructor when the boardNum is not known; BoardID 1 is
148     * assumed.
149     * <p>
150     * Allows the instantiating method to specify whether the basic feature
151     * configuration information will be read upon instantiation.
152     *
153     * @param readBoardOnInit true to automatically read the basic configuration
154     *                        info.
155     */
156    public Ds64TabbedPanel(boolean readBoardOnInit) {
157        this(1, readBoardOnInit);
158    }
159
160    JPanel generalPanel = null;
161    JPanel opswsPanel = null;
162    JScrollPane opswsScrollPane = null;
163    JTabbedPane generalTabbedPane = null;
164    JTabbedPane routesTabbedPane;
165    JPanel opswsValues = null;
166    JPanel outputAddrsPanel = null;
167    ValidatedTextField outAddr1 = null;
168    JLabel outState1 = null;
169    ValidatedTextField outAddr2 = null;
170    JLabel outState2 = null;
171    ValidatedTextField outAddr3 = null;
172    JLabel outState3 = null;
173    ValidatedTextField outAddr4 = null;
174    JLabel outState4 = null;
175    JPanel[] routePanel;
176    SimpleTurnoutStateEntry[] routeTop;
177    SimpleTurnoutStateEntry[] routeA2;
178    SimpleTurnoutStateEntry[] routeA3;
179    SimpleTurnoutStateEntry[] routeA4;
180    SimpleTurnoutStateEntry[] routeA5;
181    SimpleTurnoutStateEntry[] routeA6;
182    SimpleTurnoutStateEntry[] routeA7;
183    SimpleTurnoutStateEntry[] routeA8;
184    JToggleButton resetRouteButton = null;
185
186    JComboBox<Integer> addressComboBox;
187
188    // output controls
189    JLabel outputTypeLabel;
190    JComboBox<String> outputType;
191
192    JLabel delayTimeLabel;
193    JComboBox<String> delayTime;
194
195    JLabel outputStatesLabel;
196    JComboBox<String> outputStates;
197
198    JLabel startupDelayLabel;
199    JComboBox<String> startupDelay;
200
201    JLabel staticOutputShutoffLabel;
202    JComboBox<String> staticOutputShutoff;
203
204    // command sources
205    JLabel commandTypeLabel;
206    JComboBox<String> commandType;
207
208    JLabel commandSourceLabel;
209    JComboBox<String> commandSource;
210
211    // Crossbuck Flasher controls
212    JCheckBox output1CrossbuckFlasherCheckBox;
213    JCheckBox output2CrossbuckFlasherCheckBox;
214    JCheckBox output3CrossbuckFlasherCheckBox;
215    JCheckBox output4CrossbuckFlasherCheckBox;
216
217    // DS64 routes
218    JLabel routesControlLabel;
219    JComboBox<String> routesControl;
220
221    // local input controls
222    JLabel localControlOfOutputsStyleLabel;
223    JComboBox<String> localControlOfOutputsStyle;
224
225    JLabel sensorMessageTriggerLabel;
226    JComboBox<String> sensorMessageTrigger;
227
228    JComboBox<String> localSensorType;
229
230    JToggleButton factoryResetButton;
231
232    JRadioButtonWithInteger[] opswThrown = new JRadioButtonWithInteger[21];
233    JRadioButtonWithInteger[] opswClosed = new JRadioButtonWithInteger[21];
234
235    JPanel sensorMessageTriggerPanel;
236    JPanel localInputControlsPanel;
237
238    @Override
239    public String getHelpTarget() {
240        return "package.jmri.jmrix.loconet.ds64.DS64TabbedPanel"; // NOI18N
241    }
242
243    @Override
244    public String getTitle() {
245        return getTitle(Bundle.getMessage("MenuItemDS64Programmer"));
246    }
247
248    public javax.swing.Timer boardResetResponseTimer = null;
249
250    public void updateGuiBasicOpSw(int index) {
251        if ((index < 1) || (index == 7) || (index > 21)) {
252            return;
253        }
254        if (opsw[index]) {
255            opswThrown[index].setSelected(false);
256            opswClosed[index].setSelected(true);
257        } else {
258            opswThrown[index].setSelected(true);
259            opswClosed[index].setSelected(false);
260        }
261        opswThrown[index].updateUI();
262        opswClosed[index].updateUI();
263    }
264
265    /**
266     * Copy from the GUI to the opsw array.
267     * <p>
268     * Used before write operations start.
269     */
270    @Override
271    protected void copyToOpsw() {
272        // copy over the display
273        opsw[1] = (outputType.getSelectedIndex() == 1);
274        updateGuiBasicOpSw(1);
275        int selection = delayTime.getSelectedIndex();
276        opsw[2] = ((selection & 0x1) == 1);
277        opsw[3] = ((selection & 0x2) == 2);
278        opsw[4] = ((selection & 0x4) == 4);
279        opsw[5] = ((selection & 0x8) == 8);
280        updateGuiBasicOpSw(2);
281        updateGuiBasicOpSw(3);
282        updateGuiBasicOpSw(4);
283        updateGuiBasicOpSw(5);
284        opsw[6] = (outputStates.getSelectedIndex() == 1);
285        updateGuiBasicOpSw(6);
286        opsw[7] = (isWritingResetOpSw ? resetOpSwVal : false);
287        updateGuiBasicOpSw(7);
288        opsw[8] = startupDelay.getSelectedIndex() == 1;
289        updateGuiBasicOpSw(8);
290        opsw[9] = staticOutputShutoff.getSelectedIndex() == 1;
291        updateGuiBasicOpSw(9);
292        opsw[10] = commandType.getSelectedIndex() == 1;
293        updateGuiBasicOpSw(10);
294        opsw[11] = (routesControl.getSelectedIndex() == 1) || (routesControl.getSelectedIndex() == 3);
295        updateGuiBasicOpSw(11);
296        opsw[12] = (localControlOfOutputsStyle.getSelectedIndex() & 1) == 1;  //2 -> OpSw12="c"
297        updateGuiBasicOpSw(12);
298        opsw[13] = (sensorMessageTrigger.getSelectedIndex() == 1);
299        updateGuiBasicOpSw(13);
300        opsw[14] = commandSource.getSelectedIndex() == 1;
301        updateGuiBasicOpSw(14);
302        opsw[15] = (localControlOfOutputsStyle.getSelectedIndex() >= 2);  //0 -> OpSw15="c"
303        updateGuiBasicOpSw(15);
304        opsw[16] = routesControl.getSelectedIndex() >= 2;
305        updateGuiBasicOpSw(16);
306        opsw[17] = output1CrossbuckFlasherCheckBox.isSelected();
307        updateGuiBasicOpSw(17);
308        opsw[18] = output2CrossbuckFlasherCheckBox.isSelected();
309        updateGuiBasicOpSw(18);
310        opsw[19] = output3CrossbuckFlasherCheckBox.isSelected();
311        updateGuiBasicOpSw(19);
312        opsw[20] = output4CrossbuckFlasherCheckBox.isSelected();
313        updateGuiBasicOpSw(20);
314        opsw[21] = localSensorType.getSelectedIndex() == 1;
315        updateGuiBasicOpSw(21);
316    }
317    java.awt.Component colorizedObject;
318
319    @Override
320    protected void updateDisplay() {
321        switch (state) {
322            case 1:
323                outputType.setSelectedIndex((opsw[1] == true) ? 1 : 0);
324                break;
325            case 2:
326            case 3:
327            case 4:
328            case 5:
329                delayTime.setSelectedIndex(
330                        ((opsw[2] == true) ? 1 : 0)
331                        + ((opsw[3] == true) ? 2 : 0)
332                        + ((opsw[4] == true) ? 4 : 0)
333                        + ((opsw[5] == true) ? 8 : 0));
334                break;
335            case 6:
336                outputStates.setSelectedIndex((opsw[6] == true) ? 1 : 0);
337                break;
338            case 8:
339                startupDelay.setSelectedIndex((opsw[8] == true) ? 1 : 0);
340                break;
341            case 9:
342                staticOutputShutoff.setSelectedIndex((opsw[9] == true) ? 1 : 0);
343                break;
344            case 10:
345                commandType.setSelectedIndex((opsw[10] == true) ? 1 : 0);
346                break;
347            case 11:
348            case 16:
349                routesControl.setSelectedIndex((((opsw[16] == true) ? 2 : 0) + (opsw[11] ? 1 : 0)));
350                break;
351            case 15:
352            case 12:
353                localControlOfOutputsStyle.setSelectedIndex(((opsw[15] == true) ? 2 : 0) + (opsw[12] ? 1 : 0));
354                break;
355            case 13:
356                sensorMessageTrigger.setSelectedIndex((opsw[13] == true) ? 1 : 0);   // selection 0 - only for A inputs; 1 - both A and S inputs
357                break;
358            case 14:
359                commandSource.setSelectedIndex((opsw[14] == true) ? 1 : 0);
360                break;
361            case 17:
362                output1CrossbuckFlasherCheckBox.setSelected(opsw[17]);
363                break;
364            case 18:
365                output2CrossbuckFlasherCheckBox.setSelected(opsw[18]);
366                break;
367            case 19:
368                output3CrossbuckFlasherCheckBox.setSelected(opsw[19]);
369                break;
370            case 20:
371                output4CrossbuckFlasherCheckBox.setSelected(opsw[20]);
372
373                break;
374            case 21:
375                localSensorType.setSelectedIndex(opsw[21] ? 1 : 0);
376                break;
377            default:
378                // we are only interested in the states above. Ignore the rest
379                log.debug("Unhandled state code: {}", state);
380                break;
381        }
382        updateUI();
383    }
384
385    @Override
386    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "Cannot catch an exception without grabbing the exception, but we don't do anything with the exception details.")
387    protected int nextState(int state) {
388        if (isWritingResetOpSw) {
389            if ((state == 7) && (opsw[7] == true)) {
390                opsw[7] = false;
391                return 7;
392            } else if (state == 7) {
393                return 0;
394            }
395        }
396
397        if (onlyOneOperation == true) {
398            onlyOneOperation = false;
399            return 0;
400        }
401        if ((state > 1)
402                && (((isRead == true) && (readAllButton.isSelected() == false))
403                || ((isRead == false)
404                && ((writeAllButton.isSelected() == false)
405                && (resetRouteButton.isSelected() == false))))) {
406            // handle case where a button is de-selected by the user during the operation
407
408            Color noAccessColor = ValidatedTextField.COLOR_BG_UNEDITED;
409            if ((operationType == OpSwOpType.BasicsRead)
410                    || (operationType == OpSwOpType.BasicsWrite)) {
411                unhighlightAllBasicOpSws();
412                unhighlightAllOutputEntryFields();
413                unhighlightAllRouteEntryFields();
414                return 0;
415            } else if ((operationType == OpSwOpType.OutputsRead)
416                    || (operationType == OpSwOpType.OutputsWrite)
417                    || (operationType == OpSwOpType.Route1Read)
418                    || (operationType == OpSwOpType.Route1Write)
419                    || (operationType == OpSwOpType.Route2Read)
420                    || (operationType == OpSwOpType.Route2Write)
421                    || (operationType == OpSwOpType.Route3Read)
422                    || (operationType == OpSwOpType.Route3Write)
423                    || (operationType == OpSwOpType.Route4Read)
424                    || (operationType == OpSwOpType.Route4Write)
425                    || (operationType == OpSwOpType.Route5Read)
426                    || (operationType == OpSwOpType.Route5Write)
427                    || (operationType == OpSwOpType.Route6Read)
428                    || (operationType == OpSwOpType.Route6Write)
429                    || (operationType == OpSwOpType.Route7Read)
430                    || (operationType == OpSwOpType.Route7Write)
431                    || (operationType == OpSwOpType.Route8Read)
432                    || (operationType == OpSwOpType.Route8Write)) {
433                // handle stopping of indirect access operations
434                if (state == 48) {
435                    // for DS64, indirect operations for output addresses or route entries can be
436                    // aborted after the first 16 indirect bits are accessed
437                    changeComponentBgColor(whichComponent(33, indexToRead), noAccessColor);
438                    log.debug("Decided to stop read/write after OpSw 48 because no read/write button selected."); // NOI18N
439                    return 0;
440                } else if (state == 64) {
441                    // for DS64, indirect operations for output addresses or route entries can be
442                    // aborted after the second 16 indirect bits are accessed
443                    changeComponentBgColor(whichComponent(49, indexToRead), noAccessColor);
444                    log.debug("Decided to stop read/write after OpSw 64 because no read/write button selected."); // NOI18N
445                    return 0;
446                }
447            }
448        }
449
450        switch (state) {
451            case 1: {
452                if (colorizedObject == null) {
453                    colorizedObject = outputType;
454                }
455                colorizedObject.setBackground(null);
456                isRead = read;
457                indexToRead = 0;
458                if ((operationType == null)
459                        || (operationType == OpSwOpType.BasicsRead)
460                        || (operationType == OpSwOpType.BasicsWrite)) {
461                    colorizedObject = delayTime;
462                    colorizedObject.setBackground(Color.blue.brighter());
463                    return 2;
464                } else if ((operationType == OpSwOpType.OutputsRead)
465                        || (operationType == OpSwOpType.OutputsWrite)) {
466                    indexToRead = 0;
467                    read = false;               // want to write opSw 25 thru 32
468                    setOpSwIndex(indexToRead);  //set values for opSw25 thru 32 to point to correct index
469                    return 25;
470                } else if ((operationType == OpSwOpType.Route1Read) || (operationType == OpSwOpType.Route1Write)) {
471                    indexToRead = 16;
472                    read = false;               // want to write opSw 25 thru 32
473                    setOpSwIndex(indexToRead);  //set values for opSw25 thru 32 to point to correct index
474                    return 25;
475                } else if ((operationType == OpSwOpType.Route2Read) || (operationType == OpSwOpType.Route2Write)) {
476                    indexToRead = 20;
477                    read = false;               // want to write opSw 25 thru 32
478                    setOpSwIndex(indexToRead);  //set values for opSw25 thru 32 to point to correct index
479                    return 25;
480                } else if ((operationType == OpSwOpType.Route3Read) || (operationType == OpSwOpType.Route3Write)) {
481                    indexToRead = 24;
482                    read = false;               // want to write opSw 25 thru 32
483                    setOpSwIndex(indexToRead);  //set values for opSw25 thru 32 to point to correct index
484                    return 25;
485                } else if ((operationType == OpSwOpType.Route4Read) || (operationType == OpSwOpType.Route4Write)) {
486                    indexToRead = 28;
487                    read = false;               // want to write opSw 25 thru 32
488                    setOpSwIndex(indexToRead);  //set values for opSw25 thru 32 to point to correct index
489                    return 25;
490                } else if ((operationType == OpSwOpType.Route5Read) || (operationType == OpSwOpType.Route5Write)) {
491                    indexToRead = 32;
492                    read = false;               // want to write opSw 25 thru 32
493                    setOpSwIndex(indexToRead);  //set values for opSw25 thru 32 to point to correct index
494                    return 25;
495                } else if ((operationType == OpSwOpType.Route6Read) || (operationType == OpSwOpType.Route6Write)) {
496                    indexToRead = 36;
497                    read = false;               // want to write opSw 25 thru 32
498                    setOpSwIndex(indexToRead);  //set values for opSw25 thru 32 to point to correct index
499                    return 25;
500                } else if ((operationType == OpSwOpType.Route7Read) || (operationType == OpSwOpType.Route7Write)) {
501                    indexToRead = 40;
502                    read = false;               // want to write opSw 25 thru 32
503                    setOpSwIndex(indexToRead);  //set values for opSw25 thru 32 to point to correct index
504                    return 25;
505                } else if ((operationType == OpSwOpType.Route8Read) || (operationType == OpSwOpType.Route8Write)) {
506                    indexToRead = 44;
507                    read = false;               // want to write opSw 25 thru 32
508                    setOpSwIndex(indexToRead);  //set values for opSw25 thru 32 to point to correct index
509                    return 25;
510                }
511                return 0;
512            }
513            case 2: {
514                if (colorizedObject != null) {
515                    colorizedObject.setBackground(null);
516                }
517                colorizedObject = delayTime;
518                colorizedObject.setBackground(Color.blue.brighter());
519                return 3;
520            }
521            case 3: {
522                if (colorizedObject != null) {
523                    colorizedObject.setBackground(null);
524                }
525                colorizedObject = delayTime;
526                colorizedObject.setBackground(Color.blue.brighter());
527                return 4;
528            }
529            case 4: {
530                if (colorizedObject != null) {
531                    colorizedObject.setBackground(null);
532                }
533                colorizedObject = delayTime;
534                colorizedObject.setBackground(Color.blue.brighter());
535                return 5;
536            }
537            case 5: {
538                if (colorizedObject != null) {
539                    colorizedObject.setBackground(null);
540                }
541                colorizedObject = outputStates;
542                colorizedObject.setBackground(Color.blue.brighter());
543                return 6;
544            }
545            case 6: {
546                if (colorizedObject != null) {
547                    colorizedObject.setBackground(null);
548                }
549                colorizedObject = startupDelay;
550                colorizedObject.setBackground(Color.blue.brighter());
551                return 8;// 7 has to be done last, as it's reset
552            }
553            case 8: {
554                if (colorizedObject != null) {
555                    colorizedObject.setBackground(null);
556                }
557                colorizedObject = staticOutputShutoff;
558                colorizedObject.setBackground(Color.blue.brighter());
559                return 9;
560            }
561            case 9: {
562                if (colorizedObject != null) {
563                    colorizedObject.setBackground(null);
564                }
565                colorizedObject = commandType;
566                colorizedObject.setBackground(Color.blue.brighter());
567                return 10;
568            }
569            case 10: {
570                if (colorizedObject != null) {
571                    colorizedObject.setBackground(null);
572                }
573                colorizedObject = routesControl;
574                colorizedObject.setBackground(Color.blue.brighter());
575                return 11;
576            }
577            case 11: {
578                if (colorizedObject != null) {
579                    colorizedObject.setBackground(null);
580                }
581                colorizedObject = localControlOfOutputsStyle;
582                colorizedObject.setBackground(Color.blue.brighter());
583                return 12;
584            }
585            case 12: {
586                if (colorizedObject != null) {
587                    colorizedObject.setBackground(null);
588                }
589                colorizedObject = sensorMessageTrigger;
590                colorizedObject.setBackground(Color.blue.brighter());
591                return 13;
592            }
593            case 13: {
594                if (colorizedObject != null) {
595                    colorizedObject.setBackground(null);
596                }
597                colorizedObject = commandSource;
598                colorizedObject.setBackground(Color.blue.brighter());
599                return 14;
600            }
601            case 14: {
602                if (colorizedObject != null) {
603                    colorizedObject.setBackground(null);
604                }
605                colorizedObject = localControlOfOutputsStyle;
606                colorizedObject.setBackground(Color.blue.brighter());
607                return 15;
608            }
609            case 15: {
610                if (colorizedObject != null) {
611                    colorizedObject.setBackground(null);
612                }
613                colorizedObject = routesControl;
614                colorizedObject.setBackground(Color.blue.brighter());
615                return 16;
616            }
617            case 16: {
618                if (colorizedObject != null) {
619                    colorizedObject.setBackground(null);
620                }
621                colorizedObject = output1CrossbuckFlasherCheckBox;
622                colorizedObject.setBackground(Color.blue.brighter());
623                return 17;
624            }
625            case 17: {
626                if (colorizedObject != null) {
627                    colorizedObject.setBackground(null);
628                }
629                colorizedObject = output2CrossbuckFlasherCheckBox;
630                colorizedObject.setBackground(Color.blue.brighter());
631                return 18;
632            }
633            case 18: {
634                if (colorizedObject != null) {
635                    colorizedObject.setBackground(null);
636                }
637                colorizedObject = output3CrossbuckFlasherCheckBox;
638                colorizedObject.setBackground(Color.blue.brighter());
639                return 19;
640            }
641            case 19: {
642                if (colorizedObject != null) {
643                    colorizedObject.setBackground(null);
644                }
645                colorizedObject = output4CrossbuckFlasherCheckBox;
646                colorizedObject.setBackground(Color.blue.brighter());
647                return 20;
648            }
649            case 20: {
650                if (colorizedObject != null) {
651                    colorizedObject.setBackground(null);
652                }
653                colorizedObject = localSensorType;
654                colorizedObject.setBackground(Color.blue.brighter());
655                return 21;
656            }
657            case 21: {
658                if (colorizedObject != null) {
659                    colorizedObject.setBackground(null);
660                }
661                this.readAllButton.setEnabled(true);
662                this.writeAllButton.setEnabled(true);
663                return 0;
664            }
665            case 22: {
666                return 0;
667            }
668            case 25:
669            case 26:
670            case 27:
671            case 28:
672            case 29:
673            case 30:
674            case 31: {
675                // handle indirect index bits
676                return state + 1;
677            }
678            case 32: {
679                read = isRead;               // go back to original mode of operation
680                log.debug("Dealing with index {}", indexToRead);
681                changeGuiElementHighlight(33, indexToRead);
682                if (isRead == true) {
683                    return 46;  // want to read "out-of-turn" to speed up reads when
684                    // a route entry is disabled
685                } else {
686                    // prepare values in opsw[] from appropriate write values
687                    updateOpswForWrite(indexToRead);
688                    return 33;
689                }
690            }
691            case 33:
692            case 34:
693            case 35:
694            case 36:
695            case 37:
696            case 38:
697            case 39:
698            case 40:
699            case 41:
700            case 42:
701            case 43:
702            case 44:
703            case 46:
704            case 47:
705                return state + 1;
706            case 45:
707                if (isRead) {
708                    // Have already read OpSws 64-48 and determined that there is
709                    // a valid value in bits 33-45.  Deal with that velue, then
710                    // go to the validity bits for the other half.
711                    int extractedDataValue = 0;
712                    for (int i = 48; i >= 33; i--) {
713                        extractedDataValue = (extractedDataValue << 1) + (opsw[i] ? 1 : 0);
714                    }
715                    log.debug("Read Index {} value (OpSws 33-48) = 0x{}", indexToRead + 1, Integer.toHexString(extractedDataValue));
716                    updateGuiFromOpSws33_48();
717                    changeGuiElementHighlight(48, indexToRead); // clear the highlighted GUI element
718
719                    return 62;  // because OpSws 46, 47, and 48 were read "out-
720                    // of-turn" for speediness, and also want to read OpSws
721                    // 62-64 "out-of-turn" for speediness
722                } else {
723                    return 46;
724                }
725            case 48: {
726                changeGuiElementHighlight(48, indexToRead);
727                if (isRead == true) {
728                    // For reads, check the upper bits of this "half" to determine
729                    // whether or not to read the remaining bits in the "half"
730
731                    if ((opsw[47] == false) && (opsw[48] == false)) {
732                        // entry is valid, so need to read all opSw bits in [46:33]
733                        log.debug("Read of low value in index {} is a valid entry.", indexToRead); // NOI18N
734                        return 33;
735                    } else {
736                        log.debug("Read of low value in index {} is an invalid entry.", indexToRead); // NOI18N
737                        // entry is not valid, so do not need to read opSw bits in [46:33]
738                        // Do need to update internal opSw bits so that they imply an unused
739                        // entry
740                        changeGuiElementUnHighlight(48, indexToRead);
741                        for (int i = 33; i < 46; ++i) {
742                            opsw[i] = true;
743                        }
744                        opsw[40] = false;
745
746                        // need to update the GUI
747                        updateGuiFromOpSws33_48();
748                        changeGuiElementHighlight(48, indexToRead); // clear the highlighted GUI element
749
750                        return 62; // need to skip ahead to the validity bits of the
751                        // next entry
752                    }
753                } else {
754                    // handle the case for writes
755
756                    int extractedDataValue = 0;
757                    for (int i = 48; i >= 33; i--) {
758                        extractedDataValue = (extractedDataValue << 1) + (opsw[i] ? 1 : 0);
759                    }
760                    log.debug("Wrote Index {} value (OpSws 33-48) = 0x{}", indexToRead + 1, Integer.toHexString(extractedDataValue));
761                    updateGuiFromOpSws33_48();
762                    switch (indexToRead) {
763                        case 0: {
764                            // have written a value for output1 - update GUI
765                            outAddr1.setLastQueriedValue(outAddr1.getText());
766                            break;
767                        }
768                        case 1: {
769                            // have written a value for output3 - update GUI
770                            outAddr3.setLastQueriedValue(outAddr3.getText());
771                            break;
772                        }
773                        case 16:
774                        case 20:
775                        case 24:
776                        case 28:
777                        case 32:
778                        case 36:
779                        case 40:
780                        case 44: {
781                            // have written value for Route[n] Top entry - update GUI
782                            Integer effectiveIndex = (indexToRead - 12) / 4;
783                            routeTop[effectiveIndex].addressField.setLastQueriedValue(routeTop[effectiveIndex].addressField.getText());
784                            try {
785                                routeTop[effectiveIndex].setAddress(Integer.parseInt(routeTop[effectiveIndex].addressField.getText()));
786                            } catch (NumberFormatException e) {
787                                routeTop[effectiveIndex].setIsUnused();
788                            }
789                            break;
790                        }
791                        case 17:
792                        case 21:
793                        case 25:
794                        case 29:
795                        case 33:
796                        case 37:
797                        case 41:
798                        case 45: {
799                            // have written a value for Route n - update GUI
800                            Integer effectiveIndex = (indexToRead - 13) / 4;
801                            routeA3[effectiveIndex].addressField.setLastQueriedValue(routeA3[effectiveIndex].addressField.getText());
802                            try {
803                                routeA3[effectiveIndex].setAddress(Integer.parseInt(routeA3[effectiveIndex].addressField.getText()));
804                            } catch (NumberFormatException e) {
805                                routeA3[effectiveIndex].setIsUnused();
806
807                            }
808                            break;
809                        }
810                        case 18:
811                        case 22:
812                        case 26:
813                        case 30:
814                        case 34:
815                        case 38:
816                        case 42:
817                        case 46: {
818                            // have written a value for Route n - update GUI
819                            Integer effectiveIndex = (indexToRead - 14) / 4;
820                            routeA5[effectiveIndex].addressField.setLastQueriedValue(routeA5[effectiveIndex].addressField.getText());
821                            try {
822                                routeA5[effectiveIndex].setAddress(Integer.parseInt(routeA5[effectiveIndex].addressField.getText()));
823                            } catch (NumberFormatException e) {
824                                routeA5[effectiveIndex].setIsUnused();
825                            }
826                            break;
827                        }
828                        case 19:
829                        case 23:
830                        case 27:
831                        case 31:
832                        case 35:
833                        case 39:
834                        case 43:
835                        case 47: {
836                            // have written a value for Route n - update GUI
837                            Integer effectiveIndex = (indexToRead - 15) / 4;
838                            routeA7[effectiveIndex].addressField.setLastQueriedValue(routeA7[effectiveIndex].addressField.getText());
839                            try {
840                                routeA7[effectiveIndex].setAddress(Integer.parseInt(routeA7[effectiveIndex].addressField.getText()));
841                            } catch (NumberFormatException e) {
842                                routeA7[effectiveIndex].setIsUnused();
843                            }
844                            break;
845                        }
846                        default:
847                            log.error("invalid indirectIndex for write: {}", indexToRead); // NOI18N
848                            return 0;
849                    }
850                    return 49;
851                }
852            }
853            case 49:
854            case 50:
855            case 51:
856            case 52:
857            case 53:
858            case 54:
859            case 56:
860            case 57:
861            case 58:
862            case 59:
863            case 60:
864            case 62:
865            case 63:
866                return (state + 1);
867            case 55:
868                return 57; // skip apparantly-unused bit to see if this reduces amount of output state disruption during read
869            case 61:
870                if (isRead) {
871                    int extractedDataValue = 0;
872                    for (int i = 64; i >= 49; i--) {
873                        extractedDataValue = (extractedDataValue << 1) + (opsw[i] ? 1 : 0);
874                    }
875                    log.debug("Read Index {} value (OpSws 49-64) = 0x{}", indexToRead + 1, Integer.toHexString(extractedDataValue));
876                    updateGuiFromOpSws49_64();
877                    changeGuiElementHighlight(61, indexToRead);
878                    return determineNextStateForRead();
879                } else {
880                    return 62;
881                }
882            case 64: {
883                if (isRead == true) {
884                    // For reads, check the upper bits of this "half" to determine
885                    // whether or not to read the remaining bits in the "half"
886
887                    if ((opsw[63] == false) && (opsw[64] == false)) {
888                        // entry is valid, so need to read all opSw bits in [62:49]
889                        log.debug("Read of high value in index {} is a valid entry.", indexToRead); // NOI18N
890                        changeGuiElementUnHighlight(64, indexToRead);
891                        return 49;
892                    } else {
893                        // entry is not valid, so do not need to read opSw bits in [46:33]
894                        // Do need to update internal opSw bits so that they imply an unused
895                        // entry
896                        log.debug("Read of high value in index {} is an invalid entry.", indexToRead); // NOI18N
897                        for (int i = 49; i < 62; ++i) {
898                            opsw[i] = true;
899                        }
900                        opsw[56] = false;
901
902                        // need to update GUI to show unused value
903                        updateGuiFromOpSws49_64();
904                        changeGuiElementHighlight(64, indexToRead); // clear the highlighted GUI element
905
906                        return determineNextStateForRead();
907                    }
908                } // end handling of read operation
909                else {
910                    //handle write operation
911                    // skip to next index, or, if done with indexables,
912                    // go to end.
913                    changeGuiElementHighlight(64, indexToRead); // clear the highlighted GUI element
914                    switch (indexToRead) {
915                        case 0: {
916                            // have written a value for output2 - update GUI
917                            outAddr2.setLastQueriedValue(outAddr2.getText());
918                            outAddr2.repaint();
919                            indexToRead++;
920                            read = false;
921                            setOpSwIndex(indexToRead);
922                            return 25;
923                        }
924                        case 1: {
925                            // have written a value for output4 - update GUI
926                            outAddr4.setLastQueriedValue(outAddr4.getText());
927                            outAddr4.repaint();
928                            this.readAllButton.setEnabled(true);
929                            this.writeAllButton.setEnabled(true);
930                            return 0;
931                        }
932                        case 16:
933                        case 20:
934                        case 24:
935                        case 28:
936                        case 32:
937                        case 36:
938                        case 40:
939                        case 44: {
940                            // have written a value for Route n - update GUI
941                            Integer effectiveIndex = (indexToRead - 12) / 4;
942                            routeA2[effectiveIndex].addressField.setLastQueriedValue(routeA2[effectiveIndex].addressField.getText());
943                            try {
944                                routeA2[effectiveIndex].setAddress(Integer.parseInt(routeA2[effectiveIndex].addressField.getText()));
945                            } catch (NumberFormatException e) {
946                                routeA2[effectiveIndex].setIsUnused();
947                            }
948                            indexToRead++;
949                            read = false;
950                            setOpSwIndex(indexToRead);
951                            return 25;
952                        }
953                        case 17:
954                        case 21:
955                        case 25:
956                        case 29:
957                        case 33:
958                        case 37:
959                        case 41:
960                        case 45: {
961                            // have written a value for Route n - update GUI
962                            Integer effectiveIndex = (indexToRead - 13) / 4;
963                            routeA4[effectiveIndex].addressField.setLastQueriedValue(routeA4[effectiveIndex].addressField.getText());
964                            try {
965                                routeA4[effectiveIndex].setAddress(Integer.parseInt(routeA4[effectiveIndex].addressField.getText()));
966                            } catch (NumberFormatException e) {
967                                routeA4[effectiveIndex].setIsUnused();
968                            }
969                            indexToRead++;
970                            read = false;
971                            setOpSwIndex(indexToRead);
972                            return 25;
973                        }
974                        case 18:
975                        case 22:
976                        case 26:
977                        case 30:
978                        case 34:
979                        case 38:
980                        case 42:
981                        case 46: {
982                            // have written a value for Route n - update GUI
983                            Integer effectiveIndex = (indexToRead - 14) / 4;
984                            routeA6[effectiveIndex].addressField.setLastQueriedValue(routeA6[effectiveIndex].addressField.getText());
985                            try {
986                                routeA6[effectiveIndex].setAddress(Integer.parseInt(routeA6[effectiveIndex].addressField.getText()));
987                            } catch (NumberFormatException e) {
988                                routeA6[effectiveIndex].setIsUnused();
989                            }
990                            indexToRead++;
991                            read = false;
992                            setOpSwIndex(indexToRead);
993                            return 25;
994                        }
995                        case 19:
996                        case 23:
997                        case 27:
998                        case 31:
999                        case 35:
1000                        case 39:
1001                        case 43:
1002                        case 47: {
1003                            // have written a value for Route n - update GUI
1004                            Integer effectiveIndex = (indexToRead - 15) / 4;
1005                            routeA8[effectiveIndex].addressField.setLastQueriedValue(routeA8[effectiveIndex].addressField.getText());
1006                            try {
1007                                routeA8[effectiveIndex].setAddress(Integer.parseInt(routeA8[effectiveIndex].addressField.getText()));
1008                            } catch (NumberFormatException e) {
1009                                routeA8[effectiveIndex].setIsUnused();
1010                            }
1011                            indexToRead++;
1012                            read = false;
1013                            setOpSwIndex(indexToRead);
1014                            return 0;
1015                        }
1016                        default: {
1017                            return 0;
1018                        }
1019                    }
1020                } // end handling for write operations
1021            }
1022
1023            case 7: {
1024                this.readAllButton.setEnabled(true);
1025                this.writeAllButton.setEnabled(true);
1026                log.warn("Board has been reset.  The board will now respond at Address 1."); // NOI18N
1027                return 0;
1028            }       // done!
1029            default:
1030                log.error("unexpected state {}", state); // NOI18N
1031                this.readAllButton.setEnabled(true);
1032                this.writeAllButton.setEnabled(true);
1033                return 0;
1034        }
1035    }
1036
1037    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE",
1038                justification = "False positive on the implied local variable in indexToRead++")
1039    private int determineNextStateForRead() {
1040        switch (indexToRead) {
1041            case 1: {
1042                // have read output addresses 1, 2, 3, and 4.  No more to
1043                // read (for this tab of the GUI).
1044                this.readAllButton.setEnabled(true);
1045                this.writeAllButton.setEnabled(true);
1046                return 0;
1047            }
1048            case 0:
1049            case 16:
1050            case 20:
1051            case 24:
1052            case 28:
1053            case 32:
1054            case 36:
1055            case 40:
1056            case 44:
1057            case 17:
1058            case 21:
1059            case 25:
1060            case 29:
1061            case 33:
1062            case 37:
1063            case 41:
1064            case 45:
1065            case 18:
1066            case 22:
1067            case 26:
1068            case 30:
1069            case 34:
1070            case 38:
1071            case 42:
1072            case 46: {
1073                // have read a value for Route n.  go to next value if necessary
1074                indexToRead++;
1075                read = false;
1076                setOpSwIndex(indexToRead);
1077                return 25;
1078            }
1079            case 19:
1080            case 23:
1081            case 27:
1082            case 31:
1083            case 35:
1084            case 39:
1085            case 43:
1086            case 47: {
1087                // have read last values for Route n.  No next value so stop.
1088                indexToRead++;
1089                read = false;
1090                setOpSwIndex(indexToRead);
1091                return 0;
1092            }
1093            default:
1094                return 0;
1095        }
1096    }
1097
1098    private void changeComponentBgColor(JComponent comp, Color color) {
1099        comp.setBackground(color);
1100    }
1101
1102    private JComponent whichComponent(Integer reportedState, Integer reportedIndexToRead) {
1103        if (reportedState == 33) {
1104            switch (reportedIndexToRead) {
1105                case 0:
1106                    return outAddr1;
1107
1108                case 1:
1109                    return outAddr3;
1110
1111                case 16:
1112                case 20:
1113                case 24:
1114                case 28:
1115                case 32:
1116                case 36:
1117                case 40:
1118                case 44:
1119                    return routeTop[(reportedIndexToRead - 12) / 4].addressField;
1120
1121                case 17:
1122                case 21:
1123                case 25:
1124                case 29:
1125                case 33:
1126                case 37:
1127                case 41:
1128                case 45:
1129                    return routeA3[(reportedIndexToRead - 13) / 4].addressField;
1130
1131                case 18:
1132                case 22:
1133                case 26:
1134                case 30:
1135                case 34:
1136                case 38:
1137                case 42:
1138                case 46:
1139                    return routeA5[(reportedIndexToRead - 14) / 4].addressField;
1140
1141                case 19:
1142                case 23:
1143                case 27:
1144                case 31:
1145                case 35:
1146                case 39:
1147                case 43:
1148                case 47:
1149                    return routeA7[(reportedIndexToRead - 15) / 4].addressField;
1150
1151                default:
1152                    return null;
1153            }
1154        } else if (reportedState == 49) {
1155            switch (reportedIndexToRead) {
1156                case 0:
1157                    return outAddr2;
1158
1159                case 1:
1160                    return outAddr4;
1161
1162                case 16:
1163                case 20:
1164                case 24:
1165                case 28:
1166                case 32:
1167                case 36:
1168                case 40:
1169                case 44:
1170                    return routeA2[(reportedIndexToRead - 12) / 4].addressField;
1171
1172                case 17:
1173                case 21:
1174                case 25:
1175                case 29:
1176                case 33:
1177                case 37:
1178                case 41:
1179                case 45:
1180                    return routeA4[(reportedIndexToRead - 13) / 4].addressField;
1181
1182                case 18:
1183                case 22:
1184                case 26:
1185                case 30:
1186                case 34:
1187                case 38:
1188                case 42:
1189                case 46:
1190                    return routeA6[(reportedIndexToRead - 14) / 4].addressField;
1191
1192                case 19:
1193                case 23:
1194                case 27:
1195                case 31:
1196                case 35:
1197                case 39:
1198                case 43:
1199                case 47:
1200                    return routeA8[(reportedIndexToRead - 15) / 4].addressField;
1201
1202                default:
1203                    return null;
1204            }
1205        }
1206        return null;
1207    }
1208
1209    private void changeGuiElementUnHighlight(Integer reportedState, Integer reportedIndexToRead) {
1210        log.debug("changedGuiElementUnHiglight st={} index={}", reportedState, reportedIndexToRead);
1211        JComponent jc;
1212        switch (reportedState) {
1213            case 33:
1214                return;
1215            case 45:
1216            case 48:
1217                jc = whichComponent(33, reportedIndexToRead);
1218                if (jc != null) {
1219                    changeComponentBgColor(jc, null); // inherit from parent
1220                }
1221                return;
1222            case 61:
1223            case 64:
1224                jc = whichComponent(49, reportedIndexToRead);
1225                if (jc != null) {
1226                    changeComponentBgColor(jc, null); // inherit from parent
1227                }
1228                break;
1229            default:
1230                // nothing to do in this case
1231                break;
1232        }
1233    }
1234
1235    private void changeGuiElementHighlight(Integer reportedState, Integer reportedIndexToRead) {
1236        log.debug("changedGuiElementHiglight st={} index={}", reportedState, reportedIndexToRead);
1237        Color accessColor = Color.blue.brighter();
1238        JComponent jc;
1239        if (reportedState == 33) {
1240            jc = whichComponent(reportedState, reportedIndexToRead);
1241            changeComponentBgColor(jc, accessColor);
1242        }
1243        if (reportedState == 48) {
1244            changeGuiElementUnHighlight(33, reportedIndexToRead);
1245            jc = whichComponent(49, reportedIndexToRead);
1246            if (jc != null) {
1247                changeComponentBgColor(jc, accessColor);
1248            }
1249        }
1250        if (reportedState == 64) {
1251            jc = whichComponent(49, reportedIndexToRead);
1252            changeComponentBgColor(jc, null); // inherit from parent component
1253        }
1254    }
1255
1256    private boolean alreadyKnowThisBoardId(Integer id) {
1257        return (boardNumsEntryValue.contains(id));
1258    }
1259
1260    private Integer addBoardIdToList(Integer id) {
1261        boardNumsEntryValue.add(boardNumsEntryValue.size(), id);
1262        addressComboBox.removeAllItems();
1263        Collections.sort(boardNumsEntryValue);
1264        Integer indexOfTargetBoardAddress = 0;
1265        for (Integer index = 0; index < boardNumsEntryValue.size(); ++index) {
1266            if (boardNumsEntryValue.get(index).equals(id)) {
1267                indexOfTargetBoardAddress = index;
1268            }
1269            addressComboBox.addItem(boardNumsEntryValue.get(index));
1270        }
1271        return indexOfTargetBoardAddress;
1272    }
1273
1274    private void selectBoardIdByIndex(Integer index) {
1275        addressComboBox.setSelectedIndex(index);
1276    }
1277
1278    @Override
1279    public void readAll() {
1280        addrField.setText(addressComboBox.getSelectedItem().toString());
1281
1282        Integer curAddr = Integer.parseInt(addrField.getText());
1283
1284        // If a new board address is specified, add it (and sort it) into the current list.
1285        if (!alreadyKnowThisBoardId(curAddr)) {
1286            Integer index = addBoardIdToList(curAddr);
1287            selectBoardIdByIndex(index);
1288        }
1289        if (generalTabbedPane.getSelectedComponent().getClass() == JPanel.class) {
1290            if (((JPanel) generalTabbedPane.getSelectedComponent()) == generalPanel) {
1291                operationType = OpSwOpType.BasicsRead;
1292            } else if (((JPanel) generalTabbedPane.getSelectedComponent()) == outputAddrsPanel) {
1293                operationType = OpSwOpType.OutputsRead;
1294            }
1295        } else if (generalTabbedPane.getSelectedComponent().getClass() == JScrollPane.class) {
1296            if (((JScrollPane) generalTabbedPane.getSelectedComponent()) == opswsScrollPane) {
1297                operationType = OpSwOpType.BasicsRead;
1298            }
1299        } else if (generalTabbedPane.getSelectedComponent().getClass() == JTabbedPane.class) {
1300            if (((JTabbedPane) generalTabbedPane.getSelectedComponent() == routesTabbedPane)) {
1301                if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[1]) {
1302                    operationType = OpSwOpType.Route1Read;
1303                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[2]) {
1304                    operationType = OpSwOpType.Route2Read;
1305                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[3]) {
1306                    operationType = OpSwOpType.Route3Read;
1307                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[4]) {
1308                    operationType = OpSwOpType.Route4Read;
1309                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[5]) {
1310                    operationType = OpSwOpType.Route5Read;
1311                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[6]) {
1312                    operationType = OpSwOpType.Route6Read;
1313                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[7]) {
1314                    operationType = OpSwOpType.Route7Read;
1315                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[8]) {
1316                    operationType = OpSwOpType.Route8Read;
1317                } else {
1318                    log.error("DS64 TabbedPanel into readAll(): no known Route[n] tab selected.");
1319                    return;
1320                }
1321            } else {
1322                log.error("DS64 TabbedPanel into ReadAll(): no selected tab group");
1323                return;
1324            }
1325        } else {
1326            return;
1327        }
1328        super.readAll();
1329    }
1330
1331    public void updateBoardAddress() {
1332        addrField.setText(addressComboBox.getSelectedItem().toString());
1333    }
1334
1335    @Override
1336    public void writeAll() {
1337        addrField.setText(addressComboBox.getSelectedItem().toString());
1338
1339        Integer curAddr = Integer.parseInt(addrField.getText());
1340
1341        // If a new board address is specified, add it (and sort it) into the current list.
1342        if (!boardNumsEntryValue.contains(curAddr)) {
1343            boardNumsEntryValue.add(boardNumsEntryValue.size(), curAddr);
1344            addressComboBox.removeAllItems();
1345            Collections.sort(boardNumsEntryValue);
1346            Integer indexOfTargetBoardAddress = 0;
1347            for (Integer index = 0; index < boardNumsEntryValue.size(); ++index) {
1348                if (boardNumsEntryValue.get(index).equals(curAddr)) {
1349                    indexOfTargetBoardAddress = index;
1350                }
1351                addressComboBox.addItem(boardNumsEntryValue.get(index));
1352            }
1353            addressComboBox.setSelectedIndex(indexOfTargetBoardAddress);
1354        }
1355
1356        if (generalTabbedPane.getSelectedComponent().getClass() == JPanel.class) {
1357            if (((JPanel) generalTabbedPane.getSelectedComponent()) == generalPanel) {
1358                operationType = OpSwOpType.BasicsWrite;
1359            } else if (((JPanel) generalTabbedPane.getSelectedComponent()) == outputAddrsPanel) {
1360                operationType = OpSwOpType.OutputsWrite;
1361            }
1362        } else if (generalTabbedPane.getSelectedComponent().getClass() == JScrollPane.class) {
1363            if (((JScrollPane) generalTabbedPane.getSelectedComponent()) == opswsScrollPane) {
1364                operationType = OpSwOpType.BasicsWrite;
1365            }
1366
1367        } else if (generalTabbedPane.getSelectedComponent().getClass() == JTabbedPane.class) {
1368            if (((JTabbedPane) generalTabbedPane.getSelectedComponent()) == routesTabbedPane) {
1369
1370                if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[1]) {
1371                    operationType = OpSwOpType.Route1Write;
1372                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[2]) {
1373                    operationType = OpSwOpType.Route2Write;
1374                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[3]) {
1375                    operationType = OpSwOpType.Route3Write;
1376                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[4]) {
1377                    operationType = OpSwOpType.Route4Write;
1378                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[5]) {
1379                    operationType = OpSwOpType.Route5Write;
1380                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[6]) {
1381                    operationType = OpSwOpType.Route6Write;
1382                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[7]) {
1383                    operationType = OpSwOpType.Route7Write;
1384                } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[8]) {
1385                    operationType = OpSwOpType.Route8Write;
1386                } else {
1387                    return;
1388                }
1389            } else {
1390                return;
1391            }
1392        } else {
1393            return;
1394        }
1395
1396        super.writeAll();
1397    }
1398
1399    private enum OpSwOpType {
1400        OutputsRead, OutputsWrite,
1401        Route1Read, Route1Write,
1402        Route2Read, Route2Write,
1403        Route3Read, Route3Write,
1404        Route4Read, Route4Write,
1405        Route5Read, Route5Write,
1406        Route6Read, Route6Write,
1407        Route7Read, Route7Write,
1408        Route8Read, Route8Write,
1409        BasicsRead, BasicsWrite
1410    }
1411
1412    private OpSwOpType operationType = null;
1413    Boolean isRead;
1414    Boolean isWritingResetOpSw = false;
1415    Integer indexToRead = 0;
1416    Boolean resetOpSwVal = false;
1417
1418    /**
1419     * Set index into OpSw table
1420     *
1421     * @param index  the indirect address
1422     */
1423    protected void setOpSwIndex(int index) {
1424        opsw[25] = (index & 1) == 1;
1425        opsw[26] = (index & 2) == 2;
1426        opsw[27] = (index & 4) == 4;
1427        opsw[28] = (index & 8) == 8;
1428        opsw[29] = (index & 16) == 16;
1429        opsw[30] = (index & 32) == 32;
1430        opsw[31] = (index & 64) == 64;
1431        opsw[32] = (index & 128) == 128;
1432    }
1433
1434    /**
1435     * Updates data register to reflect address, state, and enable for two
1436     * turnouts.
1437     *
1438     * @param address1   first turnout address
1439     * @param state1     first turnout's state
1440     * @param is1Unused  true if first turnout entry is to be "unused"
1441     * @param address2   second turnout address
1442     * @param state2     second turnout's state
1443     * @param is2Unused  true if second turnout entry is to be "unused"
1444     */
1445    protected void updateOpSwsOutAddr(int address1, boolean state1, boolean is1Unused, int address2, boolean state2, boolean is2Unused) {
1446        int addr1 = address1 - 1;
1447        int addr2 = address2 - 1;
1448        if ((address1 == 0) || (is1Unused)) {
1449            addr1 = 2047;
1450            is1Unused = true;
1451        }
1452        if ((address2 == 0) || (is2Unused)) {
1453            addr2 = 2047;
1454            is2Unused = true;
1455        }
1456        opsw[33] = ((addr1 & 1) == 1);
1457        opsw[34] = ((addr1 & 2) == 2);
1458        opsw[35] = ((addr1 & 4) == 4);
1459        opsw[36] = ((addr1 & 8) == 8);
1460        opsw[37] = ((addr1 & 16) == 16);
1461        opsw[38] = ((addr1 & 32) == 32);
1462        opsw[39] = ((addr1 & 64) == 64);
1463        opsw[40] = false;
1464        opsw[41] = ((addr1 & 128) == 128);
1465        opsw[42] = ((addr1 & 256) == 256);
1466        opsw[43] = ((addr1 & 512) == 512);
1467        opsw[44] = ((addr1 & 1024) == 1024);
1468        opsw[45] = true;
1469        opsw[46] = state1;
1470        if (!is1Unused) {
1471            opsw[47] = false;
1472            opsw[48] = false;
1473        } else {
1474            opsw[47] = true;
1475            opsw[48] = true;
1476        }
1477
1478        opsw[49] = ((addr2 & 1) == 1);
1479        opsw[50] = ((addr2 & 2) == 2);
1480        opsw[51] = ((addr2 & 4) == 4);
1481        opsw[52] = ((addr2 & 8) == 8);
1482        opsw[53] = ((addr2 & 16) == 16);
1483        opsw[54] = ((addr2 & 32) == 32);
1484        opsw[55] = ((addr2 & 64) == 64);
1485        opsw[56] = false;
1486        opsw[57] = ((addr2 & 128) == 128);
1487        opsw[58] = ((addr2 & 256) == 256);
1488        opsw[59] = ((addr2 & 512) == 512);
1489        opsw[60] = ((addr2 & 1024) == 1024);
1490        opsw[61] = true;
1491        opsw[62] = state2;
1492        if (!is2Unused) {
1493            opsw[63] = false;
1494            opsw[64] = false;
1495        } else {
1496            opsw[63] = true;
1497            opsw[64] = true;
1498        }
1499    }
1500
1501    /**
1502     * Updates OpSw values for a given index into the data array
1503     *
1504     * @param index  indirect address
1505     */
1506    protected void updateOpswForWrite(int index) {
1507        Integer value1Address;
1508        Integer value2Address;
1509        boolean value1IsUnused;
1510        boolean value2IsUnused;
1511        boolean value1DirectionIsClosed;
1512        boolean value2DirectionIsClosed;
1513
1514        switch (index) {
1515            case 0: {
1516                try {
1517                    value1Address = Integer.parseInt(outAddr1.getText());
1518                } catch (NumberFormatException e) {
1519                    value1Address = 2048;
1520                }
1521
1522                try {
1523                    value2Address = Integer.parseInt(outAddr2.getText());
1524                } catch (NumberFormatException e) {
1525                    value2Address = 2048;
1526                }
1527
1528                updateOpSwsOutAddr(value1Address, false, false, value2Address, false, false);
1529                break;
1530            }
1531            case 1: {
1532                try {
1533                    value1Address = Integer.parseInt(outAddr3.getText());
1534                } catch (NumberFormatException e) {
1535                    value1Address = 2048;
1536                }
1537
1538                try {
1539                    value2Address = Integer.parseInt(outAddr4.getText());
1540                } catch (NumberFormatException e) {
1541                    value2Address = 2048;
1542                }
1543
1544                updateOpSwsOutAddr(value1Address, false, false, value2Address, false, false);
1545                break;
1546            }
1547            case 16:
1548            case 20:
1549            case 24:
1550            case 28:
1551            case 32:
1552            case 36:
1553            case 40:
1554            case 44: {
1555                Integer extractedIndex = (index - 12) / 4;
1556
1557                opsw[47] = false;
1558                opsw[48] = false; // assume valid turnout address entry
1559                opsw[63] = false;
1560                opsw[64] = false; // assume valid turnout address entry
1561
1562                if (routeTop[extractedIndex].getIsUnused()) {
1563                    log.warn("updateOpswForWrite - routetop[{}] is unused.", extractedIndex); // NOI18N
1564                    value1Address = 2048;
1565                    value1IsUnused = true;
1566                    value1DirectionIsClosed = true;
1567                } else {
1568                    value1DirectionIsClosed = routeTop[extractedIndex].closedRadioButton.isSelected();
1569                    value1IsUnused = false;
1570                    try {
1571                        value1Address = Integer.parseInt(routeTop[extractedIndex].addressField.getText());
1572                    } catch (NumberFormatException e) {
1573                        value1Address = 2048;
1574                        value1IsUnused = true;
1575                        value1DirectionIsClosed = true;
1576                    }
1577                }
1578
1579                if (routeA2[extractedIndex].getIsUnused()) {
1580                    log.warn("updateOpswForWrite - routeA2[{}] is unused.", extractedIndex); // NOI18N
1581                    value2Address = 2048;
1582                    value2IsUnused = true;
1583                    value2DirectionIsClosed = true;
1584                } else {
1585                    value2DirectionIsClosed = routeA2[extractedIndex].closedRadioButton.isSelected();
1586                    value2IsUnused = false;
1587                    try {
1588                        value2Address = Integer.parseInt(routeA2[extractedIndex].addressField.getText());
1589                    } catch (NumberFormatException e) {
1590                        value2Address = 2048;
1591                        value2IsUnused = true;
1592                        value2DirectionIsClosed = true;
1593                    }
1594                }
1595
1596                updateOpSwsOutAddr(value1Address, value1DirectionIsClosed, value1IsUnused,
1597                        value2Address, value2DirectionIsClosed, value2IsUnused);
1598                if (value1IsUnused) {
1599                    opsw[46] = true;
1600                    opsw[47] = true;
1601                    opsw[48] = true; // mark entry as invalid
1602                    routeTop[extractedIndex].unusedRadioButton.setSelected(true);
1603                    routeTop[extractedIndex].unusedRadioButton.repaint();
1604                    routeTop[extractedIndex].setAddress(2048);
1605                    routeTop[extractedIndex].addressField.setText("");
1606                }
1607                if (value2IsUnused) {
1608                    opsw[62] = true;
1609                    opsw[63] = true;
1610                    opsw[64] = true; // mark entry as invalid
1611                    routeA2[extractedIndex].unusedRadioButton.setSelected(true);
1612                    routeA2[extractedIndex].unusedRadioButton.repaint();
1613                    routeA2[extractedIndex].setAddress(2048);
1614                    routeA2[extractedIndex].addressField.setText("");
1615                }
1616                break;
1617            }
1618            case 17:
1619            case 21:
1620            case 25:
1621            case 29:
1622            case 33:
1623            case 37:
1624            case 41:
1625            case 45: {
1626                Integer extractedIndex = (index - 13) / 4;
1627
1628                opsw[47] = false;
1629                opsw[48] = false; // assume valid turnout address entry
1630                opsw[63] = false;
1631                opsw[64] = false; // assume valid turnout address entry
1632
1633                if (routeA3[extractedIndex].getIsUnused()) {
1634                    value1Address = 2048;
1635                    value1IsUnused = true;
1636                    value1DirectionIsClosed = true;
1637                } else {
1638                    value1DirectionIsClosed = routeA3[extractedIndex].closedRadioButton.isSelected();
1639                    value1IsUnused = false;
1640                    try {
1641                        value1Address = Integer.parseInt(routeA3[extractedIndex].addressField.getText());
1642                    } catch (NumberFormatException e) {
1643                        value1Address = 2048;
1644                        value1IsUnused = true;
1645                    }
1646                }
1647
1648                if (routeA4[extractedIndex].getIsUnused()) {
1649                    value2Address = 2048;
1650                    value2IsUnused = true;
1651                    value2DirectionIsClosed = true;
1652                } else {
1653                    value2DirectionIsClosed = routeA4[extractedIndex].closedRadioButton.isSelected();
1654                    value2IsUnused = false;
1655                    try {
1656                        value2Address = Integer.parseInt(routeA4[extractedIndex].addressField.getText());
1657                    } catch (NumberFormatException e) {
1658                        value2Address = 2048;
1659                        value2IsUnused = true;
1660                    }
1661                }
1662
1663                updateOpSwsOutAddr(value1Address, value1DirectionIsClosed, value1IsUnused,
1664                        value2Address, value2DirectionIsClosed, value2IsUnused);
1665                if (value1IsUnused) {
1666                    opsw[46] = true;
1667                    opsw[47] = true;
1668                    opsw[48] = true; // mark entry as invalid
1669                    routeA3[extractedIndex].unusedRadioButton.setSelected(true);
1670                    routeA3[extractedIndex].unusedRadioButton.repaint();
1671                    routeA3[extractedIndex].setAddress(2048);
1672                    routeA3[extractedIndex].addressField.setText("");
1673                }
1674                if (value2IsUnused) {
1675                    opsw[62] = true;
1676                    opsw[63] = true;
1677                    opsw[64] = true; // mark entry as invalid
1678                    routeA4[extractedIndex].unusedRadioButton.setSelected(true);
1679                    routeA4[extractedIndex].unusedRadioButton.repaint();
1680                    routeA4[extractedIndex].setAddress(2048);
1681                    routeA4[extractedIndex].addressField.setText("");
1682                }
1683                break;
1684            }
1685            case 18:
1686            case 22:
1687            case 26:
1688            case 30:
1689            case 34:
1690            case 38:
1691            case 42:
1692            case 46: {
1693                Integer extractedIndex = (index - 14) / 4;
1694                opsw[47] = false;
1695                opsw[48] = false; // assume valid turnout address entry
1696                opsw[63] = false;
1697                opsw[64] = false; // assume valid turnout address entry
1698
1699                if (routeA5[extractedIndex].getIsUnused()) {
1700                    value1Address = 2048;
1701                    value1IsUnused = true;
1702                    value1DirectionIsClosed = true;
1703                } else {
1704                    value1DirectionIsClosed = routeA5[extractedIndex].closedRadioButton.isSelected();
1705                    value1IsUnused = false;
1706                    try {
1707                        value1Address = Integer.parseInt(routeA5[extractedIndex].addressField.getText());
1708                    } catch (NumberFormatException e) {
1709                        value1Address = 2048;
1710                        value1IsUnused = true;
1711                    }
1712                }
1713
1714                if (routeA6[extractedIndex].getIsUnused()) {
1715                    value2Address = 2048;
1716                    value2IsUnused = true;
1717                    value2DirectionIsClosed = true;
1718                } else {
1719                    value2DirectionIsClosed = routeA6[extractedIndex].closedRadioButton.isSelected();
1720                    value2IsUnused = false;
1721                    try {
1722                        value2Address = Integer.parseInt(routeA6[extractedIndex].addressField.getText());
1723                    } catch (NumberFormatException e) {
1724                        value2Address = 2048;
1725                        value2IsUnused = true;
1726                    }
1727                }
1728
1729                updateOpSwsOutAddr(value1Address, value1DirectionIsClosed, value1IsUnused,
1730                        value2Address, value2DirectionIsClosed, value2IsUnused);
1731                if (value1IsUnused) {
1732                    opsw[46] = true;
1733                    opsw[47] = true;
1734                    opsw[48] = true; // mark entry as invalid
1735                    routeA5[extractedIndex].unusedRadioButton.setSelected(true);
1736                    routeA5[extractedIndex].unusedRadioButton.repaint();
1737                    routeA5[extractedIndex].setAddress(2048);
1738                    routeA5[extractedIndex].addressField.setText("");
1739                }
1740                if (value2IsUnused) {
1741                    opsw[62] = true;
1742                    opsw[63] = true;
1743                    opsw[64] = true; // mark entry as invalid
1744                    routeA6[extractedIndex].unusedRadioButton.setSelected(true);
1745                    routeA6[extractedIndex].unusedRadioButton.repaint();
1746                    routeA6[extractedIndex].setAddress(2048);
1747                    routeA6[extractedIndex].addressField.setText("");
1748                }
1749                break;
1750            }
1751            case 19:
1752            case 23:
1753            case 27:
1754            case 31:
1755            case 35:
1756            case 39:
1757            case 43:
1758            case 47: {
1759                Integer extractedIndex = (index - 15) / 4;
1760
1761                opsw[47] = false;
1762                opsw[48] = false; // assume valid turnout address entry
1763                opsw[63] = false;
1764                opsw[64] = false; // assume valid turnout address entry
1765
1766                if (routeA7[extractedIndex].getIsUnused()) {
1767                    value1Address = 2048;
1768                    value1IsUnused = true;
1769                    value1DirectionIsClosed = true;
1770                } else {
1771                    value1DirectionIsClosed = routeA7[extractedIndex].closedRadioButton.isSelected();
1772                    value1IsUnused = false;
1773                    try {
1774                        value1Address = Integer.parseInt(routeA7[extractedIndex].addressField.getText());
1775                    } catch (NumberFormatException e) {
1776                        value1Address = 2048;
1777                        value1IsUnused = true;
1778                    }
1779                }
1780
1781                if (routeA8[extractedIndex].getIsUnused()) {
1782                    value2Address = 2048;
1783                    value2IsUnused = true;
1784                    value2DirectionIsClosed = true;
1785                } else {
1786                    value2DirectionIsClosed = routeA8[extractedIndex].closedRadioButton.isSelected();
1787                    value2IsUnused = false;
1788                    try {
1789                        value2Address = Integer.parseInt(routeA8[extractedIndex].addressField.getText());
1790                    } catch (NumberFormatException e) {
1791                        value2Address = 2048;
1792                        value2IsUnused = true;
1793                    }
1794                }
1795
1796                updateOpSwsOutAddr(value1Address, value1DirectionIsClosed, value1IsUnused,
1797                        value2Address, value2DirectionIsClosed, value2IsUnused);
1798                if (value1IsUnused) {
1799                    opsw[46] = true;
1800                    opsw[47] = true;
1801                    opsw[48] = true; // mark entry as invalid
1802                    routeA7[extractedIndex].unusedRadioButton.setSelected(true);
1803                    routeA7[extractedIndex].unusedRadioButton.repaint();
1804                    routeA7[extractedIndex].setAddress(2048);
1805                    routeA7[extractedIndex].addressField.setText("");
1806                }
1807                if (value2IsUnused) {
1808                    opsw[62] = true;
1809                    opsw[63] = true;
1810                    opsw[64] = true; // mark entry as invalid
1811                    routeA8[extractedIndex].unusedRadioButton.setSelected(true);
1812                    routeA8[extractedIndex].unusedRadioButton.repaint();
1813                    routeA8[extractedIndex].setAddress(2048);
1814                    routeA8[extractedIndex].addressField.setText("");
1815                }
1816                break;
1817            }
1818            case 48: {
1819                break;
1820            }
1821            default: {
1822                break;
1823            }
1824        }
1825    }
1826
1827    private void resetRouteOperation(Integer routeNumber) {
1828        if ((routeNumber < 1) || (routeNumber > 8)) {
1829            return;
1830        }
1831        routeTop[routeNumber].unusedRadioButton.setSelected(true);
1832        routeTop[routeNumber].setIsUnused();
1833        routeTop[routeNumber].addressField.setText("");
1834
1835        routeA2[routeNumber].setIsUnused();
1836        routeA2[routeNumber].unusedRadioButton.setSelected(true);
1837        routeA2[routeNumber].addressField.setText("");
1838
1839        routeA3[routeNumber].setIsUnused();
1840        routeA3[routeNumber].unusedRadioButton.setSelected(true);
1841        routeA3[routeNumber].addressField.setText("");
1842
1843        routeA4[routeNumber].setIsUnused();
1844        routeA4[routeNumber].unusedRadioButton.setSelected(true);
1845        routeA4[routeNumber].addressField.setText("");
1846
1847        routeA5[routeNumber].setIsUnused();
1848        routeA5[routeNumber].unusedRadioButton.setSelected(true);
1849        routeA5[routeNumber].addressField.setText("");
1850
1851        routeA6[routeNumber].setIsUnused();
1852        routeA6[routeNumber].unusedRadioButton.setSelected(true);
1853        routeA6[routeNumber].addressField.setText("");
1854
1855        routeA7[routeNumber].setIsUnused();
1856        routeA7[routeNumber].unusedRadioButton.setSelected(true);
1857        routeA7[routeNumber].addressField.setText("");
1858
1859        routeA8[routeNumber].setIsUnused();
1860        routeA8[routeNumber].unusedRadioButton.setSelected(true);
1861        routeA8[routeNumber].addressField.setText("");
1862
1863        writeAll();
1864    }
1865
1866    private void unhighlightAllBasicOpSws() {
1867
1868        outputType.setBackground(null);
1869        delayTime.setBackground(null);
1870        outputStates.setBackground(null);
1871        startupDelay.setBackground(null);
1872        staticOutputShutoff.setBackground(null);
1873        commandType.setBackground(null);
1874        routesControl.setBackground(null);
1875        localControlOfOutputsStyle.setBackground(null);
1876        sensorMessageTrigger.setBackground(null);
1877        commandSource.setBackground(null);
1878        output1CrossbuckFlasherCheckBox.setBackground(null);
1879        output2CrossbuckFlasherCheckBox.setBackground(null);
1880        output3CrossbuckFlasherCheckBox.setBackground(null);
1881        output4CrossbuckFlasherCheckBox.setBackground(null);
1882        localSensorType.setBackground(null);
1883    }
1884
1885    private void unhighlightAllRouteEntryFields() {
1886        for (int i = 1; i < 9; ++i) {
1887            routeTop[i].addressField.setBackground(null);
1888            routeA2[i].addressField.setBackground(null);
1889            routeA3[i].addressField.setBackground(null);
1890            routeA4[i].addressField.setBackground(null);
1891            routeA5[i].addressField.setBackground(null);
1892            routeA6[i].addressField.setBackground(null);
1893            routeA7[i].addressField.setBackground(null);
1894            routeA8[i].addressField.setBackground(null);
1895            updateUI();
1896        }
1897    }
1898
1899    private void unhighlightAllOutputEntryFields() {
1900        outAddr1.setBackground(null);
1901        outAddr2.setBackground(null);
1902        outAddr3.setBackground(null);
1903        outAddr4.setBackground(null);
1904    }
1905
1906    @Override
1907    public void message(LocoNetMessage m) {
1908        super.message(m);
1909
1910        if (m.getOpCode() == LnConstants.OPC_LONG_ACK) {
1911            if (((m.getElement(1) == LnConstants.RE_LACK_SPEC_CASE1)
1912                    || (m.getElement(1) == LnConstants.RE_LACK_SPEC_CASE2))
1913                    && (state == 0) && resetRouteButton.isSelected()) {
1914                // Handle DS64 confirmation of OpSw write when action is a DS64
1915                // Board Factory Reset
1916                resetRouteButton.setSelected(false);
1917                resetRouteButton.updateUI();
1918            }
1919        }
1920        if (m.getOpCode() == LnConstants.OPC_SW_REQ) {
1921            int swAddr = (((m.getElement(2) & 0x0f) * 128) + (m.getElement(1) & 0x7f)) + 1;
1922            boolean dir = ((m.getElement(2) & 0x20) == 0x20);
1923            if (swAddr == Integer.parseInt(outAddr1.getText())) {
1924                outState1.setText(dir ? Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateClosed"))
1925                        : Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateThrown")));
1926                outState1.updateUI();
1927            }
1928            if (swAddr == Integer.parseInt(outAddr2.getText())) {
1929                outState2.setText(dir ? Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateClosed"))
1930                        : Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateThrown")));
1931                outState2.updateUI();
1932            }
1933            if (swAddr == Integer.parseInt(outAddr3.getText())) {
1934                outState3.setText(dir ? Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateClosed"))
1935                        : Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateThrown")));
1936                outState3.updateUI();
1937            }
1938            if (swAddr == Integer.parseInt(outAddr4.getText())) {
1939                outState4.setText(dir ? Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateClosed"))
1940                        : Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateThrown")));
1941                outState4.updateUI();
1942            }
1943        } else if ((m.getOpCode() == LnConstants.OPC_MULTI_SENSE) && ((m.getElement(1) & 0x7E) == 0x62)) {
1944            // device identity report
1945            if (m.getElement(3) == 0x03) {
1946                Integer extractedBoardId = 1 + ((m.getElement(1) & 0x1) << 7)
1947                        + (m.getElement(2) & 0x7F);
1948                if (!alreadyKnowThisBoardId(extractedBoardId)) {
1949                    addBoardIdToList(extractedBoardId);
1950                }
1951            }
1952        }
1953    }
1954
1955    /**
1956     * Reset the DS64 board
1957     */
1958    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "Functionality not yet confirmed with hardware; may be useful at a future date.")
1959    private void boardFactoryReset() {
1960
1961        // before proceeding, make sure that the user really wants to go forward
1962        Object[] dialogBoxButtonOptions = {
1963            Bundle.getMessage("ButtonResetToFactoryDefault"),
1964            Bundle.getMessage("ButtonCancel")};
1965        int userReply = JmriJOptionPane.showOptionDialog(this,
1966                Bundle.getMessage("DialogTextBoardResetWarning"),
1967                Bundle.getMessage("WarningTitle"),
1968                JmriJOptionPane.YES_NO_OPTION, JmriJOptionPane.QUESTION_MESSAGE,
1969                null, dialogBoxButtonOptions, dialogBoxButtonOptions[1]);
1970        if ( userReply != JmriJOptionPane.YES_OPTION ) {
1971            factoryResetButton.setSelected(false);
1972            return; // compare only to exactly the value for executing the reset!
1973        }
1974        readAllButton.setEnabled(false);
1975        writeAllButton.setEnabled(false);
1976        resetRouteButton.setEnabled(false);
1977        factoryResetButton.setEnabled(false);
1978
1979        // send OpSw 7 = Closed to this boardId to reset the DS64
1980        //to its factory default settings
1981        // then want to read all OpSws to update the display.
1982        read = false;
1983        isWritingResetOpSw = true;
1984        resetOpSwVal = true;
1985        opsw[7] = true;
1986        updateBoardAddress();
1987        writeOne(7);
1988        boardResetResponseTimer = new javax.swing.Timer(750,
1989                event -> {
1990                    factoryResetButton.setSelected(false);
1991                    factoryResetButton.setEnabled(true);
1992                    readAllButton.setEnabled(true);
1993                    writeAllButton.setEnabled(true);
1994                    resetRouteButton.setEnabled(true);
1995                    updateUI();
1996                }
1997        );
1998        boardResetResponseTimer.start();
1999    }
2000
2001    private final ActionListener routeResetResponseTimerListener = new ActionListener() {
2002        @Override
2003        public void actionPerformed(ActionEvent e) {
2004            log.debug("routeresetresponsetimerlistener state={}, indextoread{}", state, indexToRead);
2005            resetRouteButton.setSelected(false);
2006            readAllButton.setEnabled(true);
2007            writeAllButton.setEnabled(true);
2008            factoryResetButton.setEnabled(true);
2009        }
2010    };
2011
2012    @Override
2013    public void initComponents(LocoNetSystemConnectionMemo memo) {
2014        super.initComponents(memo);
2015        LocoNetMessage m = new LocoNetMessage(6);
2016        m.setElement(0, LnConstants.OPC_MULTI_SENSE);
2017        m.setElement(1, 0x62);
2018        m.setElement(2, 0);
2019        m.setElement(3, 0x70);
2020        m.setElement(4, 0);
2021        memo.getLnTrafficController().sendLocoNetMessage(m);
2022    }
2023
2024    @Override
2025    public void initComponents() {
2026        super.initComponents();
2027        // implements an AbstractBoardProgPanel with three tabs:
2028        //      Outputs tab - configure features most related to DS64 outputs
2029        //      Inputs tab - configure features most related to DS64 inputs
2030        //      Routes tab - configure features related to routes
2031        //          Routes tab has (left side, vertical) sub-tabs, one for each of the 8 routes
2032
2033
2034//        JLabel addrFieldLabel = new JTextLabel(Bundle.getMessage("LabelBoardID"));
2035//        addrField = addressComboBox(getSelectedItem);
2036
2037        String[] outputTypes = {Bundle.getMessage("ComboBoxOutputType0"),
2038            Bundle.getMessage("ComboBoxOutputType1")};
2039        outputTypeLabel = new JLabel(Bundle.getMessage("LabelOutputType"));
2040        outputType = new JComboBox<>(outputTypes); // opSw 1
2041        outputType.setToolTipText(Bundle.getMessage("ToolTipOutputType"));
2042        outputType.addActionListener(basicConfigChangeActionListener);
2043        outputType.setName("1"); // NOI18N
2044
2045        String[] availableDelayTimes = new String[16];
2046        availableDelayTimes[0] = Bundle.getMessage("ComboBoxPulseTime0point1");
2047        availableDelayTimes[1] = Bundle.getMessage("ComboBoxPulseTime0point2");
2048        availableDelayTimes[2] = Bundle.getMessage("ComboBoxPulseTime0point4");
2049        availableDelayTimes[3] = Bundle.getMessage("ComboBoxPulseTime0point6");
2050        availableDelayTimes[4] = Bundle.getMessage("ComboBoxPulseTime0point8");
2051        availableDelayTimes[5] = Bundle.getMessage("ComboBoxPulseTime1point0");
2052        availableDelayTimes[6] = Bundle.getMessage("ComboBoxPulseTime1point2");
2053        availableDelayTimes[7] = Bundle.getMessage("ComboBoxPulseTime1point4");
2054        availableDelayTimes[8] = Bundle.getMessage("ComboBoxPulseTime1point6");
2055        availableDelayTimes[9] = Bundle.getMessage("ComboBoxPulseTime1point8");
2056        availableDelayTimes[10] = Bundle.getMessage("ComboBoxPulseTime2point0");
2057        availableDelayTimes[11] = Bundle.getMessage("ComboBoxPulseTime2point2");
2058        availableDelayTimes[12] = Bundle.getMessage("ComboBoxPulseTime2point4");
2059        availableDelayTimes[13] = Bundle.getMessage("ComboBoxPulseTime2point6");
2060        availableDelayTimes[14] = Bundle.getMessage("ComboBoxPulseTime2point8");
2061        availableDelayTimes[15] = Bundle.getMessage("ComboBoxPulseTime3point0");
2062
2063        delayTimeLabel = new JLabel(Bundle.getMessage("LabelPulseTimeout")); // opSws 2-5
2064        delayTime = new JComboBox<>(availableDelayTimes);
2065        delayTime.setToolTipText(Bundle.getMessage("ToolTipPulseTimeout"));
2066        delayTime.setName("2345"); // NOI18N
2067        delayTime.addActionListener(basicConfigChangeActionListener);
2068
2069        String[] initialOutputStates = {Bundle.getMessage("ComboBoxOutputPowerupType0"),
2070            Bundle.getMessage("ComboBoxOutputPowerupType1")};
2071        outputStatesLabel = new JLabel(Bundle.getMessage("LabelPowerUpOutputActivity"));
2072        outputStates = new JComboBox<>(initialOutputStates);    // opsw 6
2073        outputStates.setToolTipText(Bundle.getMessage("ToolTipOutputStates"));
2074        outputStates.setName("6"); // NOI18N
2075        outputStates.addActionListener(basicConfigChangeActionListener);
2076
2077        String[] startupDelays = {Bundle.getMessage("ComboBoxOutputPowerupDelayType0"),
2078            Bundle.getMessage("ComboBoxOutputPowerupDelayType1")};
2079        startupDelayLabel = new JLabel(Bundle.getMessage("LabelInitialPowerUpDelay"));
2080        startupDelay = new JComboBox<>(startupDelays);  // opsw 8
2081        startupDelay.setToolTipText(Bundle.getMessage("ToolTipStartupDelay"));
2082        startupDelay.setName("8"); // NOI18N
2083        startupDelay.addActionListener(basicConfigChangeActionListener);
2084
2085        String[] staticOutputShutoffs = {Bundle.getMessage("ComboBoxOutputPowerManagementType0"),
2086            Bundle.getMessage("ComboBoxOutputPowerManagementType1")};
2087        staticOutputShutoffLabel = new JLabel(Bundle.getMessage("LabelOutputPowerManagementStyle"));
2088        staticOutputShutoff = new JComboBox<>(staticOutputShutoffs); // opSw 9
2089        staticOutputShutoff.setToolTipText(Bundle.getMessage("ToolTipLabelOutputPowerManagementStyle"));
2090        staticOutputShutoff.setName("9"); // NOI18N
2091        staticOutputShutoff.addActionListener(basicConfigChangeActionListener);
2092
2093        // command sources
2094        String[] commandTypes = {Bundle.getMessage("ComboBoxCommandsRecognizedFromType0"),
2095            Bundle.getMessage("ComboBoxCommandsRecognizedFromType1")};
2096        commandTypeLabel = new JLabel(Bundle.getMessage("LabelAcceptedSwitchCommandTypes"));
2097        commandType = new JComboBox<>(commandTypes); //opSw 10
2098        commandType.setToolTipText(Bundle.getMessage("ToolTipLabelAcceptedSwitchCommandTypes"));
2099        commandType.setName("10"); // NOI18N
2100        commandType.addActionListener(basicConfigChangeActionListener);
2101
2102        String[] commandSources = {Bundle.getMessage("ComboBoxCommandSourceType0"),
2103            Bundle.getMessage("ComboBoxCommandSourceType1")};
2104        commandSourceLabel = new JLabel(Bundle.getMessage("LabelAcceptSwitchCommandsFrom"));
2105        commandSource = new JComboBox<>(commandSources); // opSw14
2106        commandSource.setToolTipText(Bundle.getMessage("ToolTipCommandSource"));
2107        commandSource.setName("14"); // NOI18N
2108        commandSource.addActionListener(basicConfigChangeActionListener);
2109
2110        // Crossbuck Flasher controls
2111        output1CrossbuckFlasherCheckBox = new JCheckBox(Bundle.getMessage("CheckBoxOutputXCrossbuck", 1));
2112        output1CrossbuckFlasherCheckBox.setToolTipText(Bundle.getMessage("ToolTipCheckBoxOutput1Crossbuck"));
2113        output1CrossbuckFlasherCheckBox.setName("17"); // NOI18N
2114        output1CrossbuckFlasherCheckBox.addActionListener(basicConfigChangeActionListener);
2115        // output 2
2116        output2CrossbuckFlasherCheckBox = new JCheckBox(Bundle.getMessage("CheckBoxOutputXCrossbuck", 2));
2117        output2CrossbuckFlasherCheckBox.setToolTipText(Bundle.getMessage("ToolTipCheckBoxOutput2Crossbuck"));
2118        output2CrossbuckFlasherCheckBox.setName("18"); // NOI18N
2119        output2CrossbuckFlasherCheckBox.addActionListener(basicConfigChangeActionListener);
2120        // output 3
2121        output3CrossbuckFlasherCheckBox = new JCheckBox(Bundle.getMessage("CheckBoxOutputXCrossbuck", 3));
2122        output3CrossbuckFlasherCheckBox.setToolTipText(Bundle.getMessage("ToolTipCheckBoxOutput3Crossbuck"));
2123        output3CrossbuckFlasherCheckBox.setName("19"); // NOI18N
2124        output3CrossbuckFlasherCheckBox.addActionListener(basicConfigChangeActionListener);
2125        // output 4
2126        output4CrossbuckFlasherCheckBox = new JCheckBox(Bundle.getMessage("CheckBoxOutputXCrossbuck", 4));
2127        output4CrossbuckFlasherCheckBox.setToolTipText(Bundle.getMessage("ToolTipCheckBoxOutput4Crossbuck"));
2128        output4CrossbuckFlasherCheckBox.setName("20"); // NOI18N
2129        output4CrossbuckFlasherCheckBox.addActionListener(basicConfigChangeActionListener);
2130
2131        // DS64 routes
2132        String[] routesControls = {Bundle.getMessage("ComboBoxEntryRoutesOption0"),
2133            Bundle.getMessage("ComboBoxEntryRoutesOption1"),
2134            Bundle.getMessage("ComboBoxEntryRoutesOption2"),
2135            Bundle.getMessage("ComboBoxEntryRoutesOption3")};
2136        routesControlLabel = new JLabel(Bundle.getMessage("LabelTriggerDs64Routes"));
2137        routesControl = new JComboBox<>(routesControls);    // opSws 11, 16
2138        routesControl.setToolTipText(Bundle.getMessage("ToolTipLabelRouteControlOptions"));
2139        routesControl.setName("1116"); // NOI18N
2140        routesControl.addActionListener(basicConfigChangeActionListener);
2141
2142        // local input controls
2143        String[] localControlOfOutputsStyles = {
2144            Bundle.getMessage("ComboBoxInputsControlOutputsType0"),
2145            Bundle.getMessage("ComboBoxInputsControlOutputsType1"),
2146            Bundle.getMessage("ComboBoxInputsControlOutputsType2"),
2147            Bundle.getMessage("comboboxInputsControlOutputsType3")};
2148        localControlOfOutputsStyleLabel = new JLabel(Bundle.getMessage("LabelLocalInputsControlOutputs"));
2149        localControlOfOutputsStyle = new JComboBox<>(localControlOfOutputsStyles); // opSw12
2150        localControlOfOutputsStyle.setToolTipText(Bundle.getMessage("ToolTipLocalInputsControl"));
2151        localControlOfOutputsStyle.setName("1215"); // NOI18N
2152        localControlOfOutputsStyle.addActionListener(basicConfigChangeActionListener);
2153
2154        String[] sensorMessageTriggers = {Bundle.getMessage("ComboBoxInputsCauseMessagesType0"),
2155            Bundle.getMessage("ComboBoxInputsCauseMessagesType1")};
2156        sensorMessageTriggerLabel = new JLabel(Bundle.getMessage("LabelBetweenForMessageTypeSent"));
2157        sensorMessageTrigger = new JComboBox<>(sensorMessageTriggers); // opSw13
2158        sensorMessageTrigger.setToolTipText(Bundle.getMessage("ToolTipSensorMessageTrigger"));
2159        sensorMessageTrigger.setName("13"); // NOI18N
2160        sensorMessageTrigger.addActionListener(basicConfigChangeActionListener);
2161
2162        String[] localSensorTypes = {Bundle.getMessage("ComboBoxSensorMessageTypeSentType0"),
2163            Bundle.getMessage("ComboBoxSensorMessageTypeSentType1")};
2164        localSensorType = new JComboBox<>(localSensorTypes); // opSw21
2165        localSensorType.setToolTipText(Bundle.getMessage("ToolTipLocalSensorsType"));
2166        localSensorType.setName("21"); // NOI18N
2167        localSensorType.addActionListener(basicConfigChangeActionListener);
2168
2169        factoryResetButton = new JToggleButton(Bundle.getMessage("ButtonResetToFactoryDefault"));
2170        factoryResetButton.setToolTipText(Bundle.getMessage("ToolTipButtonResetToFactoryDefault"));
2171        factoryResetButton.addActionListener(
2172                event -> {
2173                    readAllButton.setEnabled(false);
2174                    writeAllButton.setEnabled(false);
2175                    resetRouteButton.setEnabled(false);
2176                    boardFactoryReset();
2177                }
2178        );
2179        routesTabbedPane = new JTabbedPane();
2180
2181        routePanel = new JPanel[9];
2182        routeTop = new SimpleTurnoutStateEntry[9];
2183        routeA2 = new SimpleTurnoutStateEntry[9];
2184        routeA3 = new SimpleTurnoutStateEntry[9];
2185        routeA4 = new SimpleTurnoutStateEntry[9];
2186        routeA5 = new SimpleTurnoutStateEntry[9];
2187        routeA6 = new SimpleTurnoutStateEntry[9];
2188        routeA7 = new SimpleTurnoutStateEntry[9];
2189        routeA8 = new SimpleTurnoutStateEntry[9];
2190
2191        resetRouteButton = new JToggleButton(Bundle.getMessage("ButtonResetRoute"));
2192        resetRouteButton.setToolTipText(Bundle.getMessage("ToolTipButtonResetRoute"));
2193        resetRouteButton.setEnabled(false);
2194        resetRouteButton.setVisible(false);
2195
2196        JPanel addressingPanel = provideAddressing(" "); // create read/write buttons, address
2197        readAllButton.setPreferredSize(null);
2198        readAllButton.setText(Bundle.getMessage("ButtonReadFullSheet"));
2199        readAllButton.setToolTipText(Bundle.getMessage("ToolTipButtonReadFullSheet"));
2200
2201        writeAllButton.setPreferredSize(null);
2202        writeAllButton.setText(Bundle.getMessage("ButtonWriteFullSheet"));
2203        writeAllButton.setToolTipText(Bundle.getMessage("ToolTipButtonWriteFullSheet"));
2204
2205        // make both buttons a little bit bigger, with identical (preferred) sizes
2206        // (width increased because some computers/displays trim the button text)
2207        java.awt.Dimension d = writeAllButton.getPreferredSize();
2208        int w = d.width;
2209        d = readAllButton.getPreferredSize();
2210        if (d.width > w) {
2211            w = d.width;
2212        }
2213        writeAllButton.setPreferredSize(new java.awt.Dimension((int) (w * 1.1), d.height));
2214        readAllButton.setPreferredSize(new java.awt.Dimension((int) (w * 1.1), d.height));
2215
2216        addressingPanel.add(resetRouteButton);
2217        int indexOfTargetBoardAddress = 0;
2218
2219        addressComboBox = new JComboBox<>();
2220        for (Integer index = 0; index < boardNumsEntryValue.size(); ++index) {
2221            if (boardNumsEntryValue.get(index) == origAccessBoardNum) {
2222                origAccessBoardNum = -1;
2223                indexOfTargetBoardAddress = index;
2224            }
2225            addressComboBox.addItem(boardNumsEntryValue.get(index));
2226        }
2227
2228        addressComboBox.setSelectedIndex(indexOfTargetBoardAddress);
2229        addressingPanel.add(addressComboBox, 1);
2230        addressingPanel.getComponent(2).setVisible(false);
2231        addressComboBox.setEditable(true);
2232
2233        appendLine(addressingPanel);  // add read/write buttons, address
2234
2235        generalTabbedPane = new JTabbedPane();
2236        generalPanel = new JPanel();
2237        generalPanel.setLayout(new BoxLayout(generalPanel, BoxLayout.Y_AXIS));
2238        generalPanel.setName("Basic Settings"); // NOI18N
2239
2240        JPanel allOutputControls = new JPanel();
2241        allOutputControls.setLayout(new BoxLayout(allOutputControls, BoxLayout.Y_AXIS));
2242        javax.swing.border.TitledBorder allOutputControlsTitleBorder;
2243        javax.swing.border.Border blackline;
2244        blackline = javax.swing.BorderFactory.createLineBorder(java.awt.Color.black);
2245        allOutputControlsTitleBorder = javax.swing.BorderFactory.createTitledBorder(blackline,
2246                Bundle.getMessage("TitledBorderLabelOutputControls"));
2247        allOutputControls.setBorder(allOutputControlsTitleBorder);
2248
2249        JPanel outputTypePanel = new JPanel();
2250        outputTypePanel.setLayout(new FlowLayout());
2251        outputTypePanel.add(outputTypeLabel);
2252        outputTypePanel.add(outputType);
2253        allOutputControls.add(outputTypePanel);
2254
2255        JPanel delayTimePanel = new JPanel();
2256        delayTimePanel.setLayout(new FlowLayout());
2257        delayTimePanel.add(delayTimeLabel);
2258        delayTimePanel.add(delayTime);
2259        allOutputControls.add(delayTimePanel);
2260
2261        JPanel outputStatePanel = new JPanel();
2262        outputStatePanel.setLayout(new FlowLayout());
2263        outputStatePanel.add(outputStatesLabel);
2264        outputStatePanel.add(outputStates);
2265        allOutputControls.add(outputStatePanel);
2266
2267        JPanel startupDelayPanel = new JPanel();
2268        startupDelayPanel.setLayout(new FlowLayout());
2269        startupDelayPanel.add(startupDelayLabel);
2270        startupDelayPanel.add(startupDelay);
2271        allOutputControls.add(startupDelayPanel);
2272
2273        JPanel staticOutputShutoffPanel = new JPanel();
2274        staticOutputShutoffPanel.setLayout(new FlowLayout());
2275        staticOutputShutoffPanel.add(staticOutputShutoffLabel);
2276        staticOutputShutoffPanel.add(staticOutputShutoff);
2277        allOutputControls.add(staticOutputShutoffPanel);
2278
2279        JPanel crossingGateControls = new JPanel(new java.awt.GridLayout(2, 2));
2280        crossingGateControls.add(output1CrossbuckFlasherCheckBox);
2281        crossingGateControls.add(output3CrossbuckFlasherCheckBox); // display output 3 box to the right of output 1 box
2282        crossingGateControls.add(output2CrossbuckFlasherCheckBox); // display output 2 box below output 1 box
2283        crossingGateControls.add(output4CrossbuckFlasherCheckBox);
2284        allOutputControls.add(crossingGateControls);
2285
2286        generalPanel.add(allOutputControls);
2287
2288        // command sources
2289        JPanel ds64CommandSourcesPanel = new JPanel();
2290        ds64CommandSourcesPanel.setLayout(new BoxLayout(ds64CommandSourcesPanel, BoxLayout.Y_AXIS));
2291        javax.swing.border.TitledBorder ds64CommandSourcesTitleBorder;
2292        ds64CommandSourcesTitleBorder = javax.swing.BorderFactory.createTitledBorder(blackline,
2293                Bundle.getMessage("TitledBorderLabelCommandSources"));
2294        ds64CommandSourcesPanel.setBorder(ds64CommandSourcesTitleBorder);
2295
2296        JPanel commandTypePanel = new JPanel();
2297        commandTypePanel.setLayout(new FlowLayout());
2298        commandTypePanel.add(commandTypeLabel);
2299        commandTypePanel.add(commandType);
2300        ds64CommandSourcesPanel.add(commandTypePanel);
2301
2302        JPanel commandSourcePanel = new JPanel();
2303        commandSourcePanel.setLayout(new FlowLayout());
2304        commandSourcePanel.add(commandSourceLabel);
2305        commandSourcePanel.add(commandSource);
2306        ds64CommandSourcesPanel.add(commandSourcePanel);
2307
2308        generalPanel.add(ds64CommandSourcesPanel);
2309
2310        // DS64 routes
2311        JPanel localRoutesPanel = new JPanel();
2312        localRoutesPanel.setLayout(new BoxLayout(localRoutesPanel, BoxLayout.Y_AXIS));
2313        javax.swing.border.TitledBorder localRoutesTitleBorder;
2314        localRoutesTitleBorder = javax.swing.BorderFactory.createTitledBorder(blackline,
2315                Bundle.getMessage("TitledBorderLabelRoutes"));
2316        localRoutesPanel.setBorder(localRoutesTitleBorder);
2317
2318        JPanel routesControlPanel = new JPanel();
2319        routesControlPanel.setLayout(new FlowLayout());
2320        routesControlPanel.add(routesControlLabel);
2321        routesControlPanel.add(routesControl);
2322        localRoutesPanel.add(routesControlPanel);
2323
2324        generalPanel.add(localRoutesPanel);
2325
2326        // local input controls
2327        localInputControlsPanel = new JPanel();
2328        localInputControlsPanel.setLayout(new BoxLayout(localInputControlsPanel, BoxLayout.Y_AXIS));
2329        javax.swing.border.TitledBorder localInputControlsTitleBorder;
2330        localInputControlsTitleBorder = javax.swing.BorderFactory.createTitledBorder(blackline,
2331                Bundle.getMessage("TitledBorderLabelLocalInputControls"));
2332        localInputControlsPanel.setBorder(localInputControlsTitleBorder);
2333
2334        JPanel localControlOfOutputsStylePanel = new JPanel(new FlowLayout());
2335        localControlOfOutputsStylePanel.add(localControlOfOutputsStyleLabel);
2336        localControlOfOutputsStylePanel.add(localControlOfOutputsStyle);
2337        localInputControlsPanel.add(localControlOfOutputsStylePanel);
2338
2339        sensorMessageTriggerPanel = new JPanel(new FlowLayout());
2340        sensorMessageTriggerPanel.add(localSensorType);
2341        sensorMessageTriggerPanel.add(sensorMessageTriggerLabel);
2342        sensorMessageTriggerPanel.add(sensorMessageTrigger);
2343        localInputControlsPanel.add(sensorMessageTriggerPanel);
2344
2345        generalPanel.add(localInputControlsPanel);
2346
2347        generalPanel.add(new JSeparator());
2348        JPanel factoryResetButtonPanel = new JPanel();
2349        factoryResetButtonPanel.add(factoryResetButton);
2350        generalPanel.add(factoryResetButtonPanel);
2351
2352        generalTabbedPane.addTab(Bundle.getMessage("TabTextBasicSettings"), null,
2353                generalPanel, Bundle.getMessage("TabToolTipBasicSettings"));
2354
2355        // opsws panel
2356        opswsPanel = new JPanel();
2357
2358        opswsValues = new JPanel();
2359        opswsValues.setLayout(new BoxLayout(opswsValues, BoxLayout.Y_AXIS));
2360        javax.swing.border.TitledBorder opswsValuesTitleBorder;
2361        opswsValuesTitleBorder = javax.swing.BorderFactory.createTitledBorder(blackline,
2362                Bundle.getMessage("TitledBorderLabelOpSws"));
2363        opswsValues.setBorder(opswsValuesTitleBorder);
2364
2365        opswsPanel.setLayout(new BoxLayout(opswsPanel, BoxLayout.Y_AXIS));
2366        JPanel innerPanel;
2367        ButtonGroup[] g = new ButtonGroup[22];
2368        opswThrown = new JRadioButtonWithInteger[22];
2369        opswClosed = new JRadioButtonWithInteger[22];
2370        for (int i = 1; i <= 21; i++) {
2371            if (i != 7) {
2372                log.debug("Creating entry for OpSw {}", i);
2373                innerPanel = new JPanel(new FlowLayout());
2374                innerPanel.add(new JLabel("OpSw " + i)); // NOI18N
2375                opswThrown[i] = new JRadioButtonWithInteger(i, Bundle.getMessage("TurnoutStateThrown"));
2376                opswClosed[i] = new JRadioButtonWithInteger(i, Bundle.getMessage("TurnoutStateClosed"));
2377                g[i] = new ButtonGroup();
2378                g[i].add(opswThrown[i]);
2379                g[i].add(opswClosed[i]);
2380                innerPanel.add(opswThrown[i]);
2381                innerPanel.add(opswClosed[i]);
2382                opswsPanel.add(innerPanel);
2383//                opswsPanel.add(new JSeparator());
2384                opswThrown[i].addItemListener(event -> {
2385                    if (event.getSource().getClass() == JRadioButtonWithInteger.class) {
2386                        JRadioButtonWithInteger source = ((JRadioButtonWithInteger) (event.getSource()));
2387                        int ind = source.index;
2388                        boolean st = (event.getStateChange() == ItemEvent.DESELECTED);
2389                        log.debug("ItemEventListener Opsw values: {} thrown radio button event: {} {}.", ind, st, st ? "Closed" : "Thrown"); // NOI18N
2390                        opsw[ind] = st;
2391                        copyOpswToBasic();
2392                    }
2393                });
2394            }
2395        }
2396        opswsValues.add(opswsPanel);
2397        opswsScrollPane = new JScrollPane(opswsValues);
2398        opswsScrollPane.setPreferredSize(new java.awt.Dimension(180, 200));
2399        opswsScrollPane.setName("Simple OpSws"); // NOI18N
2400
2401        generalTabbedPane.addTab(Bundle.getMessage("TabTextOpSwValues"), null,
2402                opswsScrollPane, Bundle.getMessage("TabToolTipOpSwValues"));
2403
2404        outputAddrsPanel = new JPanel();
2405        outputAddrsPanel.setLayout(new BoxLayout(outputAddrsPanel, BoxLayout.Y_AXIS));
2406
2407        JPanel p = new JPanel();
2408
2409        p.setLayout(new FlowLayout());
2410        p.add(new JLabel(Bundle.getMessage("LabelCautionReadingWritingCanCauseOutputChanges")));
2411        outputAddrsPanel.add(p);
2412
2413        p = new JPanel();
2414        p.setLayout(new FlowLayout());
2415        p.add(new JLabel(Bundle.getMessage("LabelTextOutputX", 1)));
2416        outAddr1 = new ValidatedTextField(5, false, 1, 2048, Bundle.getMessage("ErrorTextNonBlankAddressInvalid"));
2417        outState1 = new JLabel(Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateUnknown")));
2418        p.add(outAddr1);
2419        p.add(outState1);
2420        outputAddrsPanel.add(p);
2421
2422        p = new JPanel();
2423        p.setLayout(new FlowLayout());
2424        p.add(new JLabel(Bundle.getMessage("LabelTextOutputX", 2)));
2425        outAddr2 = new ValidatedTextField(5, false, 1, 2048, Bundle.getMessage("ErrorTextNonBlankAddressInvalid"));
2426        outState2 = new JLabel(Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateUnknown")));
2427        p.add(outAddr2);
2428        p.add(outState2);
2429        outputAddrsPanel.add(p);
2430
2431        p = new JPanel();
2432        p.setLayout(new FlowLayout());
2433        p.add(new JLabel(Bundle.getMessage("LabelTextOutputX", 3)));
2434        outAddr3 = new ValidatedTextField(5, false, 1, 2048, Bundle.getMessage("ErrorTextNonBlankAddressInvalid"));
2435        outState3 = new JLabel(Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateUnknown")));
2436        p.add(outAddr3);
2437        p.add(outState3);
2438        outputAddrsPanel.add(p);
2439
2440        p = new JPanel();
2441        p.setLayout(new FlowLayout());
2442        p.add(new JLabel(Bundle.getMessage("LabelTextOutputX", 4)));
2443        outAddr4 = new ValidatedTextField(5, false, 1, 2048, Bundle.getMessage("ErrorTextNonBlankAddressInvalid"));
2444        outState4 = new JLabel(Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateUnknown")));
2445        p.add(outAddr4);
2446        p.add(outState4);
2447        outputAddrsPanel.add(p);
2448        outputAddrsPanel.add(new JSeparator());
2449
2450        p = new JPanel();
2451        p.setLayout(new FlowLayout());
2452        p.add(new JLabel(Bundle.getMessage("LabelOutputsTabSensorNotes")));
2453        outputAddrsPanel.add(p);
2454
2455        generalTabbedPane.addTab(Bundle.getMessage("TabTextOutputAddrs"), null,
2456                outputAddrsPanel, Bundle.getMessage("TabToolTipOutputAddrs"));
2457
2458        routePanel[0] = new JPanel();
2459
2460        routesTabbedPane.setTabPlacement(JTabbedPane.LEFT);
2461        // create route panels (one tab each for each of 8 routes)
2462        for (int i = 1; i <= 8; ++i) {
2463            routePanel[i] = new JPanel();
2464            routePanel[i].setLayout(new BoxLayout(routePanel[i], BoxLayout.Y_AXIS));
2465
2466            routePanel[i].add(new JLabel(Bundle.getMessage("TabTextSpecificRoute",
2467                    Integer.toString(i))));
2468            routePanel[i].add(new JSeparator());
2469            JPanel q = new JPanel(new FlowLayout());
2470            q.add(new JLabel(Bundle.getMessage("LabelCautionReadingWritingCanCauseOutputChanges")));
2471            routePanel[i].add(q);
2472            routePanel[i].add(new JSeparator());
2473            routeTop[i] = new SimpleTurnoutStateEntry(2048, false, true);
2474            routeA2[i] = new SimpleTurnoutStateEntry(2048, false, true);
2475            routeA3[i] = new SimpleTurnoutStateEntry(2048, false, true);
2476            routeA4[i] = new SimpleTurnoutStateEntry(2048, false, true);
2477            routeA5[i] = new SimpleTurnoutStateEntry(2048, false, true);
2478            routeA6[i] = new SimpleTurnoutStateEntry(2048, false, true);
2479            routeA7[i] = new SimpleTurnoutStateEntry(2048, false, true);
2480            routeA8[i] = new SimpleTurnoutStateEntry(2048, false, true);
2481
2482            routePanel[i].add(routeTop[i].createEntryPanel(Bundle.getMessage("LabelTextRouteXSpecificTurnout",
2483                    Bundle.getMessage("LabelTextRouteXTopTurnout"))));
2484            routePanel[i].add(routeA2[i].createEntryPanel(Bundle.getMessage("LabelTextRouteXSpecificTurnout", 2)));
2485            routePanel[i].add(routeA3[i].createEntryPanel(Bundle.getMessage("LabelTextRouteXSpecificTurnout", 3)));
2486            routePanel[i].add(routeA4[i].createEntryPanel(Bundle.getMessage("LabelTextRouteXSpecificTurnout", 4)));
2487            routePanel[i].add(routeA5[i].createEntryPanel(Bundle.getMessage("LabelTextRouteXSpecificTurnout", 5)));
2488            routePanel[i].add(routeA6[i].createEntryPanel(Bundle.getMessage("LabelTextRouteXSpecificTurnout", 6)));
2489            routePanel[i].add(routeA7[i].createEntryPanel(Bundle.getMessage("LabelTextRouteXSpecificTurnout", 7)));
2490            routePanel[i].add(routeA8[i].createEntryPanel(Bundle.getMessage("LabelTextRouteXSpecificTurnout", 8)));
2491
2492            routesTabbedPane.addTab(
2493                    Bundle.getMessage("TabTextSpecificRoute", Integer.toString(i)),
2494                    null,
2495                    routePanel[i],
2496                    Bundle.getMessage("TabToolTipSpecificRoute", Integer.toString(i))
2497            );
2498        }
2499
2500        generalTabbedPane.addTab(Bundle.getMessage("TabTextRoutes"),
2501                null, routesTabbedPane,
2502                Bundle.getMessage("ToolTipTabTextRoutes"));
2503        resetRouteButton.addActionListener(
2504                event -> {
2505                    readAllButton.setEnabled(false);
2506                    writeAllButton.setEnabled(false);
2507                    factoryResetButton.setEnabled(false);
2508
2509                    Integer routeNumber = 0;
2510                    if (((JTabbedPane) generalTabbedPane.getSelectedComponent()) != routesTabbedPane) {
2511                        return;
2512                    }
2513                    if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[1]) {
2514                        routeNumber = 1;
2515                    } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[2]) {
2516                        routeNumber = 2;
2517                    } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[3]) {
2518                        routeNumber = 3;
2519                    } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[4]) {
2520                        routeNumber = 4;
2521                    } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[5]) {
2522                        routeNumber = 5;
2523                    } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[6]) {
2524                        routeNumber = 6;
2525                    } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[7]) {
2526                        routeNumber = 7;
2527                    } else if (((JPanel) routesTabbedPane.getSelectedComponent()) == routePanel[8]) {
2528                        routeNumber = 8;
2529                    }
2530                    if (routeNumber != 0) {
2531                        // before proceeding, make sure that the user really wants to go forward
2532                        Object[] dialogBoxButtonOptions = {
2533                            Bundle.getMessage("ButtonResetRouteN", routeNumber),
2534                            Bundle.getMessage("ButtonCancel")};
2535                        int userReply = JmriJOptionPane.showOptionDialog(this,
2536                                Bundle.getMessage("DialogTextClearRouteWarning", routeNumber),
2537                                Bundle.getMessage("WarningTitle"),
2538                                JmriJOptionPane.YES_NO_OPTION, JmriJOptionPane.QUESTION_MESSAGE,
2539                                null, dialogBoxButtonOptions, dialogBoxButtonOptions[1]);
2540                        if ( userReply != JmriJOptionPane.YES_OPTION ) {
2541                            resetRouteButton.setSelected(false);
2542                            return; // compare only to exactly the value for executing the "clear route" operation!
2543                        }
2544
2545                        resetRouteOperation(routeNumber);
2546                    }
2547                    readAllButton.setEnabled(true);
2548                    writeAllButton.setEnabled(true);
2549                    factoryResetButton.setEnabled(true);
2550                    resetRouteButton.setSelected(false);
2551                }
2552        );
2553
2554        appendLine(generalTabbedPane);
2555        JPanel statusPanel = new JPanel();
2556        setStatus(" ");
2557        statusPanel.add(new JSeparator());
2558        statusPanel.add(provideStatusLine());
2559        statusPanel.add(new JSeparator());
2560        appendLine(statusPanel);
2561
2562        setTypeWord(0x73);  // configure DS64 message type
2563        opsw[7] = false;
2564        operationType = OpSwOpType.BasicsRead;
2565
2566        routesTabbedPane.addChangeListener(new ChangeListener() {
2567            // This method is called whenever the selected tab changes
2568
2569            String route1TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(1));
2570            String route2TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(2));
2571            String route3TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(3));
2572            String route4TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(4));
2573            String route5TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(5));
2574            String route6TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(6));
2575            String route7TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(7));
2576            String route8TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(8));
2577
2578            @Override
2579            public void stateChanged(ChangeEvent evt) {
2580                unhighlightAllBasicOpSws();
2581                unhighlightAllOutputEntryFields();
2582                unhighlightAllRouteEntryFields();
2583
2584                String activeTabTitle = routesTabbedPane.getTitleAt(routesTabbedPane.getSelectedIndex());
2585
2586                if ((activeTabTitle.equals(route1TabText))
2587                        || (activeTabTitle.equals(route2TabText))
2588                        || (activeTabTitle.equals(route3TabText))
2589                        || (activeTabTitle.equals(route4TabText))
2590                        || (activeTabTitle.equals(route5TabText))
2591                        || (activeTabTitle.equals(route6TabText))
2592                        || (activeTabTitle.equals(route7TabText))
2593                        || (activeTabTitle.equals(route8TabText))) {
2594                    resetRouteButton.setVisible(true);
2595                    resetRouteButton.setEnabled(true);
2596                    resetRouteButton.updateUI();
2597                    readAllButton.setSelected(false);
2598                    readAllButton.updateUI();
2599                    updateUI();
2600                }
2601            }
2602
2603        });
2604
2605        generalTabbedPane.addChangeListener(new ChangeListener() {
2606            // This method is called whenever the selected tab changes
2607            String route1TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(1));
2608            String route2TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(2));
2609            String route3TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(3));
2610            String route4TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(4));
2611            String route5TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(5));
2612            String route6TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(6));
2613            String route7TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(7));
2614            String route8TabText = Bundle.getMessage("TabTextSpecificRoute", Integer.toString(8));
2615            String outputsTabText = Bundle.getMessage("TabTextOutputAddrs");
2616
2617            @Override
2618            public void stateChanged(ChangeEvent evt) {
2619                String activeTabTitle;
2620                unhighlightAllBasicOpSws();
2621                unhighlightAllOutputEntryFields();
2622                unhighlightAllRouteEntryFields();
2623
2624                activeTabTitle = generalTabbedPane.getTitleAt(generalTabbedPane.getSelectedIndex());
2625                JTabbedPane pane = (JTabbedPane) evt.getSource();
2626
2627                // Get current tab
2628                if ((activeTabTitle.equals(Bundle.getMessage("TabTextRoutes")))) {
2629                    activeTabTitle = routesTabbedPane.getTitleAt(routesTabbedPane.getSelectedIndex());
2630                    if ((activeTabTitle.equals(route1TabText))
2631                            || (activeTabTitle.equals(route2TabText))
2632                            || (activeTabTitle.equals(route3TabText))
2633                            || (activeTabTitle.equals(route4TabText))
2634                            || (activeTabTitle.equals(route5TabText))
2635                            || (activeTabTitle.equals(route6TabText))
2636                            || (activeTabTitle.equals(route7TabText))
2637                            || (activeTabTitle.equals(route8TabText))) {
2638                        resetRouteButton.setEnabled(true);
2639                        resetRouteButton.setVisible(true);
2640                        readAllButton.setEnabled(true);
2641                        writeAllButton.setSelected(false);
2642                        readAllButton.setSelected(false);
2643                        writeAllButton.setEnabled(true);
2644                        factoryResetButton.setEnabled(true);
2645                        routesTabbedPane.updateUI();
2646                        updateUI();
2647                    } else {
2648                        routesTabbedPane.setSelectedIndex(0);
2649                        routesTabbedPane.updateUI();
2650                    }
2651                } else if (activeTabTitle.equals(outputsTabText)) {
2652                    resetRouteButton.setEnabled(false);
2653                    resetRouteButton.setVisible(false);
2654                    readAllButton.setEnabled(true);
2655                    writeAllButton.setSelected(false);
2656                    readAllButton.setSelected(false);
2657                    writeAllButton.setEnabled(true);
2658                    readAllButton.updateUI();
2659                    updateUI();
2660                } else {
2661                    readAllButton.setEnabled(true);
2662                    writeAllButton.setEnabled(true);
2663                    writeAllButton.setSelected(false);
2664                    readAllButton.setSelected(false);
2665                    resetRouteButton.setVisible(false);
2666                    resetRouteButton.setEnabled(false);
2667                    readAllButton.updateUI();
2668                    updateUI();
2669                }
2670                Container c = pane.getRootPane().getParent();
2671                c.setPreferredSize(null);
2672                if (c instanceof Window) {
2673                    ((Window) c).pack();
2674                }
2675            }
2676
2677        });
2678
2679        responseTimer.addActionListener(routeResetResponseTimerListener);
2680        commandType.setToolTipText(Bundle.getMessage("ToolTipLabelAcceptedSwitchCommandTypes"));
2681        updateBasicOpSwTab();
2682
2683        panelToScroll();
2684
2685    }
2686
2687    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "Cannot catch an exception without grabbing the exception, but we don't do anything with the exception details.")
2688    private void updateGuiFromOpSws49_64() {
2689        Integer readValue;
2690        boolean isUsed = true;
2691
2692        readValue = 0;
2693        for (int i = 60; i >= 49; i--) {
2694            if (i != 56) {
2695                readValue = (readValue << 1) + (opsw[i] ? 1 : 0);
2696            }
2697        }
2698        readValue++; // account for physical/user numbering difference
2699
2700        String readValueString = readValue.toString();
2701        if ((opsw[63] == true) && (opsw[64] == true)) {
2702            readValueString = "";
2703            isUsed = false;
2704        }
2705        boolean direction = opsw[62];
2706
2707        switch (indexToRead) {
2708            case 0: {
2709                // have read value for output2 - update local storage
2710                outAddr2.setText(Integer.toString(readValue));
2711                outAddr2.setLastQueriedValue(outAddr2.getText());
2712                outState2.setText(direction
2713                        ? Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateClosed"))
2714                        : Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateThrown")));
2715                break;
2716            }
2717            case 1: {
2718                // have read value for output4 - update local storage
2719                outAddr4.setText(Integer.toString(readValue));
2720                outAddr4.setLastQueriedValue(outAddr4.getText());
2721                outState4.setText(direction
2722                        ? Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateClosed"))
2723                        : Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateThrown")));
2724                break;
2725            }
2726            case 16:
2727            case 20:
2728            case 24:
2729            case 28:
2730            case 32:
2731            case 36:
2732            case 40:
2733            case 44: {
2734                // have a read value for Route n - update local storage
2735                Integer effectiveIndex = (indexToRead - 12) / 4;
2736
2737                if (isUsed == false) {
2738                    routeA2[effectiveIndex].setIsUnused();
2739                    routeA2[effectiveIndex].addressField.setText("");
2740                    routeA2[effectiveIndex].unusedRadioButton.setSelected(true);
2741                } else {
2742                    routeA2[effectiveIndex].setAddress(readValue);
2743                    routeA2[effectiveIndex].addressField.setText(readValueString);
2744                    routeA2[effectiveIndex].addressField.setLastQueriedValue(readValueString);
2745                    if (opsw[62] == true) {
2746                        routeA2[effectiveIndex].closedRadioButton.setSelected(true);
2747                    } else {
2748                        routeA2[effectiveIndex].thrownRadioButton.setSelected(true);
2749                    }
2750                }
2751                break;
2752            }
2753            case 17:
2754            case 21:
2755            case 25:
2756            case 29:
2757            case 33:
2758            case 37:
2759            case 41:
2760            case 45: {
2761                // have a read value for Route n - update local storage
2762                Integer effectiveIndex = (indexToRead - 13) / 4;
2763                if (isUsed == false) {
2764                    routeA4[effectiveIndex].setIsUnused();
2765                    routeA4[effectiveIndex].addressField.setText("");
2766                    routeA4[effectiveIndex].unusedRadioButton.setSelected(true);
2767                } else {
2768                    routeA4[effectiveIndex].setAddress(readValue);
2769                    routeA4[effectiveIndex].addressField.setText(readValueString);
2770                    routeA4[effectiveIndex].addressField.setLastQueriedValue(readValueString);
2771                    if (opsw[62] == true) {
2772                        routeA4[effectiveIndex].closedRadioButton.setSelected(true);
2773                    } else {
2774                        routeA4[effectiveIndex].thrownRadioButton.setSelected(true);
2775                    }
2776                }
2777                break;
2778            }
2779            case 18:
2780            case 22:
2781            case 26:
2782            case 30:
2783            case 34:
2784            case 38:
2785            case 42:
2786            case 46: {
2787                // have a read value for Route n - update local storage
2788                Integer effectiveIndex = (indexToRead - 14) / 4;
2789                if (isUsed == false) {
2790                    routeA6[effectiveIndex].setIsUnused();
2791                    routeA6[effectiveIndex].addressField.setText("");
2792                    routeA6[effectiveIndex].unusedRadioButton.setSelected(true);
2793                } else {
2794                    routeA6[effectiveIndex].setAddress(readValue);
2795                    routeA6[effectiveIndex].addressField.setText(readValueString);
2796                    routeA6[effectiveIndex].addressField.setLastQueriedValue(readValueString);
2797                    if (opsw[62] == true) {
2798                        routeA6[effectiveIndex].closedRadioButton.setSelected(true);
2799                    } else {
2800                        routeA6[effectiveIndex].thrownRadioButton.setSelected(true);
2801                    }
2802                }
2803                break;
2804            }
2805            case 19:
2806            case 23:
2807            case 27:
2808            case 31:
2809            case 35:
2810            case 39:
2811            case 43:
2812            case 47: {
2813                // have a read value for Route n - update local storage
2814                Integer effectiveIndex = (indexToRead - 15) / 4;
2815                if (isUsed == false) {
2816                    routeA8[effectiveIndex].setIsUnused();
2817                    routeA8[effectiveIndex].addressField.setText("");
2818                    routeA8[effectiveIndex].unusedRadioButton.setSelected(true);
2819                } else {
2820                    routeA8[effectiveIndex].setAddress(readValue);
2821                    routeA8[effectiveIndex].addressField.setText(readValueString);
2822                    routeA8[effectiveIndex].addressField.setLastQueriedValue(readValueString);
2823                    if (opsw[62] == true) {
2824                        routeA8[effectiveIndex].closedRadioButton.setSelected(true);
2825                    } else {
2826                        routeA8[effectiveIndex].thrownRadioButton.setSelected(true);
2827                    }
2828                }
2829                break;
2830            }
2831            default:
2832                break;
2833        }
2834    }
2835
2836    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "Cannot catch an exception without grabbing the exception, but we don't do anything with the exception details.")
2837    void updateGuiFromOpSws33_48() {
2838        Integer readValue;
2839        boolean isUsed = true;
2840
2841        readValue = 0;
2842        for (int i = 44; i >= 33; i--) {
2843            if (i != 40) {
2844                readValue = (readValue << 1) + (opsw[i] ? 1 : 0);
2845            }
2846        }
2847        readValue++; // account for physical/user numbering difference
2848
2849        String readValueString = readValue.toString();
2850        if ((opsw[47] == true) && (opsw[48] == true)) {
2851            readValueString = "";
2852            isUsed = false;
2853        }
2854        boolean direction = opsw[46];
2855
2856        switch (indexToRead) {
2857            case 0: {
2858                // have read value for output1 - update local storage
2859                outAddr1.setText(readValueString);
2860                outAddr1.setLastQueriedValue(readValueString);
2861                outAddr1.isValid();
2862                outState1.setText(direction
2863                        ? Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateClosed"))
2864                        : Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateThrown")));
2865                break;
2866            }
2867            case 1: {
2868                // have read value for output3 - update local storage
2869                outAddr3.setText(readValueString);
2870                outAddr3.setLastQueriedValue(readValueString);
2871                outState3.setText(direction
2872                        ? Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateClosed"))
2873                        : Bundle.getMessage("LabelTurnoutCurrentStateX", Bundle.getMessage("BeanStateThrown")));
2874                break;
2875            }
2876            case 16:
2877            case 20:
2878            case 24:
2879            case 28:
2880            case 32:
2881            case 36:
2882            case 40:
2883            case 44: {
2884                // have read value for Route n Top - update local storage
2885                Integer effectiveIndex = (indexToRead - 12) / 4;
2886                if (isUsed == false) {
2887                    routeTop[effectiveIndex].setIsUnused();
2888                    routeTop[effectiveIndex].addressField.setText("");
2889                    routeTop[effectiveIndex].unusedRadioButton.setSelected(true);
2890                } else {
2891                    routeTop[effectiveIndex].setAddress(readValue);
2892                    routeTop[effectiveIndex].addressField.setText(readValueString);
2893                    routeTop[effectiveIndex].addressField.setLastQueriedValue(readValueString);
2894                    if (opsw[46] == true) {
2895                        routeTop[effectiveIndex].closedRadioButton.setSelected(true);
2896                    } else {
2897                        routeTop[effectiveIndex].thrownRadioButton.setSelected(true);
2898                    }
2899                }
2900                break;
2901            }
2902            case 17:
2903            case 21:
2904            case 25:
2905            case 29:
2906            case 33:
2907            case 37:
2908            case 41:
2909            case 45: {
2910                // have a read value for Route n - update local storage
2911                Integer effectiveIndex = (indexToRead - 13) / 4;
2912                if (isUsed == false) {
2913                    routeA3[effectiveIndex].setIsUnused();
2914                    routeA3[effectiveIndex].addressField.setText("");
2915                    routeA3[effectiveIndex].unusedRadioButton.setSelected(true);
2916                } else {
2917                    routeA3[effectiveIndex].setAddress(readValue);
2918                    routeA3[effectiveIndex].addressField.setText(readValueString);
2919                    routeA3[effectiveIndex].addressField.setLastQueriedValue(readValueString);
2920                    if (opsw[46] == true) {
2921                        routeA3[effectiveIndex].closedRadioButton.setSelected(true);
2922                    } else {
2923                        routeA3[effectiveIndex].thrownRadioButton.setSelected(true);
2924                    }
2925                }
2926                break;
2927            }
2928            case 18:
2929            case 22:
2930            case 26:
2931            case 30:
2932            case 34:
2933            case 38:
2934            case 42:
2935            case 46: {
2936                // have a read value for Route n - update local storage
2937                Integer effectiveIndex = (indexToRead - 14) / 4;
2938                if (isUsed == false) {
2939                    routeA5[effectiveIndex].setIsUnused();
2940                    routeA5[effectiveIndex].addressField.setText("");
2941                    routeA5[effectiveIndex].unusedRadioButton.setSelected(true);
2942                } else {
2943                    routeA5[effectiveIndex].setAddress(readValue);
2944                    routeA5[effectiveIndex].addressField.setText(readValueString);
2945                    routeA5[effectiveIndex].addressField.setLastQueriedValue(readValueString);
2946                    if (opsw[46] == true) {
2947                        routeA5[effectiveIndex].closedRadioButton.setSelected(true);
2948                    } else {
2949                        routeA5[effectiveIndex].thrownRadioButton.setSelected(true);
2950                    }
2951                }
2952                break;
2953            }
2954            case 19:
2955            case 23:
2956            case 27:
2957            case 31:
2958            case 35:
2959            case 39:
2960            case 43:
2961            case 47: {
2962                // have a read value for Route n - update local storage
2963                Integer effectiveIndex = (indexToRead - 15) / 4;
2964                if (isUsed == false) {
2965                    routeA7[effectiveIndex].setIsUnused();
2966                    routeA7[effectiveIndex].addressField.setText("");
2967                    routeA7[effectiveIndex].unusedRadioButton.setSelected(true);
2968                } else {
2969                    routeA7[effectiveIndex].setAddress(readValue);
2970                    routeA7[effectiveIndex].addressField.setText(readValueString);
2971                    routeA7[effectiveIndex].addressField.setLastQueriedValue(readValueString);
2972                    if (opsw[46] == true) {
2973                        routeA7[effectiveIndex].closedRadioButton.setSelected(true);
2974                    } else {
2975                        routeA7[effectiveIndex].thrownRadioButton.setSelected(true);
2976                    }
2977                }
2978                break;
2979            }
2980            default: {
2981                break;
2982            }
2983        }
2984    }
2985
2986    private final ActionListener basicConfigChangeActionListener = new ActionListener() {
2987        @Override
2988        public void actionPerformed(ActionEvent e) {
2989            if (e.getSource().getClass() == JComboBox.class) {
2990                switch (((Component) e.getSource()).getName()) {
2991                    case "1": // NOI18N
2992                        opsw[1] = (outputType.getSelectedIndex() == 1);
2993                        updateGuiBasicOpSw(1);
2994                        break;
2995                    case "2345": // NOI18N
2996                        int selection = delayTime.getSelectedIndex();
2997                        opsw[2] = ((selection & 0x1) == 1);
2998                        opsw[3] = ((selection & 0x2) == 2);
2999                        opsw[4] = ((selection & 0x4) == 4);
3000                        opsw[5] = ((selection & 0x8) == 8);
3001                        updateGuiBasicOpSw(2);
3002                        updateGuiBasicOpSw(3);
3003                        updateGuiBasicOpSw(4);
3004                        updateGuiBasicOpSw(5);
3005                        break;
3006                    case "6": // NOI18N
3007                        opsw[6] = (outputStates.getSelectedIndex() == 1);
3008                        updateGuiBasicOpSw(6);
3009                        break;
3010                    case "8": // NOI18N
3011                        opsw[8] = startupDelay.getSelectedIndex() == 1;
3012                        updateGuiBasicOpSw(8);
3013                        break;
3014                    case "9": // NOI18N
3015                        opsw[9] = staticOutputShutoff.getSelectedIndex() == 1;
3016                        updateGuiBasicOpSw(9);
3017                        break;
3018                    case "10": // NOI18N
3019                        opsw[10] = commandType.getSelectedIndex() == 1;
3020                        updateGuiBasicOpSw(10);
3021                        break;
3022                    case "13": // NOI18N
3023                        opsw[13] = (sensorMessageTrigger.getSelectedIndex() == 1);
3024                        updateGuiBasicOpSw(13);
3025                        break;
3026                    case "14": // NOI18N
3027                        opsw[14] = commandSource.getSelectedIndex() == 1;
3028                        updateGuiBasicOpSw(14);
3029                        break;
3030                    case "21": // NOI18N
3031                        opsw[21] = localSensorType.getSelectedIndex() == 1;
3032                        updateGuiBasicOpSw(21);
3033                        break;
3034
3035                    case "1116": // NOI18N
3036                        opsw[11] = (routesControl.getSelectedIndex() == 1) || (routesControl.getSelectedIndex() == 3);
3037                        opsw[16] = routesControl.getSelectedIndex() >= 2;
3038                        updateGuiBasicOpSw(11);
3039                        updateGuiBasicOpSw(16);
3040                        break;
3041                    case "1215": // NOI18N
3042                        opsw[12] = (localControlOfOutputsStyle.getSelectedIndex() & 1) == 1;  //2 -> OpSw12="c"
3043                        opsw[15] = (localControlOfOutputsStyle.getSelectedIndex() >= 2);  //0 -> OpSw15="c"
3044                        updateGuiBasicOpSw(12);
3045                        updateGuiBasicOpSw(15);
3046                        break;
3047                    default:
3048                }
3049            } else if (e.getSource().getClass() == JCheckBox.class) {
3050                switch (((Component) e.getSource()).getName()) {
3051                    case "17": // NOI18N
3052                        opsw[17] = output1CrossbuckFlasherCheckBox.isSelected();
3053                        updateGuiBasicOpSw(17);
3054                        break;
3055                    case "18": // NOI18N
3056                        opsw[18] = output2CrossbuckFlasherCheckBox.isSelected();
3057                        updateGuiBasicOpSw(18);
3058                        break;
3059                    case "19": // NOI18N
3060                        opsw[19] = output3CrossbuckFlasherCheckBox.isSelected();
3061                        updateGuiBasicOpSw(19);
3062                        break;
3063                    case "20": // NOI18N
3064                        opsw[20] = output4CrossbuckFlasherCheckBox.isSelected();
3065                        updateGuiBasicOpSw(20);
3066                        break;
3067                    default:
3068                        break;
3069                }
3070
3071            }
3072        }
3073    };
3074
3075    private void updateBasicOpSwTab() {
3076        for (int i = 1; i <= 21; ++i) {
3077            if (i != 7) {
3078                opswThrown[i].setSelected(!opsw[i]);
3079                opswClosed[i].setSelected(opsw[i]);
3080            }
3081        }
3082    }
3083
3084    private class JRadioButtonWithInteger extends JRadioButton {
3085
3086        public int index;
3087
3088        JRadioButtonWithInteger(int i, String s) {
3089            super(s);
3090            index = i;
3091        }
3092    }
3093
3094    
3095    /**
3096     * Copy from the GUI OpSw tab to the GUI Basics tab
3097     */
3098    protected void copyOpswToBasic() {
3099        // copy over values from OpSw tab to the Basics tab
3100        outputType.setSelectedIndex(opsw[1]?1:0);
3101        
3102        delayTime.setSelectedIndex(
3103            (opsw[2]?1:0) + (opsw[3]?2:0) + (opsw[4]?4:0) + (opsw[5]?8:0));
3104        
3105        outputStates.setSelectedIndex(opsw[6]?1:0);
3106        isWritingResetOpSw = opsw[7];
3107        startupDelay.setSelectedIndex(opsw[8]?1:0);
3108        staticOutputShutoff.setSelectedIndex(opsw[9]?1:0);
3109        commandType.setSelectedIndex(opsw[10]?1:0);
3110        routesControl.setSelectedIndex((opsw[11]?1:0) + (opsw[16]?2:0));
3111        localControlOfOutputsStyle.setSelectedIndex((opsw[12]?1:0) + (opsw[15]?2:0));
3112        sensorMessageTrigger.setSelectedIndex(opsw[13]?1:0);
3113        commandSource.setSelectedIndex(opsw[14]?1:0);
3114        output1CrossbuckFlasherCheckBox.setSelected(opsw[17]);
3115        output2CrossbuckFlasherCheckBox.setSelected(opsw[18]);
3116        output3CrossbuckFlasherCheckBox.setSelected(opsw[19]);
3117        output4CrossbuckFlasherCheckBox.setSelected(opsw[20]);
3118        localSensorType.setSelectedIndex(opsw[21]?1:0);
3119    }
3120    
3121    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Ds64TabbedPanel.class);
3122
3123}