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