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