001package jmri.jmrix.cmri.serial.nodeconfigmanager;
002
003import java.awt.*;
004import java.awt.event.*;
005import java.io.IOException;
006import java.util.ArrayList;
007
008import javax.swing.*;
009import javax.swing.border.Border;
010import javax.swing.border.TitledBorder;
011import javax.swing.table.*;
012
013import jmri.jmrix.cmri.CMRISystemConnectionMemo;
014import jmri.jmrix.cmri.serial.SerialNode;
015import jmri.jmrix.cmri.serial.nodeiolist.NodeIOListFrame;
016import jmri.util.davidflanagan.HardcopyWriter;
017import jmri.util.swing.JmriJOptionPane;
018
019/**
020 *
021 * Frames for a table view to manage CMRInet node configuration management. c2
022 * Created a table view for node configuration operations. Add, Edit, Delete and
023 * Update are executed from the NodeTableManager. This class was derived from
024 * the NodeConfig class.
025 *
026 * @author Bob Jacobsen Copyright (C) 2004
027 * @author Dave Duchamp Copyright (C) 2004
028 * @author Chuck Catania Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018
029 */
030public class NodeConfigManagerFrame extends jmri.util.JmriJFrame {
031
032    ArrayList<SerialNode> cmriNode = new ArrayList<>();
033    public int numConfigNodes = 0;
034
035    public int numBits = 48;  // number of bits in assignment table
036    public int numInputBits = 24;  // number of input bits for selected node
037    public int numOutputBits = 48; // number of output bits for selected node
038
039    protected int selectedNodeAddr = -1;
040    protected int selectedTableRow = -1;
041    protected boolean doingPrint = false;
042
043    // node select pane items
044    JLabel nodeLabel = new JLabel(Bundle.getMessage("NodeBoxLabel") + " ");
045
046    // node table pane items
047    protected JPanel nodeTablePanel = null;
048    protected Border inputBorder = BorderFactory.createEtchedBorder();
049
050    protected NodeTableModel nodeTableModel = null;
051    protected JTable nodeTable = null;
052
053    // button pane items
054    JButton addButton = new JButton(Bundle.getMessage("ButtonAdd"));
055    JButton doneButton = new JButton(Bundle.getMessage("ButtonDone"));
056    JButton printButton = new JButton(Bundle.getMessage("PrintButtonText"));
057
058    NodeConfigManagerFrame curFrame;
059
060    protected JTextField nodeAddrField = new JTextField(3);
061    protected JLabel nodeAddrStatic = new JLabel("000");
062    protected JComboBox<String> nodeTypeBox;
063    protected JTextField receiveDelayField = new JTextField(3);
064    protected JTextField nodeDescription = new JTextField(32);
065    protected JTextField pulseWidthField = new JTextField(4);
066    protected JComboBox<String> cardSizeBox;
067    protected JComboBox<String> cardSize8Box;
068    protected JLabel cardSizeText = new JLabel("   " + Bundle.getMessage("LabelCardSize"));
069    protected JLabel onBoardBytesText = new JLabel(Bundle.getMessage("LabelOnBoardBytes") + " 3 Input Bytes, 6 Output Bytes");
070
071    protected JButton addNodeButton = new JButton(Bundle.getMessage("ButtonAdd"));
072    protected JButton editNodeButton = new JButton(Bundle.getMessage("ButtonEdit"));
073    protected JButton deleteNodeButton = new JButton(Bundle.getMessage("ButtonDelete"));
074    protected JButton doneNodeButton = new JButton(Bundle.getMessage("ButtonDone"));
075    protected JButton updateNodeButton = new JButton(Bundle.getMessage("ButtonUpdate"));
076    protected JButton cancelNodeButton = new JButton(Bundle.getMessage("ButtonCancel"));
077
078    protected JLabel statusText1 = new JLabel();
079    protected JLabel statusText2 = new JLabel();
080    protected JLabel statusText3 = new JLabel();
081
082    protected JPanel panel2 = new JPanel();
083    protected JPanel panel2a = new JPanel();
084    protected JPanel panel2b = new JPanel();
085    protected JPanel panel2c = new JPanel();  //c2 IOX config
086    protected JPanel panelnodeDescBox = new JPanel();   //c2 node desctipion box
087    protected JPanel panelnodeDesc = new JPanel();  //c2 node description
088    protected JPanel panelnetOpt = new JPanel();  //c2 CMRInet options
089    protected JPanel panelnetOptBox = new JPanel();  //c2 CMRInet options frame
090    protected JPanel panelnodeOpt = new JPanel();   //c2 node options
091
092    protected boolean changedNode = false;  // true if a node was changed, deleted, or added
093    protected boolean editMode = false;     // true if in edit mode
094
095    protected SerialNode curNode = null;    // Serial Node being edited
096    protected int nodeAddress = 0;          // Node address
097    protected int nodeType = SerialNode.SMINI; // Node type - default SMINI
098    protected int bitsPerCard = 24;         // number of bits per card
099    protected int receiveDelay = 0;         // transmission delay
100    protected int pulseWidth = 500;         // pulse width for turnout control (milliseconds)
101    protected int num2LSearchLights = 0;    // number of 2-lead oscillating searchlights
102
103    protected int numCards = 0;             //set by consistency check routine
104    protected int cpNodeOnboard = 4;        //Number of fixed bytes(cards) for a cpNode
105    protected int osNodeOnboard = 8;        //Number of fixed bytes(cards) for a osNode
106
107    protected boolean errorInStatus1 = false;
108    protected boolean errorInStatus2 = false;
109
110    protected String editStatus1 = Bundle.getMessage("NotesEdit1");
111    protected String editStatus2 = Bundle.getMessage("NotesEdit2");
112    protected String editStatus3 = Bundle.getMessage("NotesEdit3");
113    protected String addStatus1 = Bundle.getMessage("NotesAdd1");
114    protected String addStatus2 = Bundle.getMessage("NotesAdd2");
115    protected String addStatus3 = Bundle.getMessage("NotesAdd3");
116    protected String delStatus1 = Bundle.getMessage("NotesDel1");
117    protected String delStatus2 = Bundle.getMessage("NotesDel2");
118    protected String delStatus3 = Bundle.getMessage("NotesDel3");
119
120    protected String nodeDescText = "";
121    protected int deleteNodeAddress = 0;
122
123    HandlerClass nodeOptHandler = new HandlerClass();
124
125    // --------------------------
126    // CMRInet Options CheckBoxes
127    // --------------------------
128    protected JCheckBox cbx_cmrinetopt_AUTOPOLL = new JCheckBox(Bundle.getMessage("cmrinetOpt0"), true);
129    protected JCheckBox cbx_cmrinetopt_USECMRIX = new JCheckBox(Bundle.getMessage("cmrinetOpt1"));
130    protected JCheckBox cbx_cmrinetopt_USEBCC = new JCheckBox(Bundle.getMessage("cmrinetOpt2"));
131    protected JCheckBox cbx_cmrinetopt_BIT8 = new JCheckBox(Bundle.getMessage("cmrinetOpt8"));
132    protected JCheckBox cbx_cmrinetopt_BIT15 = new JCheckBox(Bundle.getMessage("cmrinetOpt15"));
133
134    // -------------------------
135    // cpNode Options CheckBoxes
136    // -------------------------
137    protected JCheckBox cbx_cpnodeopt_SENDEOT = new JCheckBox(Bundle.getMessage("cpnodeOpt0"));
138    protected JCheckBox cbx_cpnodeopt_BIT1 = new JCheckBox(Bundle.getMessage("cpnodeOpt1"));
139    protected JCheckBox cbx_cpnodeopt_BIT2 = new JCheckBox(Bundle.getMessage("cpnodeOpt2"));
140    protected JCheckBox cbx_cpnodeopt_BIT8 = new JCheckBox(Bundle.getMessage("cpnodeOpt8"));
141    protected JCheckBox cbx_cpnodeopt_BIT15 = new JCheckBox(Bundle.getMessage("cpnodeOpt15"));
142
143    private CMRISystemConnectionMemo _memo = null;
144
145    /**
146     * Constructor method.
147     * @param memo system connection.
148     */
149    public NodeConfigManagerFrame(CMRISystemConnectionMemo memo) {
150        super();
151        _memo = memo;
152        curFrame = this;
153
154        // Clear information arrays
155        for (int i = 0; i < 64; i++) {
156            cardType[i] = Bundle.getMessage("CardTypeNone"); // NOI18N
157        }
158        for (int i = 0; i < 48; i++) {
159            searchlightBits[i] = false;
160            firstSearchlight[i] = false;
161        }
162        // addHelpMenu("package.jmri.jmrix.cmri.serial.nodeconfigmanager.NodeConfigManagerFrame", true); // NOI18N duplicate, see initComponents
163    }
164
165    /**
166     * {@inheritDoc}
167     */
168    @Override
169    public void initComponents() {
170        // set the frame's initial state
171        setTitle(Bundle.getMessage("WindowTitle") + Bundle.getMessage("WindowConnectionMemo")+_memo.getUserName());  // NOI18N
172        setSize(500, 150);
173
174        Container contentPane = getContentPane();
175        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
176
177        // load the SerialNode data
178        initializeNodes();
179
180        // Set up the assignment panel
181        nodeTablePanel = new JPanel();
182        nodeTablePanel.setLayout(new BoxLayout(nodeTablePanel, BoxLayout.Y_AXIS));
183
184        nodeTableModel = new NodeTableModel();
185        nodeTable = new JTable(nodeTableModel);
186
187        nodeTable.setShowGrid(true);
188        nodeTable.setGridColor(Color.black);
189        nodeTable.setRowSelectionAllowed(false);
190        nodeTable.setFont(new Font("Arial", Font.PLAIN, 14));
191        nodeTable.setRowHeight(30);
192
193        nodeTable.getTableHeader().setReorderingAllowed(false);
194        nodeTable.setPreferredScrollableViewportSize(new java.awt.Dimension(300, 350));
195        TableColumnModel assignmentColumnModel = nodeTable.getColumnModel();
196
197        DefaultTableCellRenderer dtcen = new DefaultTableCellRenderer();
198        dtcen.setHorizontalAlignment(SwingConstants.CENTER);
199        DefaultTableCellRenderer dtrt = new DefaultTableCellRenderer();
200        dtrt.setHorizontalAlignment(SwingConstants.RIGHT);
201
202        TableCellRenderer rendererFromHeader = nodeTable.getTableHeader().getDefaultRenderer();
203        JLabel headerLabel = (JLabel) rendererFromHeader;
204        headerLabel.setHorizontalAlignment(JLabel.CENTER);
205        headerLabel.setBackground(Color.LIGHT_GRAY);
206
207        TableColumn nodenumColumn = assignmentColumnModel.getColumn(NodeTableModel.NODENUM_COLUMN);
208        nodenumColumn.setMinWidth(40);
209        nodenumColumn.setMaxWidth(80);
210        nodenumColumn.setCellRenderer(dtcen);
211        nodenumColumn.setResizable(false);
212
213        TableColumn nodetypeColumn = assignmentColumnModel.getColumn(NodeTableModel.NODETYPE_COLUMN);
214        nodetypeColumn.setMinWidth(40);
215        nodetypeColumn.setMaxWidth(80);
216        nodetypeColumn.setCellRenderer(dtcen);
217        nodetypeColumn.setResizable(false);
218
219        TableColumn numbitsColumn = assignmentColumnModel.getColumn(NodeTableModel.NUMBITS_COLUMN);
220        numbitsColumn.setMinWidth(40);
221        numbitsColumn.setMaxWidth(120);
222        numbitsColumn.setCellRenderer(dtcen);
223        numbitsColumn.setResizable(false);
224
225        TableColumn numinputsColumn = assignmentColumnModel.getColumn(NodeTableModel.NUMINCARDS_COLUMN);
226        numinputsColumn.setMinWidth(40);
227        numinputsColumn.setMaxWidth(80);
228        numinputsColumn.setCellRenderer(dtcen);
229        numinputsColumn.setResizable(false);
230
231        TableColumn numoutputsColumn = assignmentColumnModel.getColumn(NodeTableModel.NUMOUTCARDS_COLUMN);
232        numoutputsColumn.setMinWidth(10);
233        numoutputsColumn.setMaxWidth(100);
234        numoutputsColumn.setCellRenderer(dtcen);
235        numoutputsColumn.setResizable(false);
236
237        TableColumn numinbytesColumn = assignmentColumnModel.getColumn(NodeTableModel.NUMINBYTES_COLUMN);
238        numinbytesColumn.setMinWidth(10);
239        numinbytesColumn.setMaxWidth(80);
240        numinbytesColumn.setCellRenderer(dtcen);
241        numinbytesColumn.setResizable(false);
242
243        TableColumn numoutbytesColumn = assignmentColumnModel.getColumn(NodeTableModel.NUMOUTBYTES_COLUMN);
244        numoutbytesColumn.setMinWidth(10);
245        numoutbytesColumn.setMaxWidth(100);
246        numoutbytesColumn.setCellRenderer(dtcen);
247        numoutbytesColumn.setResizable(false);
248
249        TableColumn selectColumn = assignmentColumnModel.getColumn(NodeTableModel.SELECT_COLUMN);
250        JComboBox<String> comboBox = new JComboBox<>();
251        comboBox.addItem(Bundle.getMessage("SelectSelect"));
252        comboBox.addItem(Bundle.getMessage("SelectEdit"));
253        comboBox.addItem(Bundle.getMessage("SelectInfo"));
254        comboBox.addItem(Bundle.getMessage("SelectDelete"));
255        selectColumn.setCellEditor(new DefaultCellEditor(comboBox));
256
257        selectColumn.setMinWidth(40);
258        selectColumn.setMaxWidth(90);
259        selectColumn.setCellRenderer(dtcen);
260        selectColumn.setResizable(false);
261
262        TableColumn nodedescColumn = assignmentColumnModel.getColumn(NodeTableModel.NODEDESC_COLUMN);
263        nodedescColumn.setMinWidth(40);
264        nodedescColumn.setMaxWidth(350);
265        nodedescColumn.setResizable(true);
266        JScrollPane nodeTableScrollPane = new JScrollPane(nodeTable);
267
268        Border inputBorderTitled = BorderFactory.createTitledBorder(inputBorder,
269                " ",
270                TitledBorder.LEFT, TitledBorder.ABOVE_TOP);
271        nodeTablePanel.add(nodeTableScrollPane, BorderLayout.CENTER);
272        nodeTablePanel.setBorder(inputBorderTitled);
273        setPreferredSize(new Dimension(950, 550));
274
275        nodeTable.setAutoCreateRowSorter(true);
276        nodeTable.getRowSorter().toggleSortOrder(NodeTableModel.NODENUM_COLUMN);
277
278        contentPane.add(nodeTablePanel);
279
280        // Setup main window buttons
281        JPanel panel3 = new JPanel();
282        panel3.setLayout(new BoxLayout(panel3, FlowLayout.RIGHT));
283        panel3.setPreferredSize(new Dimension(950, 50));
284
285        // Set up Add button
286        addButton.setVisible(true);
287        addButton.setToolTipText(Bundle.getMessage("AddButtonTip"));
288        addButton.addActionListener((java.awt.event.ActionEvent e) -> {
289            addButtonActionPerformed(e);
290        });
291        panel3.add(addButton);
292
293        // Set up Print button
294        printButton.setVisible(true);
295        printButton.setToolTipText(Bundle.getMessage("PrintButtonTip"));
296        if (numConfigNodes > 0) {
297            printButton.addActionListener((java.awt.event.ActionEvent e) -> {
298                printButtonActionPerformed(e);
299            });
300        }
301        panel3.add(printButton);
302
303        // Set up Done button
304        doneButton.setVisible(true);
305        doneButton.setToolTipText(Bundle.getMessage("DoneButtonTip"));
306        doneButton.addActionListener((java.awt.event.ActionEvent e) -> {
307            doneButtonActionPerformed();
308        });
309        panel3.add(doneButton);
310
311        contentPane.add(panel3);
312        addHelpMenu("package.jmri.jmrix.cmri.serial.nodeconfigmanager.NodeConfigManagerFrame", true);
313        // pack for display
314        pack();
315        nodeTablePanel.setVisible(true);
316    }
317
318    /**
319     * Get the selected node address from the node table.
320     * @return selected node ID.
321     */
322    public int getSelectedNodeAddr() {
323        return (Integer) nodeTable.getValueAt(nodeTable.getSelectedRow(), 0);
324    }
325
326    /**
327     * Handle the done button click.
328     */
329    public void doneButtonActionPerformed() {
330        changedNode = false;
331        setVisible(false);
332        dispose();
333    }
334
335    public void addButtonActionPerformed(ActionEvent e) {
336        NodeConfigManagerFrame f = new NodeConfigManagerFrame(_memo);
337        try {
338            f.initNodeConfigWindow();
339        } catch (Exception ex) {
340            log.info("addButtonActionPerformed Exception-C2: {}", ex.toString());
341        }
342        f.nodeTableModel = nodeTableModel;
343        f.initNodeVariables();
344        f.buttonSet_ADD();
345        f.setLocation(100, 100);
346        f.setVisible(true);
347    }
348
349    /**
350     * Print.
351     *
352     * @param e the triggering event
353     */
354    public void printButtonActionPerformed(java.awt.event.ActionEvent e) {
355        int[] colWidth = new int[10];
356        // initialize column widths
357        TableColumnModel nodeTableColumnModel = nodeTable.getColumnModel();
358        colWidth[0] = nodeTableColumnModel.getColumn(NodeTableModel.NODENUM_COLUMN).getWidth();
359        colWidth[1] = nodeTableColumnModel.getColumn(NodeTableModel.NODETYPE_COLUMN).getWidth();
360        colWidth[2] = nodeTableColumnModel.getColumn(NodeTableModel.NUMBITS_COLUMN).getWidth();
361        colWidth[3] = nodeTableColumnModel.getColumn(NodeTableModel.NUMINCARDS_COLUMN).getWidth();
362        colWidth[4] = nodeTableColumnModel.getColumn(NodeTableModel.NUMOUTCARDS_COLUMN).getWidth();
363        colWidth[5] = nodeTableColumnModel.getColumn(NodeTableModel.SELECT_COLUMN).getWidth();
364        colWidth[6] = nodeTableColumnModel.getColumn(NodeTableModel.NUMBITS_COLUMN).getWidth();
365        colWidth[7] = nodeTableColumnModel.getColumn(NodeTableModel.NUMBITS_COLUMN).getWidth();
366        colWidth[8] = nodeTableColumnModel.getColumn(NodeTableModel.NUMBITS_COLUMN).getWidth();
367        colWidth[9] = nodeTableColumnModel.getColumn(NodeTableModel.NUMBITS_COLUMN).getWidth();
368
369        // set up a page title
370        String head = "CMRInet Node Table";
371        // initialize a printer writer
372        HardcopyWriter writer = null;
373        try {
374            writer = new HardcopyWriter(curFrame, head, 10, .8, .5, .5, .5, false);
375        } catch (HardcopyWriter.PrintCanceledException ex) {
376            return;
377        }
378        writer.increaseLineSpacing(20);
379        // print the assignments
380        (nodeTableModel).printTable(writer, colWidth);
381    }
382
383    /**
384     * Edit node table selection.
385     */
386    public void editActionSelected() {
387        selectedNodeAddr = getSelectedNodeAddr();
388
389        NodeConfigManagerFrame f = new NodeConfigManagerFrame(_memo);
390        f.nodeTableModel = nodeTableModel;
391        f.selectedTableRow = nodeTable.convertRowIndexToModel(nodeTable.getSelectedRow());
392
393        try {
394            f.initNodeConfigWindow();
395            f.editNodeButtonActionPerformed(selectedNodeAddr);
396        } catch (Exception ex) {
397            log.info("editActionSelected", ex);
398
399        }
400        f.setLocation(200, 200);
401        f.buttonSet_EDIT();
402        f.setVisible(true);
403    }
404
405    /**
406     * Handle the delete button click
407     */
408    public void deleteActionSelected() {
409        selectedNodeAddr = getSelectedNodeAddr();
410
411        NodeConfigManagerFrame f = new NodeConfigManagerFrame(_memo);
412        f.nodeTableModel = nodeTableModel;
413        f.selectedTableRow = nodeTable.convertRowIndexToModel(nodeTable.getSelectedRow());
414        try {
415            f.initNodeConfigWindow();
416            f.deleteNodeButtonActionPerformed(selectedNodeAddr);
417        } catch (Exception ex) {
418            log.info("deleteActionSelected", ex);
419
420        }
421        f.setLocation(200, 200);
422        f.buttonSet_DELETE();
423        f.setVisible(true);
424    }
425
426    /**
427     * Handle info action.
428     */
429    public void infoActionSelected() {
430        selectedNodeAddr = getSelectedNodeAddr();
431
432        NodeIOListFrame f = new NodeIOListFrame(_memo);
433        try {
434            f.initComponents();
435            f.displayNodeIOBits(selectedNodeAddr);
436        } catch (Exception ex) {
437            log.info("infoActionSelected Exception-C2: {}", ex.toString());
438
439        }
440        f.setLocation(100, 100);
441        f.setVisible(true);
442    }
443
444    /**
445     * Method to initialize configured nodes and sets up the node select combo
446     * box
447     */
448    public void initializeNodes() {
449        // get all configured nodes
450        if (!cmriNode.isEmpty()) {
451            cmriNode.clear();
452        }
453
454        SerialNode node = (SerialNode) _memo.getTrafficController().getNode(0);
455        int index = 1;
456        while (node != null) {
457            cmriNode.add(node);
458            node = (SerialNode) _memo.getTrafficController().getNode(index);
459            index++;
460        }
461
462        numConfigNodes = cmriNode.size();
463    }
464
465    /**
466     * Set up table for displaying bit assignments
467     */
468    public class NodeTableModel extends AbstractTableModel {
469
470        @Override
471        public String getColumnName(int c) {
472            return nodeTableColumnsNames[c];
473        }
474
475        @Override
476        public Class<?> getColumnClass(int c) {
477            switch (c) {
478                case NODENUM_COLUMN:
479                    return Integer.class;
480
481                case NODETYPE_COLUMN:
482                    return String.class;
483
484                case NUMBITS_COLUMN:
485                case NUMINCARDS_COLUMN:
486                case NUMOUTCARDS_COLUMN:
487                case NUMINBYTES_COLUMN:
488                case NUMOUTBYTES_COLUMN:
489                    return Integer.class;
490
491                case SELECT_COLUMN:
492                    return String.class;
493
494                case NODEDESC_COLUMN:
495                default:
496                    return String.class;
497            }
498        }
499
500        @Override
501        public boolean isCellEditable(int r, int c) {
502            if (c == SELECT_COLUMN) {
503                return true;
504            } else {
505                return false;
506            }
507        }
508
509        @Override
510        public int getColumnCount() {
511            return NUM_COLUMNS;
512        }
513
514        @Override
515        public int getRowCount() {
516            return cmriNode.size();
517        }
518
519        public void removeRow(int row) {
520            cmriNode.remove(row);
521            numConfigNodes = cmriNode.size();
522            fireTableRowsDeleted(row, row);
523        }
524
525        public void addRow(SerialNode newNode) {
526            cmriNode.add(newNode);
527            numConfigNodes = cmriNode.size();
528            fireTableDataChanged();
529        }
530
531        public void changeRow(int row, SerialNode aNode) {
532            cmriNode.set(row, aNode);
533            fireTableDataChanged();
534        }
535
536        @Override
537        public void setValueAt(Object value, int row, int col) {
538            if (col == SELECT_COLUMN) {
539                if (Bundle.getMessage("SelectEdit").equals(value)) {
540                    editActionSelected();
541                } else if (Bundle.getMessage("SelectInfo").equals(value)) {
542                    infoActionSelected();
543                } else if (Bundle.getMessage("SelectDelete").equals(value)) {
544                    deleteActionSelected();
545                }
546            } else {
547                log.info("setValueAt Row{} value {}", row, value);
548            }
549            fireTableDataChanged();
550        }
551
552        @Override
553        public Object getValueAt(int r, int c) {
554            switch (c) {
555                case NODENUM_COLUMN:
556                    if (!doingPrint) {
557                        return cmriNode.get(r).getNodeAddress();
558                    } else {
559                        return Integer.toString(cmriNode.get(r).getNodeAddress());
560                    }
561
562                case NODETYPE_COLUMN:
563                    return "  " + nodeTableTypes[cmriNode.get(r).getNodeType()];
564
565                case NUMBITS_COLUMN:
566                    return Integer.toString(cmriNode.get(r).getNumBitsPerCard());
567
568                case NUMINCARDS_COLUMN:
569                    if (cmriNode.get(r).getNodeType() == SerialNode.SMINI) {
570                        return Integer.toString(cmriNode.get(r).numInputCards() * 3);
571                    } else {
572                        return Integer.toString(cmriNode.get(r).numInputCards());
573                    }
574
575                case NUMOUTCARDS_COLUMN:
576                    if (cmriNode.get(r).getNodeType() == SerialNode.SMINI) {
577                        return Integer.toString(cmriNode.get(r).numOutputCards() * 3);
578                    } else {
579                        return Integer.toString(cmriNode.get(r).numOutputCards());
580                    }
581
582                case NUMINBYTES_COLUMN:
583                    return Integer.toString((cmriNode.get(r).getNumBitsPerCard()) * cmriNode.get(r).numInputCards());
584
585                case NUMOUTBYTES_COLUMN:
586                    return Integer.toString((cmriNode.get(r).getNumBitsPerCard()) * cmriNode.get(r).numOutputCards());
587
588                case SELECT_COLUMN:
589
590                    return "Select";
591                case NODEDESC_COLUMN:
592
593                    return " " + cmriNode.get(r).getcmriNodeDesc();
594                default:
595                    return "";
596            }
597        }
598
599        public static final int NODENUM_COLUMN = 0;
600        public static final int NODETYPE_COLUMN = 1;
601        public static final int NUMBITS_COLUMN = 2;
602        public static final int NUMINCARDS_COLUMN = 3;
603        public static final int NUMOUTCARDS_COLUMN = 4;
604        public static final int NUMINBYTES_COLUMN = 5;
605        public static final int NUMOUTBYTES_COLUMN = 6;
606        public static final int SELECT_COLUMN = 7;
607        public static final int NODEDESC_COLUMN = 8;
608        public static final int NUM_COLUMNS = NODEDESC_COLUMN + 1;
609
610//        private String[] pollStatus = {"ERROR", "IDLE", "POLLING", "TIMEOUT", "SLOW POLL"};
611        /**
612         * Method to print or print preview the assignment table. Printed in
613         * proportionately sized columns across the page with headings and
614         * vertical lines between each column. Data is word wrapped within a
615         * column. Can only handle 4 columns of data as strings. Adapted from
616         * routines in BeanTableDataModel.java by Bob Jacobsen and Dennis Miller
617         * @param w hardcopywriter instance.
618         * @param colWidth column width array.
619        */
620        public void printTable(HardcopyWriter w, int colWidth[]) {
621            // determine the column sizes - proportionately sized, with space between for lines
622            int[] columnSize = new int[NUM_COLUMNS];
623            int charPerLine = w.getCharactersPerLine();
624            int tableLineWidth = 0;  // table line width in characters
625            int totalColWidth = 0;
626
627            doingPrint = true;
628            for (int j = 0; j < NUM_COLUMNS; j++) {
629                if (j != SELECT_COLUMN) {
630                    totalColWidth += colWidth[j];
631                }
632            }
633            float ratio = ((float) charPerLine) / ((float) totalColWidth);
634            for (int j = 0; j < NUM_COLUMNS; j++) {
635                if (j != SELECT_COLUMN) {
636                    columnSize[j] = ((int) (colWidth[j] * ratio)) - 1;
637                    tableLineWidth += (columnSize[j] + 1);
638                }
639            }
640
641            // Draw horizontal dividing line
642            w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(), tableLineWidth);
643
644            // print the column header labels
645            String[] columnStrings = new String[NUM_COLUMNS];
646            // Put each column header in the array
647            for (int i = 0; i < NUM_COLUMNS; i++) {
648                if (i != SELECT_COLUMN) {
649                    columnStrings[i] = this.getColumnName(i);
650                }
651            }
652            //          w.setFontStyle(Font.BOLD);
653            printColumns(w, columnStrings, columnSize);
654            w.setFontStyle(Font.PLAIN);
655
656            // draw horizontal line
657            w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(), tableLineWidth);
658            // now print each row of data
659            String[] spaces = new String[NUM_COLUMNS];
660            // create base strings the width of each of the columns
661            for (int k = 0; k < NUM_COLUMNS; k++) {
662                if (k != SELECT_COLUMN) {
663                    spaces[k] = "";
664                    for (int i = 0; i < columnSize[k]; i++) {
665                        spaces[k] = spaces[k] + " ";
666                    }
667                }
668            }
669            for (int i = 0; i < this.getRowCount(); i++) {
670                for (int j = 0; j < NUM_COLUMNS; j++) {
671                    if (j != SELECT_COLUMN) {
672                        //check for special, null contents
673                        if (this.getValueAt(i, j) == null) {
674                            columnStrings[j] = spaces[j];
675                        } else {
676                            columnStrings[j] = (String) this.getValueAt(i, j);
677                        }
678                    }
679                }
680                printColumns(w, columnStrings, columnSize);
681                // draw horizontal line
682                w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(), tableLineWidth);
683            }
684            doingPrint = false;
685            w.close();
686        }
687
688        protected void printColumns(HardcopyWriter w, String columnStrings[], int columnSize[]) {
689            String columnString = "";
690            StringBuilder lineString = new StringBuilder("");
691            String[] spaces = new String[NUM_COLUMNS];
692            // create base strings the width of each of the columns
693            for (int k = 0; k < NUM_COLUMNS; k++) {
694                if (k != SELECT_COLUMN) {
695                    spaces[k] = "";
696                    for (int i = 0; i < columnSize[k]; i++) {
697                        spaces[k] = spaces[k] + " ";
698                    }
699                }
700            }
701            // loop through each column
702            boolean complete = false;
703            while (!complete) {
704                complete = true;
705                for (int i = 0; i < NUM_COLUMNS; i++) {
706                    if (i != SELECT_COLUMN) {
707                        // if the column string is too wide cut it at word boundary (valid delimiters are space, - and _)
708                        // use the initial part of the text,pad it with spaces and place the remainder back in the array
709                        // for further processing on next line
710                        // if column string isn't too wide, pad it to column width with spaces if needed
711                        if (columnStrings[i].length() > columnSize[i]) {
712                            // this column string will not fit on one line
713                            boolean noWord = true;
714                            for (int k = columnSize[i]; k >= 1; k--) {
715                                if (columnStrings[i].substring(k - 1, k).equals(" ")
716                                        || columnStrings[i].substring(k - 1, k).equals("-")
717                                        || columnStrings[i].substring(k - 1, k).equals("_")) {
718                                    columnString = columnStrings[i].substring(0, k)
719                                            + spaces[i].substring(columnStrings[i].substring(0, k).length());
720                                    columnStrings[i] = columnStrings[i].substring(k);
721                                    noWord = false;
722                                    complete = false;
723                                    break;
724                                }
725                            }
726                            if (noWord) {
727                                columnString = columnStrings[i].substring(0, columnSize[i]);
728                                columnStrings[i] = columnStrings[i].substring(columnSize[i]);
729                                complete = false;
730                            }
731                        } else {
732                            // this column string will fit on one line
733                            columnString = columnStrings[i] + spaces[i].substring(columnStrings[i].length());
734                            columnStrings[i] = "";
735                        }
736                        lineString.append(columnString).append(" ");
737                    }
738                }
739                try {
740                    w.write(lineString.toString());
741                    //write vertical dividing lines
742                    int iLine = w.getCurrentLineNumber();
743                    for (int i = 0, k = 0; i < w.getCharactersPerLine(); k++) {
744                        if (k != SELECT_COLUMN) {
745                            w.write(iLine, i, iLine + 1, i);
746                            if (k < NUM_COLUMNS) {
747                                i = i + columnSize[k] + 1;
748                            } else {
749                                i = w.getCharactersPerLine();
750                            }
751                        }
752                    }
753                    w.write("\n");
754                    lineString = new StringBuilder("");
755                } catch (IOException e) {
756                    log.warn("error during printing: ", e);
757                }
758            }
759        }
760    }
761
762    private String[] nodeTableColumnsNames
763            = {"Address", "   Type", "Bits per Card", "IN Cards", "OUT Cards", "IN Bits", "OUT Bits", " ", "  Description"};
764
765    private String[] nodeTableTypes = {"--", "SMINI", "SUSIC", "CPNODE", "CPMEGA"};
766
767    /*
768     * ----------------------------------------------------------
769     *    ------ Node Configuration Management Routines ------
770     * ----------------------------------------------------------
771     */
772    public void initNodeVariables() {
773        // Clear information arrays
774        for (int i = 0; i < 64; i++) {
775            cardType[i] = Bundle.getMessage("CardTypeNone");
776        }
777
778        //cpMega onboard bytes held in a separate array and will be copied
779        //to CardArray.
780        for (int i = 0; i < 16; i++) {
781            onBoardType[i] = Bundle.getMessage("CardTypeNone");
782        }
783
784        for (int i = 0; i < 48; i++) {
785            searchlightBits[i] = false;
786            firstSearchlight[i] = false;
787        }
788        for (int i = 0; i < SerialNode.NUMCMRINETOPTS; i++) {
789            cmrinetOpts[i] = 0;
790        }
791        for (int i = 0; i < SerialNode.NUMCPNODEOPTS; i++) {
792            cpnodeOpts[i] = 0;
793        }
794        nodeDescText = "";
795    }
796
797    /**
798     * Initialize the node configuration window This window is a template for
799     * ADD,EDIT,DELETE node operations
800     */
801    public void initNodeConfigWindow() {
802        setTitle(Bundle.getMessage("WindowTitle"));
803        Container contentPane = getContentPane();
804        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
805
806        // Set up node address and node type
807        JPanel panel1 = new JPanel();
808        panel1.setLayout(new BoxLayout(panel1, BoxLayout.Y_AXIS));
809        JPanel panel11 = new JPanel();
810        panel11.setLayout(new FlowLayout());
811        panel11.add(new JLabel(Bundle.getMessage("LabelNodeAddress") + " "));
812        panel11.add(nodeAddrField);
813        nodeAddrField.setToolTipText(Bundle.getMessage("TipNodeAddress"));
814        nodeAddrField.setText("-1");
815        panel11.add(nodeAddrStatic);
816        nodeAddrStatic.setVisible(false);
817        panel11.add(new JLabel("   " + Bundle.getMessage("LabelNodeType") + " "));
818        nodeTypeBox = new JComboBox<>();
819        panel11.add(nodeTypeBox);
820        nodeTypeBox.addItem("SMINI");
821        nodeTypeBox.addItem("USIC_SUSIC");
822        nodeTypeBox.addItem("CPNODE");
823        nodeTypeBox.addItem("CPMEGA");
824
825        /*
826         * Here add code for other types of nodes
827         */
828        nodeTypeBox.addActionListener(new java.awt.event.ActionListener() {
829            @Override
830            public void actionPerformed(java.awt.event.ActionEvent event) {
831                String s = (String) nodeTypeBox.getSelectedItem();
832
833                if (s.equals("SMINI")) {
834                    panel2.setVisible(false);
835                    panel2a.setVisible(true);
836                    panel2b.setVisible(false);
837                    panel2c.setVisible(false);
838                    cardSizeText.setVisible(false);
839                    cardSizeBox.setVisible(false);
840                    cardSize8Box.setVisible(false);
841                    panelnodeDescBox.setVisible(true);
842                    panelnodeDesc.setVisible(true);
843                    panelnetOpt.setVisible(true);
844                    panelnetOptBox.setVisible(true);
845                    panelnodeOpt.setVisible(false);
846                    nodeType = SerialNode.SMINI;
847                    onBoardBytesText.setText(Bundle.getMessage("LabelOnBoardBytes") + " 3 Input, 6 Output");
848                } else if (s.equals("USIC_SUSIC")) {
849                    panel2.setVisible(true);
850                    panel2a.setVisible(false);
851                    panel2b.setVisible(false);
852                    panel2c.setVisible(false);
853                    cardSizeText.setVisible(true);
854                    cardSizeBox.setVisible(true);
855                    cardSize8Box.setVisible(false);
856                    panelnodeDescBox.setVisible(true);
857                    panelnodeDesc.setVisible(true);
858                    panelnetOpt.setVisible(true);
859                    panelnetOptBox.setVisible(true);
860                    panelnodeOpt.setVisible(false);
861                    nodeType = SerialNode.USIC_SUSIC;
862                    onBoardBytesText.setText("  ");
863                } else if (s.equals("CPNODE")) {
864                    panel2.setVisible(false);
865                    panel2a.setVisible(false);
866                    panel2b.setVisible(true);
867                    panel2c.setVisible(false);   // IOX bytes
868                    cardSizeText.setVisible(true);
869                    cardSizeBox.setVisible(false);
870                    cardSize8Box.setVisible(true);
871                    panelnodeDescBox.setVisible(true);
872                    panelnodeDesc.setVisible(true);
873                    panelnetOpt.setVisible(true);
874                    panelnetOptBox.setVisible(true);
875                    panelnodeOpt.setVisible(true);
876                    nodeType = SerialNode.CPNODE;
877                    onBoardBytesText.setText(Bundle.getMessage("LabelOnBoardBytes") + " 2 Bytes");
878                } else if (s.equals("CPMEGA")) {
879                    panel2.setVisible(false);
880                    panel2a.setVisible(false);
881                    panel2b.setVisible(true);
882                    panel2c.setVisible(true);   // IOX bytes
883                    cardSizeText.setVisible(true);
884                    cardSizeBox.setVisible(false);
885                    cardSize8Box.setVisible(true);
886                    panelnodeDescBox.setVisible(true);
887                    panelnodeDesc.setVisible(true);
888                    panelnetOpt.setVisible(true);
889                    panelnetOptBox.setVisible(true);
890                    panelnodeOpt.setVisible(true);
891                    nodeType = SerialNode.CPMEGA;
892                    onBoardBytesText.setText(Bundle.getMessage("LabelOnBoardBytes") + " 8 Bytes");
893                }
894                /*
895                 * Here add code for other types of nodes
896                 */
897
898                // reset notes as appropriate
899                resetNotes();
900            }
901        });
902        nodeTypeBox.setToolTipText(Bundle.getMessage("TipNodeType"));
903
904        JPanel panel12 = new JPanel();
905        panel12.setLayout(new FlowLayout());
906        panel12.add(new JLabel(Bundle.getMessage("LabelDelay") + " "));
907        panel12.add(receiveDelayField);
908        receiveDelayField.setToolTipText(Bundle.getMessage("TipDelay"));
909        receiveDelayField.setText("0");
910        panel12.add(cardSizeText);
911        cardSizeBox = new JComboBox<>();
912        cardSize8Box = new JComboBox<>();
913        panel12.add(cardSizeBox);
914        panel12.add(cardSize8Box);
915        cardSizeBox.addItem(Bundle.getMessage("CardSize24"));
916        cardSizeBox.addItem(Bundle.getMessage("CardSize32"));
917        cardSize8Box.addItem(Bundle.getMessage("CardSize8"));
918        /*
919         * Here add code for other types of nodes
920         */
921
922        cardSizeBox.addActionListener(new java.awt.event.ActionListener() {
923            @Override
924            public void actionPerformed(java.awt.event.ActionEvent event) {
925                String s = (String) cardSizeBox.getSelectedItem();
926                if (s.equals(Bundle.getMessage("CardSize24"))) {
927                    bitsPerCard = 24;
928                } else if (s.equals(Bundle.getMessage("CardSize32"))) {
929                    bitsPerCard = 32;
930                } else if (s.equals(Bundle.getMessage("CardSize8"))) {
931                    bitsPerCard = 8;
932                }
933                // here add code for other node types, if required
934            }
935        });
936        cardSizeBox.setToolTipText(Bundle.getMessage("TipCardSize"));
937        cardSizeText.setVisible(false);
938        cardSizeBox.setVisible(false);
939        cardSize8Box.setVisible(false);
940
941        JPanel panel13 = new JPanel();
942        panel13.setLayout(new FlowLayout());
943        panel13.add(new JLabel(Bundle.getMessage("LabelPulseWidth") + " "));
944        panel13.add(pulseWidthField);
945        pulseWidthField.setToolTipText(Bundle.getMessage("TipPulseWidth"));
946        pulseWidthField.setText("500");
947        panel13.add(new JLabel(Bundle.getMessage("LabelMilliseconds")));
948
949        JPanel panel14 = new JPanel();
950        panel14.add(onBoardBytesText);
951        panel14.setVisible(true);
952
953        // Load the top half of the common window
954        panel1.add(panel11);
955        panel1.add(panel12);
956        panel1.add(panel13);
957        panel1.add(panel14);
958        contentPane.add(panel1);
959
960        // Set up USIC/SUSIC card type configuration table
961        JPanel panel21 = new JPanel();
962        panel21.setLayout(new BoxLayout(panel21, BoxLayout.Y_AXIS));
963        panel21.add(new JLabel(Bundle.getMessage("HintCardTypePartA")));
964        panel21.add(new JLabel(" " + Bundle.getMessage("HintCardTypePartB")));
965        panel21.add(new JLabel(" " + Bundle.getMessage("HintCardTypePartC")));
966        panel21.add(new JLabel("   "));
967        panel21.add(new JLabel(Bundle.getMessage("HintCardTypePartD")));
968        panel21.add(new JLabel(" " + Bundle.getMessage("HintCardTypePartE")));
969        panel21.add(new JLabel(" " + Bundle.getMessage("HintCardTypePartF")));
970        panel2.add(panel21);
971        TableModel cardConfigModel = new CardConfigModel();
972        JTable cardConfigTable = new JTable(cardConfigModel);
973        cardConfigTable.setRowSelectionAllowed(false);
974        cardConfigTable.setPreferredScrollableViewportSize(new java.awt.Dimension(180, 95));
975
976        JComboBox<String> cardTypeCombo = new JComboBox<>();
977        cardTypeCombo.addItem(Bundle.getMessage("CardTypeOutput"));
978        cardTypeCombo.addItem(Bundle.getMessage("CardTypeInput"));
979        cardTypeCombo.addItem(Bundle.getMessage("CardTypeNone"));
980
981        TableColumnModel typeColumnModel = cardConfigTable.getColumnModel();
982        TableColumn addressColumn = typeColumnModel.getColumn(CardConfigModel.ADDRESS_COLUMN);
983        addressColumn.setMinWidth(70);
984        addressColumn.setMaxWidth(80);
985        TableColumn cardTypeColumn = typeColumnModel.getColumn(CardConfigModel.TYPE_COLUMN);
986        cardTypeColumn.setCellEditor(new DefaultCellEditor(cardTypeCombo));
987        cardTypeColumn.setResizable(false);
988        cardTypeColumn.setMinWidth(90);
989        cardTypeColumn.setMaxWidth(100);
990
991        JScrollPane cardScrollPane = new JScrollPane(cardConfigTable);
992        panel2.add(cardScrollPane, BorderLayout.CENTER);
993        contentPane.add(panel2);
994        panel2.setVisible(false);
995
996        // Set up SMINI oscillating 2-lead searchlight configuration table
997        JPanel panel2a1 = new JPanel();
998        panel2a1.setLayout(new BoxLayout(panel2a1, BoxLayout.Y_AXIS));
999        panel2a1.add(new JLabel(Bundle.getMessage("HintSearchlightPartA")));
1000        panel2a1.add(new JLabel(" " + Bundle.getMessage("HintSearchlightPartB")));
1001        panel2a1.add(new JLabel(" " + Bundle.getMessage("HintSearchlightPartC")));
1002        panel2a1.add(new JLabel("   "));
1003        panel2a1.add(new JLabel(Bundle.getMessage("HintSearchlightPartD")));
1004        panel2a1.add(new JLabel(" " + Bundle.getMessage("HintSearchlightPartE")));
1005        panel2a1.add(new JLabel(" " + Bundle.getMessage("HintSearchlightPartF")));
1006        panel2a.add(panel2a1);
1007
1008        TableModel searchlightConfigModel = new SearchlightConfigModel();
1009        JTable searchlightConfigTable = new JTable(searchlightConfigModel);
1010        searchlightConfigTable.setRowSelectionAllowed(false);
1011        searchlightConfigTable.setPreferredScrollableViewportSize(new java.awt.Dimension(208, 100));
1012        TableColumnModel searchlightColumnModel = searchlightConfigTable.getColumnModel();
1013        TableColumn portColumn = searchlightColumnModel.getColumn(SearchlightConfigModel.PORT_COLUMN);
1014        portColumn.setMinWidth(90);
1015        portColumn.setMaxWidth(100);
1016        JScrollPane searchlightScrollPane = new JScrollPane(searchlightConfigTable);
1017        panel2a.add(searchlightScrollPane, BorderLayout.CENTER);
1018        contentPane.add(panel2a);
1019        panel2.setVisible(false);
1020
1021        // Set up CPMEGA on board byte I/O assignments
1022        JPanel panel2b3 = new JPanel();
1023        panel2b3.setLayout(new BoxLayout(panel2b3, BoxLayout.Y_AXIS));
1024        panel2b3.add(new JLabel("Assign Onboard Bytes"));
1025        panel2c.add(panel2b3);
1026
1027        TableModel osnodeConfigModel = new OSnodeConfigModel();
1028        JTable osnodeConfigTable = new JTable(osnodeConfigModel);
1029        osnodeConfigTable.setRowSelectionAllowed(false);
1030        osnodeConfigTable.setPreferredScrollableViewportSize(new java.awt.Dimension(170, 95)); //160
1031
1032        JComboBox<String> osnodeTypeCombo = new JComboBox<>();
1033        osnodeTypeCombo.addItem(Bundle.getMessage("CardTypeOutput"));
1034        osnodeTypeCombo.addItem(Bundle.getMessage("CardTypeInput"));
1035        osnodeTypeCombo.addItem(Bundle.getMessage("CardTypeNone"));
1036
1037        TableColumnModel osnodePortModel = osnodeConfigTable.getColumnModel();
1038        TableColumn x11Column = osnodePortModel.getColumn(OSnodeConfigModel.CARDNUM_COLUMN);
1039        x11Column.setMinWidth(50);
1040        x11Column.setMaxWidth(90);
1041        TableColumn x21Column = osnodePortModel.getColumn(OSnodeConfigModel.CARDTYPE_COLUMN);
1042        x21Column.setCellEditor(new DefaultCellEditor(osnodeTypeCombo));
1043        x21Column.setResizable(false);
1044        x21Column.setMinWidth(80);
1045        x21Column.setMaxWidth(100);
1046
1047        JScrollPane osnodeScrollPane = new JScrollPane(osnodeConfigTable);
1048        panel2c.add(osnodeScrollPane, BorderLayout.CENTER);
1049        contentPane.add(panel2c);
1050        panel2.setVisible(false);
1051
1052        // Set up I/O Expander (IOX) port assignments
1053        JPanel panel2b1 = new JPanel();
1054        panel2b1.setLayout(new BoxLayout(panel2b1, BoxLayout.Y_AXIS));
1055        panel2b1.add(new JLabel("Assign IOX Ports"));
1056
1057        panel2b.add(panel2b1);
1058
1059        TableModel cpnodeConfigModel = new CPnodeConfigModel();
1060        JTable cpnodeConfigTable = new JTable(cpnodeConfigModel);
1061        cpnodeConfigTable.setRowSelectionAllowed(false);
1062        cpnodeConfigTable.setPreferredScrollableViewportSize(new java.awt.Dimension(240, 130)); //160
1063
1064        JComboBox<String> cpnodeTypeCombo = new JComboBox<>();
1065        cpnodeTypeCombo.addItem(Bundle.getMessage("CardTypeOutput"));
1066        cpnodeTypeCombo.addItem(Bundle.getMessage("CardTypeInput"));
1067        cpnodeTypeCombo.addItem(Bundle.getMessage("CardTypeNone"));
1068
1069        TableColumnModel cpnodePortModel = cpnodeConfigTable.getColumnModel();
1070        TableColumn x0Column = cpnodePortModel.getColumn(CPnodeConfigModel.CARD_COLUMN);
1071        x0Column.setMinWidth(30);
1072        x0Column.setMaxWidth(50);
1073        TableColumn x1Column = cpnodePortModel.getColumn(CPnodeConfigModel.CARDNUM_COLUMN);
1074        x1Column.setMinWidth(70);
1075        x1Column.setMaxWidth(120);
1076        TableColumn x2Column = cpnodePortModel.getColumn(CPnodeConfigModel.CARDTYPE_COLUMN);
1077        x2Column.setCellEditor(new DefaultCellEditor(cpnodeTypeCombo));
1078        x2Column.setResizable(false);
1079        x2Column.setMinWidth(80);
1080        x2Column.setMaxWidth(100);
1081
1082        JScrollPane cpnodeScrollPane = new JScrollPane(cpnodeConfigTable);
1083        panel2b.add(cpnodeScrollPane, BorderLayout.CENTER);
1084        contentPane.add(panel2b);
1085        panel2b.setVisible(false);
1086
1087        // node Description field - all node types have this field
1088        panelnodeDescBox.setLayout(new BoxLayout(panelnodeDescBox, BoxLayout.Y_AXIS));
1089        panelnodeDesc.setLayout(new FlowLayout());
1090        panelnodeDesc.add(new JLabel("Description:"));
1091        nodeDescription.setVisible(true);
1092        panelnodeDesc.add(nodeDescription);
1093        panelnodeDesc.setVisible(true);
1094        contentPane.add(panelnodeDesc);
1095
1096        // Set up CMRInet Options
1097        panelnetOpt.setLayout(new GridLayout(0, 2));
1098
1099        panelnetOpt.add(cbx_cmrinetopt_AUTOPOLL);
1100        cbx_cmrinetopt_AUTOPOLL.addItemListener(nodeOptHandler);
1101
1102        panelnetOpt.add(cbx_cmrinetopt_USECMRIX);
1103        cbx_cmrinetopt_USECMRIX.addItemListener(nodeOptHandler);
1104        cbx_cmrinetopt_USECMRIX.setVisible(false);
1105
1106        panelnetOpt.add(cbx_cmrinetopt_USEBCC);
1107        cbx_cmrinetopt_USEBCC.addItemListener(nodeOptHandler);
1108        cbx_cmrinetopt_USEBCC.setVisible(false);
1109
1110        panelnetOpt.add(cbx_cmrinetopt_BIT8);
1111        cbx_cmrinetopt_BIT8.addItemListener(nodeOptHandler);
1112        cbx_cmrinetopt_BIT8.setVisible(false);
1113
1114        panelnetOpt.add(cbx_cmrinetopt_BIT15);
1115        cbx_cmrinetopt_BIT15.addItemListener(nodeOptHandler);
1116        cbx_cmrinetopt_BIT15.setVisible(false);
1117
1118        Border panelnetOptBorder = BorderFactory.createEtchedBorder();
1119        Border panelnetOptTitled = BorderFactory.createTitledBorder(panelnetOptBorder, "CMRInet Options");
1120        panelnetOpt.setBorder(panelnetOptTitled);
1121        panelnetOpt.setVisible(true);
1122        contentPane.add(panelnetOpt);
1123
1124        panelnetOptBox.add(Box.createHorizontalStrut(50));
1125        contentPane.add(panelnetOptBox);
1126
1127        // Set up cpNode Options
1128        panelnodeOpt.setLayout(new GridLayout(0, 2));
1129        panelnodeOpt.add(cbx_cpnodeopt_SENDEOT);
1130        cbx_cpnodeopt_SENDEOT.addItemListener(nodeOptHandler);
1131        cbx_cpnodeopt_SENDEOT.setVisible(true);
1132
1133        panelnodeOpt.add(cbx_cpnodeopt_BIT1);
1134        cbx_cpnodeopt_BIT1.addItemListener(nodeOptHandler);
1135        cbx_cpnodeopt_BIT1.setVisible(false);
1136
1137        panelnodeOpt.add(cbx_cpnodeopt_BIT2);
1138        cbx_cpnodeopt_BIT2.addItemListener(nodeOptHandler);
1139        cbx_cpnodeopt_BIT2.setVisible(false);
1140
1141        panelnodeOpt.add(cbx_cpnodeopt_BIT8);
1142        cbx_cpnodeopt_BIT8.addItemListener(nodeOptHandler);
1143        cbx_cpnodeopt_BIT8.setVisible(false);
1144
1145        panelnodeOpt.add(cbx_cpnodeopt_BIT15);
1146        cbx_cpnodeopt_BIT15.addItemListener(nodeOptHandler);
1147        cbx_cpnodeopt_BIT15.setVisible(false);
1148
1149        Border panelnodeOptBorder = BorderFactory.createEtchedBorder();
1150        Border panelnodeOptTitled = BorderFactory.createTitledBorder(panelnodeOptBorder, "cpNode Options");
1151        panelnodeOpt.setBorder(panelnodeOptTitled);
1152        panelnodeOpt.setVisible(false);
1153        contentPane.add(panelnodeOpt);
1154
1155        // Set up the notes area panel for various message displays
1156        JPanel panel3 = new JPanel();
1157        panel3.setLayout(new BoxLayout(panel3, BoxLayout.Y_AXIS));
1158        JPanel panel31 = new JPanel();
1159        panel31.setLayout(new FlowLayout());
1160        statusText1.setVisible(false);
1161        panel31.add(statusText1);
1162        JPanel panel32 = new JPanel();
1163        panel32.setLayout(new FlowLayout());
1164        statusText2.setVisible(false);
1165        panel32.add(statusText2);
1166        JPanel panel33 = new JPanel();
1167        panel33.setLayout(new FlowLayout());
1168        statusText3.setVisible(false);
1169        panel33.add(statusText3);
1170        panel3.add(panel31);
1171        panel3.add(panel32);
1172        panel3.add(panel33);
1173        Border panel3Border = BorderFactory.createEtchedBorder();
1174        Border panel3Titled = BorderFactory.createTitledBorder(panel3Border, Bundle.getMessage("BoxLabelNotes"));
1175        panel3.setBorder(panel3Titled);
1176        contentPane.add(panel3);
1177
1178        // Set up the functions buttons
1179        JPanel panel4 = new JPanel();
1180        panel4.setLayout(new FlowLayout());
1181
1182        addNodeButton.setText(Bundle.getMessage("ButtonAdd"));
1183        addNodeButton.setVisible(false);
1184        addNodeButton.setToolTipText(Bundle.getMessage("TipAddButton"));
1185        addNodeButton.addActionListener((java.awt.event.ActionEvent e) -> {
1186            addNodeButtonActionPerformed();
1187        });
1188        panel4.add(addNodeButton);
1189
1190        deleteNodeButton.setText(Bundle.getMessage("ButtonDelete"));
1191        deleteNodeButton.setVisible(false);
1192        deleteNodeButton.setToolTipText(Bundle.getMessage("TipDeleteButton"));
1193        deleteNodeButton.addActionListener((java.awt.event.ActionEvent e) -> {
1194            deleteNodeButtonActionConfirm();
1195        });
1196        panel4.add(deleteNodeButton);
1197
1198        updateNodeButton.setText(Bundle.getMessage("ButtonUpdate"));
1199        updateNodeButton.setVisible(false);
1200        updateNodeButton.setToolTipText(Bundle.getMessage("TipUpdateButton"));
1201        updateNodeButton.addActionListener((java.awt.event.ActionEvent e) -> {
1202            updateNodeButtonActionPerformed();
1203        });
1204        panel4.add(updateNodeButton);
1205
1206        cancelNodeButton.setText(Bundle.getMessage("ButtonCancel"));
1207        cancelNodeButton.setVisible(false);
1208        cancelNodeButton.setToolTipText(Bundle.getMessage("TipCancelButton"));
1209        cancelNodeButton.addActionListener((java.awt.event.ActionEvent e) -> {
1210            cancelNodeButtonActionPerformed();
1211        });
1212        panel4.add(cancelNodeButton);
1213
1214        doneNodeButton.setText(Bundle.getMessage("ButtonDone"));
1215        doneNodeButton.setVisible(false);
1216        doneNodeButton.setToolTipText(Bundle.getMessage("TipDoneButton"));
1217        doneNodeButton.addActionListener((java.awt.event.ActionEvent e) -> {
1218            doneNodeButtonActionPerformed();
1219        });
1220        panel4.add(doneNodeButton);
1221
1222        contentPane.add(panel4);
1223        pack();
1224    }
1225
1226    /*
1227     * -------------------------------------------------------
1228     * Methods to set the correct states of the window buttons
1229     * -------------------------------------------------------
1230     */
1231    public void buttonSet_ADD() {
1232        setTitle("ADD NODE");
1233        statusText1.setText(addStatus1);
1234        statusText1.setVisible(true);
1235        statusText2.setText(addStatus2);
1236        statusText2.setVisible(true);
1237        statusText3.setText(addStatus3);
1238        statusText3.setVisible(false);
1239
1240        panel2c.setVisible(false);
1241
1242        addNodeButton.setVisible(true);
1243        deleteNodeButton.setVisible(false);
1244        updateNodeButton.setVisible(false);
1245        doneNodeButton.setVisible(false);
1246        cancelNodeButton.setVisible(true);
1247    }
1248
1249    public void buttonSet_EDIT() {
1250        setTitle("EDIT NODE");
1251        statusText1.setText(editStatus1);
1252        statusText1.setVisible(true);
1253        statusText2.setText(editStatus2);
1254        statusText2.setVisible(true);
1255        statusText3.setText(editStatus3);
1256        statusText3.setVisible(false);
1257
1258        addNodeButton.setVisible(false);
1259        deleteNodeButton.setVisible(false);
1260        updateNodeButton.setVisible(true);
1261        doneNodeButton.setVisible(false);
1262        cancelNodeButton.setVisible(true);
1263    }
1264
1265    public void buttonSet_DELETE() {
1266        setTitle("DELETE NODE");
1267        statusText1.setText(delStatus1);
1268        statusText1.setVisible(true);
1269        statusText2.setText(delStatus2);
1270        statusText2.setVisible(true);
1271        statusText3.setText(delStatus3);
1272        statusText3.setVisible(false);
1273
1274        addNodeButton.setVisible(false);
1275        deleteNodeButton.setVisible(true);
1276        updateNodeButton.setVisible(false);
1277        doneNodeButton.setVisible(false);
1278        cancelNodeButton.setVisible(true);
1279    }
1280
1281    public void addNodeButtonActionPerformed() {
1282        // Check that a node with this address does not exist
1283        curNode = null;
1284        int nodeAddress = readNodeAddress();
1285        if (nodeAddress < 0) {
1286            return;
1287        }
1288
1289        // get a SerialNode corresponding to this node address if one exists
1290        curNode = (SerialNode) _memo.getTrafficController().getNodeFromAddress(nodeAddress);
1291        // curNode can never be null at this point. Was this intended to catch
1292        // an exception?
1293        if (curNode != null) {
1294            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("Error1", Integer.toString(nodeAddress)),
1295                "", JmriJOptionPane.ERROR_MESSAGE);
1296
1297            statusText1.setText(Bundle.getMessage("Error1", Integer.toString(nodeAddress)));
1298            statusText1.setVisible(true);
1299            errorInStatus1 = true;
1300            resetNotes2();
1301            return;
1302        }
1303
1304        // get node information from window and check for data consistency
1305        if (!readReceiveDelay()) {
1306            return;
1307        }
1308        if (!readPulseWidth()) {
1309            return;
1310        }
1311        if (!checkConsistency()) {
1312            return;
1313        }
1314
1315        // all ready, create the new node
1316        curNode = new SerialNode(nodeAddress, nodeType, _memo.getTrafficController());
1317        nodeTableModel.addRow(curNode);
1318
1319        // configure the new node
1320        setNodeParameters();
1321
1322        // register any orphan sensors that this node may have
1323        if (_memo.getSensorManager() != null) {
1324            (_memo.getSensorManager()).registerSensorsForNode(curNode);
1325        }
1326
1327        // reset text displays after succefully adding node
1328        resetNotes();
1329        changedNode = true;
1330        log.info("changedNode = {}", changedNode);
1331        // provide user feedback
1332        statusText1.setText(Bundle.getMessage("FeedBackAdd") + " " + Integer.toString(nodeAddress));
1333        statusText2.setVisible(false);
1334        statusText3.setVisible(true);
1335        doneNodeButton.setVisible(true);
1336        cancelNodeButton.setVisible(false);
1337        errorInStatus1 = true;
1338    }
1339
1340    /**
1341     * Load all of the configured node information from the serial node class.
1342     *
1343     * @param nodeaddr the node address
1344     */
1345    public void setupNodeInformationWindow(int nodeaddr) {
1346        // Find Serial Node address
1347        nodeAddress = nodeaddr;
1348        if (nodeAddress < 0) {
1349            return;
1350        }
1351
1352        // get the SerialNode corresponding to this node address
1353        curNode = (SerialNode) _memo.getTrafficController().getNodeFromAddress(nodeAddress);
1354        if (curNode == null) {
1355            statusText1.setText(Bundle.getMessage("Error4"));
1356            statusText1.setVisible(true);
1357            errorInStatus1 = true;
1358            resetNotes2();
1359            return;
1360        }
1361
1362        // Set up static node address from the table, cannot be changed
1363        nodeAddrStatic.setText(Integer.toString(nodeAddress));
1364        nodeAddrField.setVisible(false);
1365        nodeAddrStatic.setVisible(true);
1366
1367        // get information for this node and set up combo box
1368        nodeType = curNode.getNodeType();
1369        switch (nodeType) {
1370
1371            // SMINI
1372            case SerialNode.SMINI:
1373                nodeTypeBox.setSelectedItem("SMINI");
1374                bitsPerCard = 24;
1375                cardSizeBox.setSelectedItem(Bundle.getMessage("CardSize24"));
1376                onBoardBytesText.setText(Bundle.getMessage("LabelOnBoardBytes") + " 3 Input Bytes, 6 Output Bytes");
1377                // set up the searchlight arrays
1378                num2LSearchLights = 0;
1379                for (int i = 0; i < 48; i++) {
1380                    if (curNode.isSearchLightBit(i)) {
1381                        searchlightBits[i] = true;
1382                        searchlightBits[i + 1] = true;
1383                        firstSearchlight[i] = true;
1384                        firstSearchlight[i + 1] = false;
1385                        num2LSearchLights++;
1386                        i++;
1387                    } else {
1388                        searchlightBits[i] = false;
1389                        firstSearchlight[i] = false;
1390                    }
1391                }
1392                break;
1393
1394            // USIC/SUSIC
1395            case SerialNode.USIC_SUSIC:
1396                nodeTypeBox.setSelectedItem("USIC_SUSIC");
1397                bitsPerCard = curNode.getNumBitsPerCard();
1398                if (bitsPerCard == 24) {
1399                    cardSizeBox.setSelectedItem(Bundle.getMessage("CardSize24"));
1400                }
1401                if (bitsPerCard == 32) {
1402                    cardSizeBox.setSelectedItem(Bundle.getMessage("CardSize32"));
1403                }
1404                onBoardBytesText.setText("  ");
1405
1406                break;
1407
1408            // CPNODE
1409            case SerialNode.CPNODE:
1410                nodeTypeBox.setSelectedItem("CPNODE");
1411                bitsPerCard = 8;
1412                cardSize8Box.setSelectedItem(Bundle.getMessage("CardSize8"));
1413                onBoardBytesText.setText(Bundle.getMessage("LabelOnBoardBytes") + " 2 Bytes");
1414
1415                // cpNode Options
1416                cbx_cpnodeopt_SENDEOT.setSelected(curNode.iscpnodeBit(SerialNode.optbitNode_SENDEOT));
1417                cbx_cpnodeopt_BIT1.setSelected(false);
1418                cbx_cpnodeopt_BIT2.setSelected(false);
1419                cbx_cpnodeopt_BIT8.setSelected(curNode.iscpnodeBit(SerialNode.optbitNode_BIT8));
1420                cbx_cpnodeopt_BIT15.setSelected(curNode.iscpnodeBit(SerialNode.optbitNode_BIT15));
1421                break;
1422
1423            // CPMEGA
1424            case SerialNode.CPMEGA:
1425                nodeTypeBox.setSelectedItem("CPMEGA");
1426                bitsPerCard = 8;
1427                cardSize8Box.setSelectedItem(Bundle.getMessage("CardSize8"));
1428                onBoardBytesText.setText(Bundle.getMessage("LabelOnBoardBytes") + " 8 Bytes");
1429
1430                // cpMega Options
1431                cbx_cpnodeopt_SENDEOT.setSelected(curNode.iscpnodeBit(SerialNode.optbitNode_SENDEOT));
1432                cbx_cpnodeopt_BIT1.setSelected(false);
1433                cbx_cpnodeopt_BIT2.setSelected(false);
1434                cbx_cpnodeopt_BIT8.setSelected(curNode.iscpnodeBit(SerialNode.optbitNode_BIT8));
1435                cbx_cpnodeopt_BIT15.setSelected(curNode.iscpnodeBit(SerialNode.optbitNode_BIT15));
1436                break;
1437
1438            default:
1439                log.error("Unknown Node Type {}", nodeType);
1440                break;
1441        }
1442
1443        // CMRInet Options for all node types
1444        cbx_cmrinetopt_AUTOPOLL.setSelected(curNode.isCMRInetBit(SerialNode.optbitNet_AUTOPOLL));
1445        cbx_cmrinetopt_USECMRIX.setSelected(curNode.isCMRInetBit(SerialNode.optbitNet_USECMRIX));
1446        cbx_cmrinetopt_USEBCC.setSelected(curNode.isCMRInetBit(SerialNode.optbitNet_USEBCC));
1447        cbx_cmrinetopt_BIT8.setSelected(curNode.isCMRInetBit(SerialNode.optbitNet_BIT8));
1448        cbx_cmrinetopt_BIT15.setSelected(curNode.isCMRInetBit(SerialNode.optbitNet_BIT15));
1449
1450        // set up receive delay
1451        receiveDelay = curNode.getTransmissionDelay();
1452        receiveDelayField.setText(Integer.toString(receiveDelay));
1453
1454        // set up pulse width
1455        pulseWidth = curNode.getPulseWidth();
1456        pulseWidthField.setText(Integer.toString(pulseWidth));
1457
1458        // node description
1459        nodeDescText = curNode.getcmriNodeDesc();
1460        nodeDescription.setText(nodeDescText);
1461
1462        // set up card types
1463        for (int i = 0; i < 64; i++) {
1464            if (curNode.isOutputCard(i)) {
1465                cardType[i] = Bundle.getMessage("CardTypeOutput");
1466            } else if (curNode.isInputCard(i)) {
1467                cardType[i] = Bundle.getMessage("CardTypeInput");
1468            } else {
1469                cardType[i] = Bundle.getMessage("CardTypeNone");
1470            }
1471        }
1472
1473        if (nodeType == SerialNode.CPMEGA) {
1474            for (int i = 0; i < 8; i++) // Remap the onboard bytes
1475            {
1476                if (curNode.isOutputCard(i)) {
1477                    onBoardType[i] = Bundle.getMessage("CardTypeOutput");
1478                } else if (curNode.isInputCard(i)) {
1479                    onBoardType[i] = Bundle.getMessage("CardTypeInput");
1480                } else {
1481                    onBoardType[i] = Bundle.getMessage("CardTypeNone");
1482                }
1483            }
1484        }
1485
1486        // ensure that table displays correctly
1487        panel2.setVisible(false);
1488        panel2a.setVisible(false);
1489        if (nodeType == SerialNode.USIC_SUSIC) {
1490            panel2.setVisible(true);
1491        } else if (nodeType == SerialNode.SMINI) {
1492            panel2a.setVisible(true);
1493        } else if (nodeType == SerialNode.CPNODE) {
1494            panel2c.setVisible(false);
1495            panel2b.setVisible(true);
1496        } else if (nodeType == SerialNode.CPMEGA) {
1497            panel2c.setVisible(true);
1498            panel2b.setVisible(false);
1499        }
1500
1501    }
1502
1503    /**
1504     * Create and load edit function window.
1505     *
1506     * @param nodeaddr the node address
1507     */
1508    public void editNodeButtonActionPerformed(int nodeaddr) {
1509        // Find Serial Node address
1510
1511        nodeAddress = nodeaddr;
1512        if (nodeAddress < 0) {
1513            return;
1514        }
1515
1516        // get the SerialNode corresponding to this node address
1517        curNode = (SerialNode) _memo.getTrafficController().getNodeFromAddress(nodeAddress);
1518        if (curNode == null) {
1519            statusText1.setText(Bundle.getMessage("Error4"));
1520            statusText1.setVisible(true);
1521            errorInStatus1 = true;
1522            resetNotes2();
1523            return;
1524        }
1525
1526        // Load the node data into the window
1527        setupNodeInformationWindow(nodeAddress);
1528
1529        // Switch to edit notes
1530        editMode = true;
1531        statusText1.setText(editStatus1);
1532        statusText1.setVisible(true);
1533        statusText2.setText(editStatus2);
1534        statusText2.setVisible(true);
1535        statusText3.setText(editStatus3);
1536        statusText3.setVisible(true);
1537    }
1538
1539    /**
1540     * Handle update button clicked.
1541     */
1542    public void updateNodeButtonActionPerformed() {
1543        // get node information from window
1544        if (!readReceiveDelay()) {
1545            return;
1546        }
1547        if (!readPulseWidth()) {
1548            return;
1549        }
1550
1551        // check consistency of node information
1552        if (!checkConsistency()) {
1553            return;
1554        }
1555
1556        // update node information
1557        if (curNode.getNodeType() != nodeType) {
1558            // node type has changed
1559            curNode.setNodeType(nodeType);
1560        }
1561
1562        // cmri node description  c2
1563        curNode.setcmriNodeDesc(nodeDescription.getText());
1564        setNodeParameters();
1565        nodeTableModel.changeRow(selectedTableRow, curNode);
1566
1567        // Switch buttons
1568        changedNode = true;
1569        doneNodeButton.setVisible(true);
1570        updateNodeButton.setVisible(true);
1571        cancelNodeButton.setVisible(true);
1572        nodeAddrField.setVisible(false);
1573        nodeAddrStatic.setVisible(true);
1574
1575        // provide user feedback
1576        statusText1.setText(Bundle.getMessage("FeedBackUpdate") + " " + Integer.toString(nodeAddress));
1577        statusText2.setVisible(false);
1578        cancelNodeButton.setVisible(false);
1579        errorInStatus1 = true;
1580    }
1581
1582    /**
1583     * Handle delete button pressed.
1584     */
1585    public void deleteNodeButtonActionConfirm() {
1586        // confirm deletion with the user
1587
1588        if (JmriJOptionPane.OK_OPTION == JmriJOptionPane.showConfirmDialog(
1589                this, Bundle.getMessage("ConfirmDelete1") + "\n"
1590                + Bundle.getMessage("ConfirmDelete2"), Bundle.getMessage("ConfirmDeleteTitle"),
1591                JmriJOptionPane.OK_CANCEL_OPTION,
1592                JmriJOptionPane.WARNING_MESSAGE)) {
1593
1594            // delete this node
1595            _memo.getTrafficController().deleteNode(deleteNodeAddress);
1596            nodeTableModel.removeRow(selectedTableRow);
1597
1598            initializeNodes();
1599
1600            // provide user feedback
1601            resetNotes();
1602            statusText1.setText(Bundle.getMessage("FeedBackDelete") + " " + Integer.toString(deleteNodeAddress));
1603            statusText1.setVisible(true);
1604            errorInStatus1 = true;
1605            changedNode = true;
1606            deleteNodeButton.setVisible(false);
1607            doneNodeButton.setVisible(true);
1608            cancelNodeButton.setVisible(false);
1609            statusText2.setVisible(false);
1610        } else {
1611            // reset as needed
1612            resetNotes();
1613        }
1614    }
1615
1616    /**
1617     * Set up delete node window.
1618     *
1619     * @param nodeAddr the node address
1620     */
1621    public void deleteNodeButtonActionPerformed(int nodeAddr) {
1622        // Set up static node address
1623        nodeAddress = nodeAddr;
1624        nodeAddrStatic.setText(Integer.toString(nodeAddress));
1625        nodeAddrField.setVisible(false);
1626        nodeAddrStatic.setVisible(true);
1627
1628        // Find Serial Node address
1629        if (nodeAddress < 0) {
1630            log.info("nodeAddress < 0");
1631            return;
1632        }
1633
1634        // get the SerialNode corresponding to this node address
1635        curNode = (SerialNode) _memo.getTrafficController().getNodeFromAddress(nodeAddress);
1636        deleteNodeAddress = nodeAddr;
1637        // Load the node data into the window
1638
1639        setupNodeInformationWindow(nodeAddress);
1640
1641        // get the node corresponding to this node address
1642        if (curNode == null) {
1643            statusText1.setText(Bundle.getMessage("Error4"));
1644            statusText1.setVisible(true);
1645            errorInStatus1 = true;
1646            resetNotes2();
1647        } else {
1648            statusText1.setText(Bundle.getMessage("NotesDel3"));
1649            statusText1.setVisible(true);
1650        }
1651    }
1652
1653    /**
1654     * Handle done button clicked.
1655     */
1656    public void doneNodeButtonActionPerformed() {
1657        if (editMode) {
1658            // Reset
1659            editMode = false;
1660            curNode = null;
1661            // Switch buttons
1662            addNodeButton.setVisible(false);
1663            editNodeButton.setVisible(false);
1664            deleteNodeButton.setVisible(false);
1665            doneNodeButton.setVisible(true);
1666            updateNodeButton.setVisible(false);
1667            cancelNodeButton.setVisible(false);
1668            nodeAddrField.setVisible(false);
1669            nodeAddrStatic.setVisible(false);
1670        }
1671        if (changedNode) {
1672            JmriJOptionPane.showMessageDialog(this,
1673                    Bundle.getMessage("Reminder1") + "\n" + Bundle.getMessage("Reminder2"),
1674                    Bundle.getMessage("ReminderTitle"),
1675                    JmriJOptionPane.INFORMATION_MESSAGE);
1676        }
1677        changedNode = false;
1678
1679        setVisible(false);
1680        dispose();
1681    }
1682
1683    /**
1684     * Handle cancel button clicked.
1685     */
1686    public void cancelNodeButtonActionPerformed() {
1687        // Reset
1688        editMode = false;
1689        changedNode = false;
1690        curNode = null;
1691        // Switch buttons
1692        addNodeButton.setVisible(false);
1693        editNodeButton.setVisible(false);
1694        deleteNodeButton.setVisible(false);
1695        doneNodeButton.setVisible(false);
1696        updateNodeButton.setVisible(false);
1697        cancelNodeButton.setVisible(false);
1698        // make node address editable again
1699        nodeAddrField.setVisible(false);
1700        nodeAddrStatic.setVisible(false);
1701        // refresh notes panel
1702        // statusText1.setText(stdStatus1);
1703        // statusText2.setText(stdStatus2);
1704        // statusText3.setText(stdStatus3);
1705        setVisible(false);
1706        dispose();
1707    }
1708
1709    @Override
1710    public void windowClosing(java.awt.event.WindowEvent e) {
1711        doneNodeButtonActionPerformed();
1712        super.windowClosing(e);
1713    }
1714
1715    /**
1716     * Set the node parameters by type. Some parameters are specific to a
1717     * particular node type.
1718     */
1719    void setNodeParameters() {
1720        // receive delay is common for all node types
1721        int numInput = 0;
1722        int numOutput = 0;
1723        curNode.setTransmissionDelay(receiveDelay);
1724
1725        // pulse width is common for all node types
1726        curNode.setPulseWidth(pulseWidth);
1727
1728        // continue in a node specific way
1729        switch (nodeType) {
1730
1731            // SMINI
1732            case SerialNode.SMINI:
1733
1734                // Note: most parameters are set by default on creation
1735                int numSet = 0;
1736                // Configure 2-lead oscillating searchlights - first clear unneeded searchlights
1737                for (int j = 0; j < 47; j++) {
1738                    if (curNode.isSearchLightBit(j)) {
1739                        if (!firstSearchlight[j]) {
1740                            // this is the first bit of a deleted searchlight - clear it
1741                            curNode.clear2LeadSearchLight(j);
1742                            // skip over the second bit of the cleared searchlight
1743                            j++;
1744                        } else {
1745                            // this is the first bit of a kept searchlight - skip second bit
1746                            j++;
1747                        }
1748                    }
1749                }
1750                // Add needed searchlights that are not already configured
1751                for (int i = 0; i < 47; i++) {
1752                    if (firstSearchlight[i]) {
1753                        if (!curNode.isSearchLightBit(i)) {
1754                            // this is the first bit of an added searchlight
1755                            curNode.set2LeadSearchLight(i);
1756                        }
1757                        numSet++;
1758                    }
1759                }
1760                // consistency check
1761                if (numSet != num2LSearchLights) {
1762                    log.error("Inconsistent numbers of 2-lead searchlights. numSet = {}, num2LSearchLights = {}", Integer.toString(numSet), Integer.toString(num2LSearchLights));
1763                }
1764                break;
1765
1766            // USIC/SUSIC
1767            case SerialNode.USIC_SUSIC:
1768                // set number of bits per card
1769                curNode.setNumBitsPerCard(bitsPerCard);
1770                // configure the input/output cards
1771                numInput = 0;
1772                numOutput = 0;
1773                for (int i = 0; i < 64; i++) {
1774                    if ("No Card".equals(cardType[i])) {
1775                        curNode.setCardTypeByAddress(i, SerialNode.NO_CARD);
1776                    } else if ("Input Card".equals(cardType[i])) {
1777                        curNode.setCardTypeByAddress(i, SerialNode.INPUT_CARD);
1778                        numInput++;
1779                    } else if ("Output Card".equals(cardType[i])) {
1780                        curNode.setCardTypeByAddress(i, SerialNode.OUTPUT_CARD);
1781                        numOutput++;
1782                    } else {
1783                        log.error("Unexpected card type - {}", cardType[i]);
1784                    }
1785                }
1786                // consistency check
1787                if (numCards != (numOutput + numInput)) {
1788                    log.error("Inconsistent numbers of cards - setNodeParameters.");
1789                }
1790
1791                // Force created node to be polled as the default
1792                curNode.setOptNet_AUTOPOLL(1);
1793                break;
1794
1795            // CPNODE
1796            case SerialNode.CPNODE:
1797                // set number of bits per card
1798                bitsPerCard = 8;
1799                curNode.setNumBitsPerCard(bitsPerCard);
1800                numInput = 2;
1801                numOutput = 2;
1802                // configure the input/output cards
1803                for (int i = 4; i < 64; i++) // Skip the onboard bytes
1804                {
1805                    if ("No Card".equals(cardType[i])) {
1806                        curNode.setCardTypeByAddress(i, SerialNode.NO_CARD);
1807                    } else if ("Input Card".equals(cardType[i])) {
1808                        curNode.setCardTypeByAddress(i, SerialNode.INPUT_CARD);
1809                        numInput++;
1810                    } else if ("Output Card".equals(cardType[i])) {
1811                        curNode.setCardTypeByAddress(i, SerialNode.OUTPUT_CARD);
1812                        numOutput++;
1813                    } else {
1814                        log.error("Unexpected card type - {}", cardType[i]);
1815                    }
1816                }
1817
1818                // Set the node option bits.  Some are moved from the CMRInet options
1819                curNode.setOptNet_AUTOPOLL(cbx_cmrinetopt_AUTOPOLL.isSelected() ? 1 : 0);
1820
1821                curNode.setOptNode_SENDEOT(cbx_cpnodeopt_SENDEOT.isSelected() ? 1 : 0);
1822                curNode.setOptNode_USECMRIX(cbx_cmrinetopt_USECMRIX.isSelected() ? 1 : 0); // Copy from CMRInet
1823                curNode.setOptNode_USEBCC(cbx_cmrinetopt_USEBCC.isSelected() ? 1 : 0);     // Copy from CMRInet
1824                curNode.setOptNode_BIT8(cbx_cpnodeopt_BIT8.isSelected() ? 1 : 0);
1825                curNode.setOptNode_BIT15(cbx_cpnodeopt_BIT15.isSelected() ? 1 : 0);
1826
1827                break;
1828
1829            // CPMEGA
1830            case SerialNode.CPMEGA:
1831                // set number of bits per card
1832                bitsPerCard = 8;
1833                curNode.setNumBitsPerCard(bitsPerCard);
1834                numInput = 0;
1835                numOutput = 0;
1836
1837                for (int i = 0; i < 8; i++) // Pick up the onboard bytes
1838                {
1839                    if ("No Card".equals(onBoardType[i])) {
1840                        curNode.setCardTypeByAddress(i, SerialNode.NO_CARD);
1841                    } else if ("Input Card".equals(onBoardType[i])) {
1842                        curNode.setCardTypeByAddress(i, SerialNode.INPUT_CARD);
1843                        numInput++;
1844                    } else if ("Output Card".equals(onBoardType[i])) {
1845                        curNode.setCardTypeByAddress(i, SerialNode.OUTPUT_CARD);
1846                        numOutput++;
1847                    } else {
1848                        log.error("Unexpected card type - {}", onBoardType[i]);
1849                    }
1850                }
1851
1852                // configure the IOX cards
1853                for (int i = 8; i < 64; i++) // Skip the onboard bytes
1854                {
1855                    if ("No Card".equals(cardType[i])) {
1856                        curNode.setCardTypeByAddress(i, SerialNode.NO_CARD);
1857                    } else if ("Input Card".equals(cardType[i])) {
1858                        curNode.setCardTypeByAddress(i, SerialNode.INPUT_CARD);
1859                        numInput++;
1860                    } else if ("Output Card".equals(cardType[i])) {
1861                        curNode.setCardTypeByAddress(i, SerialNode.OUTPUT_CARD);
1862                        numOutput++;
1863                    } else {
1864                        log.error("Unexpected card type - {}", cardType[i]);
1865                    }
1866                }
1867
1868                // Set the node option bits.  Some are moved from the CMRInet options
1869                curNode.setOptNet_AUTOPOLL(1);  // Default node to be polled
1870
1871                curNode.setOptNode_SENDEOT(cbx_cpnodeopt_SENDEOT.isSelected() ? 1 : 0);
1872                curNode.setOptNode_USECMRIX(cbx_cmrinetopt_USECMRIX.isSelected() ? 1 : 0); // Copy from CMRInet
1873                curNode.setOptNode_USEBCC(cbx_cmrinetopt_USEBCC.isSelected() ? 1 : 0);     // Copy from CMRInet
1874                curNode.setOptNode_BIT8(cbx_cpnodeopt_BIT8.isSelected() ? 1 : 0);
1875                curNode.setOptNode_BIT15(cbx_cpnodeopt_BIT15.isSelected() ? 1 : 0);
1876
1877                break;
1878
1879            default:
1880                log.error("Unexpected node type in setNodeParameters- {}", Integer.toString(nodeType));
1881                break;
1882        }
1883
1884        // Set the node description for all types
1885        curNode.setcmriNodeDesc(nodeDescription.getText());
1886
1887        // Cause reinitialization of this Node to reflect these parameters
1888        _memo.getTrafficController().initializeSerialNode(curNode);
1889    }
1890
1891    /**
1892     * Reset the notes error after error display.
1893     */
1894    private void resetNotes() {
1895        if (errorInStatus1) {
1896            if (editMode) {
1897                statusText1.setText(editStatus1);
1898            } else {
1899                //  statusText1.setText(stdStatus1);
1900            }
1901            errorInStatus1 = false;
1902        }
1903        resetNotes2();
1904    }
1905
1906    /**
1907     * Reset the second line of Notes area.
1908     */
1909    private void resetNotes2() {
1910        if (errorInStatus2) {
1911            if (editMode) {
1912                statusText1.setText(editStatus2);
1913            } else {
1914                //                statusText2.setText(stdStatus2);
1915            }
1916            errorInStatus2 = false;
1917        }
1918    }
1919
1920    /**
1921     * Read node address and check for legal range. Sets the error message in
1922     * {@link #statusText1} if not legal.
1923     *
1924     * @return A node address in the range 0-127 if legal; -1 if not legal
1925     */
1926    private int readNodeAddress() {
1927        int addr = -1;
1928        try {
1929            addr = Integer.parseInt(nodeAddrField.getText());
1930        } catch (NumberFormatException e) {
1931            statusText1.setText(Bundle.getMessage("Error5"));
1932            statusText1.setVisible(true);
1933            errorInStatus1 = true;
1934            resetNotes2();
1935            return -1;
1936        }
1937
1938        if ((addr < 0) || (addr > 127)) {
1939//            statusText1.setText(Bundle.getMessage("Error6"));
1940//            statusText1.setVisible(true);
1941            errorInStatus1 = true;
1942            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("Error6"), "", JmriJOptionPane.ERROR_MESSAGE);
1943            resetNotes2();
1944            return -1;
1945        }
1946        return (addr);
1947    }
1948
1949    /**
1950     * Read receive delay from window. If an error is detected, a suitable error
1951     * message is placed in the Notes area.
1952     *
1953     * @return true if successful; false otherwise
1954     */
1955    protected boolean readReceiveDelay() {
1956        // get the transmission delay
1957        try {
1958            receiveDelay = Integer.parseInt(receiveDelayField.getText());
1959        } catch (NumberFormatException e) {
1960            statusText1.setText(Bundle.getMessage("Error7"));
1961            statusText1.setVisible(true);
1962            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("Error7"),
1963                "", JmriJOptionPane.ERROR_MESSAGE);
1964            receiveDelay = 0;
1965            errorInStatus1 = true;
1966            resetNotes2();
1967            return (false);
1968        }
1969        if (receiveDelay < 0) {
1970            statusText1.setText(Bundle.getMessage("Error8"));
1971            statusText1.setVisible(true);
1972            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("Error8"),
1973                "", JmriJOptionPane.ERROR_MESSAGE);
1974            receiveDelay = 0;
1975            errorInStatus1 = true;
1976            resetNotes2();
1977            return (false);
1978        }
1979        if (receiveDelay > 65535) {
1980            statusText1.setText(Bundle.getMessage("Error9"));
1981            statusText1.setVisible(true);
1982            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("Error9"),
1983                "", JmriJOptionPane.ERROR_MESSAGE);
1984            receiveDelay = 0;
1985            errorInStatus1 = true;
1986            resetNotes2();
1987            return (false);
1988        }
1989        // successful
1990        return true;
1991    }
1992
1993    /**
1994     * Read pulse width from window. If an error is detected, a suitable error
1995     * message is placed in the Notes area.
1996     *
1997     * @return true if successful; false otherwise
1998     */
1999    protected boolean readPulseWidth() {
2000        // get the pulse width
2001        try {
2002            pulseWidth = Integer.parseInt(pulseWidthField.getText());
2003        } catch (NumberFormatException e) {
2004            statusText1.setText(Bundle.getMessage("Error18"));
2005            statusText1.setVisible(true);
2006            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("Error18"),
2007                "", JmriJOptionPane.ERROR_MESSAGE);
2008            pulseWidth = 500;
2009            errorInStatus1 = true;
2010            resetNotes2();
2011            return (false);
2012        }
2013        if (pulseWidth < 100) {
2014            statusText1.setText(Bundle.getMessage("Error16"));
2015            statusText1.setVisible(true);
2016            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("Error16"),
2017                "", JmriJOptionPane.ERROR_MESSAGE);
2018            pulseWidth = 100;
2019            pulseWidthField.setText(Integer.toString(pulseWidth));
2020            errorInStatus1 = true;
2021            resetNotes2();
2022            return (false);
2023        }
2024        if (pulseWidth > 10000) {
2025            statusText1.setText(Bundle.getMessage("Error17"));
2026            statusText1.setVisible(true);
2027            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("Error17"),
2028                "", JmriJOptionPane.ERROR_MESSAGE);
2029            pulseWidth = 500;
2030            pulseWidthField.setText(Integer.toString(pulseWidth));
2031            errorInStatus1 = true;
2032            resetNotes2();
2033            return (false);
2034        }
2035        // successful
2036        return true;
2037    }
2038
2039    /**
2040     * Check for consistency errors by node type. If an error is detected, a
2041     * suitable error message is placed in the Notes area.
2042     *
2043     * @return true if successful; false otherwise
2044     */
2045    protected boolean checkConsistency() {
2046        switch (nodeType) {
2047            case SerialNode.SMINI:
2048                // ensure that number of searchlight bits is consistent
2049                int numBits = 0;
2050                for (int i = 0; i < 48; i++) {
2051                    if (searchlightBits[i]) {
2052                        numBits++;
2053                    }
2054                }
2055                if ((2 * num2LSearchLights) != numBits) {
2056                    statusText1.setText(Bundle.getMessage("Error10"));
2057                    statusText1.setVisible(true);
2058                    JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("Error10"),
2059                        "", JmriJOptionPane.ERROR_MESSAGE);
2060                    errorInStatus1 = true;
2061                    resetNotes2();
2062                    return (false);
2063                }
2064                break;
2065            case SerialNode.USIC_SUSIC:
2066                // ensure that at least one card is defined
2067                numCards = 0;
2068                boolean atNoCard = false;
2069                for (int i = 0; i < 64; i++) {
2070                    if ((cardType[i].equals(Bundle.getMessage("CardTypeOutput")))
2071                            || (cardType[i].equals(Bundle.getMessage("CardTypeInput")))) {
2072                        if (atNoCard) {
2073                            // gap error
2074                            statusText1.setText(Bundle.getMessage("Error11"));
2075                            statusText1.setVisible(true);
2076                            statusText2.setText(Bundle.getMessage("Error12"));
2077                            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("Error11") + Bundle.getMessage("Error12"),
2078                                "", JmriJOptionPane.ERROR_MESSAGE);
2079                            errorInStatus1 = true;
2080                            errorInStatus2 = true;
2081                            return (false);
2082                        } else {
2083                            numCards++;
2084                        }
2085                    } else if (cardType[i].equals(Bundle.getMessage("CardTypeNone"))) {
2086                        atNoCard = true;
2087                    }
2088                }
2089                // ensure that at least one card has been defined
2090                if (numCards <= 0) {
2091                    // no card error
2092                    statusText1.setText(Bundle.getMessage("Error13"));
2093                    statusText2.setText(Bundle.getMessage("Error14"));
2094                    statusText1.setVisible(true);
2095                    JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("Error13") + Bundle.getMessage("Error14"),
2096                        "", JmriJOptionPane.ERROR_MESSAGE);
2097                    errorInStatus1 = true;
2098                    errorInStatus2 = true;
2099                    return (false);
2100                }
2101                // check that card size is 24 or 32 bit
2102                if ((bitsPerCard != 24) && (bitsPerCard != 32)) {
2103                    // card size error
2104                    statusText1.setText(Bundle.getMessage("Error15"));
2105                    statusText1.setVisible(true);
2106                    errorInStatus1 = true;
2107                    JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("Error15"),
2108                        "", JmriJOptionPane.ERROR_MESSAGE);
2109                    resetNotes2();
2110                    return (false);
2111                }
2112                // further checking if in Edit mode
2113                if (editMode) {
2114                    // get pre-edit numbers of cards
2115                    int numOutput = curNode.numOutputCards();
2116                    int numInput = curNode.numInputCards();
2117                    // will the number of cards be reduced by this edit?
2118                    if (numCards < (numOutput + numInput)) {
2119                        if ( JmriJOptionPane.YES_OPTION != JmriJOptionPane.showConfirmDialog(this,
2120                                Bundle.getMessage("ConfirmUpdate1") + "\n"
2121                                + Bundle.getMessage("ConfirmUpdate2") + "\n"
2122                                + Bundle.getMessage("ConfirmUpdate3"),
2123                                Bundle.getMessage("ConfirmUpdateTitle"),
2124                                JmriJOptionPane.YES_NO_OPTION,
2125                                JmriJOptionPane.WARNING_MESSAGE)) {
2126                            // user did not click yes - cancel the update
2127                            return (false);
2128                        }
2129                    }
2130                }
2131                break;
2132            case SerialNode.CPNODE:
2133                for (int j = 0; j < 64; j++) {
2134                    if ((cardType[j].equals(Bundle.getMessage("CardTypeOutput")))
2135                            || (cardType[j].equals(Bundle.getMessage("CardTypeInput")))) {
2136                        numCards++;
2137                    }
2138                }
2139                break;
2140            case SerialNode.CPMEGA:
2141                for (int j = 0; j < 64; j++) {
2142                    if ((cardType[j].equals(Bundle.getMessage("CardTypeOutput")))
2143                            || (cardType[j].equals(Bundle.getMessage("CardTypeInput")))) {
2144                        numCards++;
2145                    }
2146                }
2147                break;
2148
2149            // here add code for other types of nodes
2150            default:
2151                log.warn("Unexpected node type - {}", nodeType);
2152                break;
2153        }
2154        return true;
2155    }
2156
2157    /**
2158     * Set up table for selecting card type by address for USIC_SUSIC nodes
2159     */
2160    public class CardConfigModel extends AbstractTableModel {
2161
2162        @Override
2163        public String getColumnName(int c) {
2164            return cardConfigColumnNames[c];
2165        }
2166
2167        @Override
2168        public Class<?> getColumnClass(int c) {
2169            return String.class;
2170        }
2171
2172        @Override
2173        public int getColumnCount() {
2174            return 2;
2175        }
2176
2177        @Override
2178        public int getRowCount() {
2179            return 64;
2180        }
2181
2182        @Override
2183        public Object getValueAt(int r, int c) {
2184            if (c == 0) {
2185                return "       " + Integer.toString(r);
2186            } else if (c == 1) {
2187                return "  " + cardType[r];
2188            }
2189            return "";
2190        }
2191
2192        @Override
2193        public void setValueAt(Object type, int r, int c) {
2194            if (c == 1) {
2195                cardType[r] = (String) type;
2196            }
2197        }
2198
2199        @Override
2200        public boolean isCellEditable(int r, int c) {
2201            return (c == 1);
2202        }
2203
2204        public static final int ADDRESS_COLUMN = 0;
2205        public static final int TYPE_COLUMN = 1;
2206    }
2207    private final String[] cardConfigColumnNames = {
2208        Bundle.getMessage("HeadingCardAddress"),
2209        Bundle.getMessage("HeadingCardType")
2210    };
2211    private final String[] cardType = new String[64];
2212    private final String[] onBoardType = new String[16];
2213
2214    /**
2215     * Set up model for SMINI table for designating oscillating 2-lead
2216     * searchlights
2217     */
2218    public class SearchlightConfigModel extends AbstractTableModel {
2219
2220        @Override
2221        public String getColumnName(int c) {
2222            return searchlightConfigColumnNames[c];
2223        }
2224
2225        @Override
2226        public Class<?> getColumnClass(int c) {
2227            if (c > 0) {
2228                return Boolean.class;
2229            } else {
2230                return String.class;
2231            }
2232        }
2233
2234        @Override
2235        public int getColumnCount() {
2236            return 9;
2237        }
2238
2239        @Override
2240        public int getRowCount() {
2241            return 6;
2242        }
2243
2244        @Override
2245        public Object getValueAt(int r, int c) {
2246            if (c == 0) {
2247                switch (r) {
2248                    case 0:
2249                        return ("Card 0 Port A");
2250                    case 1:
2251                        return ("Card 0 Port B");
2252                    case 2:
2253                        return ("Card 0 Port C");
2254                    case 3:
2255                        return ("Card 1 Port A");
2256                    case 4:
2257                        return ("Card 1 Port B");
2258                    case 5:
2259                        return ("Card 1 Port C");
2260                    default:
2261                        return ("");
2262                }
2263            } else {
2264                int index = (r * 8) + (c - 1);
2265                return searchlightBits[index];
2266            }
2267        }
2268
2269        @Override
2270        public void setValueAt(Object type, int r, int c) {
2271            if (c > 0) {
2272                int index = (r * 8) + (c - 1);
2273                if (!((Boolean) type)) {
2274                    searchlightBits[index] = false;
2275                    if (firstSearchlight[index]) {
2276                        searchlightBits[index + 1] = false;
2277                        firstSearchlight[index] = false;
2278                    } else {
2279                        searchlightBits[index - 1] = false;
2280                        firstSearchlight[index - 1] = false;
2281                    }
2282                    num2LSearchLights--;
2283                } else {
2284                    if (index < 47) {
2285                        if (!searchlightBits[index] && !searchlightBits[index + 1]) {
2286                            searchlightBits[index] = true;
2287                            searchlightBits[index + 1] = true;
2288                            firstSearchlight[index] = true;
2289                            firstSearchlight[index + 1] = false;
2290                            if (index > 0) {
2291                                firstSearchlight[index - 1] = false;
2292                            }
2293                            num2LSearchLights++;
2294                        }
2295                    }
2296                }
2297                panel2a.setVisible(false);
2298                panel2a.setVisible(true);
2299            }
2300        }
2301
2302        @Override
2303        public boolean isCellEditable(int r, int c) {
2304            return (c != 0);
2305        }
2306
2307        public static final int PORT_COLUMN = 0;
2308    }
2309    private final String[] searchlightConfigColumnNames = {Bundle.getMessage("HeadingPort"), "0", "1", "2", "3", "4", "5", "6", "7"};
2310    private final boolean[] searchlightBits = new boolean[48];   // true if this bit is a searchlight bit
2311    private final boolean[] firstSearchlight = new boolean[48];  // true if first of a pair of searchlight bits
2312
2313    /**
2314     * Handles checkboxes for cpNode options
2315     */
2316    private class HandlerClass implements ItemListener {
2317
2318        @Override
2319        public void itemStateChanged(ItemEvent e) {
2320            JCheckBox checkbox = (JCheckBox) e.getSource();
2321
2322            if (checkbox == cbx_cmrinetopt_AUTOPOLL) {
2323                curNode.setCMRInetOpts(SerialNode.optbitNet_AUTOPOLL, (cbx_cmrinetopt_AUTOPOLL.isSelected() ? 1 : 0));
2324            } else if (checkbox == cbx_cmrinetopt_USECMRIX) {
2325                curNode.setCMRInetOpts(SerialNode.optbitNet_USECMRIX, (cbx_cmrinetopt_USECMRIX.isSelected() ? 1 : 0));
2326                curNode.setcpnodeOpts(SerialNode.optbitNet_USECMRIX, curNode.getCMRInetOpts(SerialNode.optbitNet_USECMRIX));
2327            } else if (checkbox == cbx_cmrinetopt_USEBCC) {
2328                curNode.setCMRInetOpts(SerialNode.optbitNet_USEBCC, (cbx_cmrinetopt_USEBCC.isSelected() ? 1 : 0));
2329                curNode.setcpnodeOpts(SerialNode.optbitNet_USEBCC, curNode.getCMRInetOpts(SerialNode.optbitNet_USEBCC));
2330            } else if (checkbox == cbx_cmrinetopt_BIT8) {
2331                curNode.setCMRInetOpts(SerialNode.optbitNet_BIT8, (cbx_cmrinetopt_BIT8.isSelected() ? 1 : 0));
2332            } else if (checkbox == cbx_cmrinetopt_BIT15) {
2333                curNode.setCMRInetOpts(SerialNode.optbitNet_BIT15, (cbx_cmrinetopt_BIT15.isSelected() ? 1 : 0));
2334            } else if (checkbox == cbx_cpnodeopt_SENDEOT) {
2335                curNode.setcpnodeOpts(SerialNode.optbitNode_SENDEOT, (cbx_cpnodeopt_SENDEOT.isSelected() ? 1 : 0));
2336            } else if (checkbox == cbx_cpnodeopt_BIT1) {
2337                cbx_cpnodeopt_BIT1.setSelected(false);
2338            } else if (checkbox == cbx_cpnodeopt_BIT2) {
2339                cbx_cpnodeopt_BIT2.setSelected(false);
2340            } else if (checkbox == cbx_cpnodeopt_BIT8) {
2341                curNode.setcpnodeOpts(SerialNode.optbitNode_BIT8, (cbx_cpnodeopt_BIT8.isSelected() ? 1 : 0));
2342            } else if (checkbox == cbx_cpnodeopt_BIT15) {
2343                curNode.setcpnodeOpts(SerialNode.optbitNode_BIT15, (cbx_cpnodeopt_BIT15.isSelected() ? 1 : 0));
2344            }
2345            changedNode = true;
2346        }
2347    }
2348
2349    /**
2350     * Set up table for selecting card type by address for CPNODE/CPMEGA nodes
2351     */
2352    public class CPnodeConfigModel extends AbstractTableModel {
2353
2354        @Override
2355        public String getColumnName(int c) {
2356            return cpnodeConfigColumnNames[c];
2357        }
2358
2359        @Override
2360        public Class<?> getColumnClass(int c) {
2361            return String.class;
2362        }
2363
2364        @Override
2365        public int getColumnCount() {
2366            return CARDTYPE_COLUMN+1;
2367        }
2368
2369        @Override
2370        public int getRowCount() {
2371            return 16;
2372        }
2373
2374        @Override
2375        public Object getValueAt(int r, int c) {
2376            String[] cdPort = {"  A", "  B"};
2377            String val = "     ";
2378            switch (c) {
2379                case CARD_COLUMN:
2380                    val = Integer.toString(r+2);
2381                    return "   " + val;
2382                case CARDNUM_COLUMN:
2383                    int i = r / 2;
2384                    if (r % 2 == 0) {
2385                        val = Integer.toHexString(0x20 + i) + " ";
2386                    }
2387                    return "    " + val + "     " + cdPort[(r % 2)];
2388                case CARDTYPE_COLUMN:
2389                    return " " + cardType[r + cpNodeOnboard];
2390                default:
2391                    return "";
2392            }
2393        }
2394
2395        @Override
2396        public void setValueAt(Object type, int r, int c) {
2397            if (c == CARDTYPE_COLUMN) {
2398                cardType[r + cpNodeOnboard] = (String) type;
2399            }
2400        }
2401
2402        @Override
2403        public boolean isCellEditable(int r, int c) {
2404            return (c == CARDTYPE_COLUMN);
2405        }
2406
2407        public static final int CARD_COLUMN = 0;
2408        public static final int CARDNUM_COLUMN = 1;
2409        public static final int CARDTYPE_COLUMN = 2;
2410    }
2411    private final String[] cpnodeConfigColumnNames = {"Card", "IOX Addr Port", "Port Type"};
2412
2413    /**
2414     * Set up table for selecting card type by address for CPNODE/CPMEGA nodes
2415     */
2416    public class OSnodeConfigModel extends AbstractTableModel {
2417
2418        @Override
2419        public String getColumnName(int c) {
2420            return osnodeConfigColumnNames[c];
2421        }
2422
2423        @Override
2424        public Class<?> getColumnClass(int c) {
2425            return String.class;
2426        }
2427
2428        @Override
2429        public int getColumnCount() {
2430            return 2;
2431        }
2432
2433        @Override
2434        public int getRowCount() {
2435            return 8;
2436        }
2437
2438        @Override
2439        public Object getValueAt(int r, int c) {
2440            switch (c) {
2441                case CARDNUM_COLUMN:
2442                    int i = r;
2443                    return "    " + i + " ";
2444                case CARDTYPE_COLUMN:
2445                    return " " + onBoardType[r];
2446                default:
2447                    return "";
2448            }
2449        }
2450
2451        @Override
2452        public void setValueAt(Object type, int r, int c) {
2453            if (c == CARDTYPE_COLUMN) {
2454                onBoardType[r] = (String) type;
2455            }
2456        }
2457
2458        @Override
2459        public boolean isCellEditable(int r, int c) {
2460            return (c == 1);
2461        }
2462
2463        public static final int CARDNUM_COLUMN = 0;
2464        public static final int CARDTYPE_COLUMN = 1;
2465    }
2466    private final String[] osnodeConfigColumnNames = {"  Byte ", "Port Type"};
2467
2468    private final int[] cpnodeOpts = new int[16];  // Local storage for node options
2469    private final int[] cmrinetOpts = new int[16];  // Local storage for node options
2470
2471    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NodeConfigManagerFrame.class);
2472
2473}