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