001package jmri.jmrix.cmri.serial.nodeiolist;
002
003import java.awt.*;
004import java.awt.event.ActionEvent;
005import java.awt.event.ActionListener;
006import java.io.IOException;
007import java.util.ArrayList;
008
009import javax.swing.*;
010import javax.swing.border.Border;
011import javax.swing.table.*;
012
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016import jmri.jmrix.cmri.CMRISystemConnectionMemo;
017import jmri.jmrix.cmri.serial.SerialNode;
018import jmri.util.davidflanagan.CompatibleHardcopyWriter;
019
020/**
021 * Frame for running CMRI assignment list.
022 *
023 * @author Dave Duchamp Copyright (C) 2006
024 * @author Chuck Catania Copyright (C) 2014, 2016, 2017
025 */
026public class NodeIOListFrame extends jmri.util.JmriJFrame {
027
028    ArrayList<SerialNode> cmriNode = new ArrayList<SerialNode>();
029
030    protected boolean inputSelected = true;  // true if displaying input assignments, false for output
031    protected SerialNode selNode = null;
032    public int selNodeNum = 0;  // Address (ua) of selected Node
033    public int numBits = 48;  // number of bits in assignment table
034    public int numInputBits = 24;  // number of input bits for selected node
035    public int numOutputBits = 48; // number of output bits for selected node
036
037    // node select pane items
038    JLabel nodeLabel = new JLabel(Bundle.getMessage("NodeBoxLabel") + " ");
039    JLabel nodeAddrBox = new JLabel("??");
040    ButtonGroup bitTypeGroup = new ButtonGroup();
041    JRadioButton inputBits = new JRadioButton(Bundle.getMessage("ShowInputButton") + "   ", true);
042    JRadioButton outputBits = new JRadioButton(Bundle.getMessage("ShowOutputButton"), false);
043    JLabel nodeInfoText = new JLabel("Node Information Text");
044
045    JLabel nodeDesc = new JLabel("Description:");
046
047    // assignment pane items
048    protected JPanel assignmentPanel = null;
049    protected Border inputBorder = BorderFactory.createEtchedBorder();
050    protected Border inputBorderTitled = BorderFactory.createTitledBorder(inputBorder,
051            Bundle.getMessage("AssignmentPanelInputName"));
052    protected Border outputBorder = BorderFactory.createEtchedBorder();
053    protected Border outputBorderTitled = BorderFactory.createTitledBorder(outputBorder,
054            Bundle.getMessage("AssignmentPanelOutputName"));
055    protected JTable assignmentTable = null;
056    protected TableModel assignmentListModel = null;
057
058    // button pane items
059    JButton printButton = new JButton(Bundle.getMessage("PrintButtonText"));
060    JButton doneButton = new JButton(Bundle.getMessage("DoneButtonText"));
061
062    NodeIOListFrame curFrame;
063    private CMRISystemConnectionMemo _memo = null;
064
065    public NodeIOListFrame(CMRISystemConnectionMemo memo) {
066        super();
067        _memo = memo;
068        curFrame = this;
069    }
070
071    /**
072     * {@inheritDoc}
073     */
074    @Override
075    public void initComponents() {
076
077        // set the frame's initial state
078        setTitle(Bundle.getMessage("WindowTitle") + Bundle.getMessage("WindowConnectionMemo") + _memo.getUserName()); // NOI18N
079        setSize(500, 300);
080        Container contentPane = getContentPane();
081        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
082
083        // Set up the node selection panel
084        initializeNodes();
085        if (cmriNode.size() > 0) {
086            inputBits.addActionListener(new ActionListener() {
087                @Override
088                public void actionPerformed(ActionEvent event) {
089                    if (inputSelected == false) {
090                        inputSelected = true;
091                        displayNodeIOBits(selNodeNum);
092                    }
093                }
094            });
095            outputBits.addActionListener(new ActionListener() {
096                @Override
097                public void actionPerformed(ActionEvent event) {
098                    if (inputSelected == true) {
099                        inputSelected = false;
100                        displayNodeIOBits(selNodeNum);
101                    }
102                }
103            });
104        } else {
105            nodeInfoText.setText(Bundle.getMessage("NoNodesError"));
106        }
107        nodeLabel.setToolTipText(Bundle.getMessage("NodeBoxTip"));
108        inputBits.setToolTipText(Bundle.getMessage("ShowInputTip"));
109        outputBits.setToolTipText(Bundle.getMessage("ShowOutputTip"));
110
111        JPanel panel1 = new JPanel();
112        panel1.setLayout(new BoxLayout(panel1, BoxLayout.Y_AXIS));
113        JPanel panel11 = new JPanel();
114        panel11.add(nodeLabel);
115        bitTypeGroup.add(outputBits);
116        bitTypeGroup.add(inputBits);
117        panel11.add(inputBits);
118        panel11.add(outputBits);
119        JPanel panel12 = new JPanel();
120        panel12.add(nodeInfoText);
121
122        JPanel panel13 = new JPanel();
123        panel13.add(nodeDesc);
124        panel13.setVisible(true);
125        nodeDesc.setVisible(false);
126        panel1.add(panel13);
127
128        panel1.add(panel11);
129        panel1.add(panel12);
130
131        Border panel1Border = BorderFactory.createEtchedBorder();
132        Border panel1Titled = BorderFactory.createTitledBorder(panel1Border, Bundle.getMessage("NodePanelName") + " " + _memo.getUserName());
133        panel1.setBorder(panel1Titled);
134        contentPane.add(panel1);
135
136// Set up the assignment panel
137        assignmentPanel = new JPanel();
138        assignmentPanel.setLayout(new BoxLayout(assignmentPanel, BoxLayout.Y_AXIS));
139        assignmentListModel = new AssignmentTableModel();
140        assignmentTable = new JTable(assignmentListModel);
141        assignmentTable.setRowSelectionAllowed(false);
142        assignmentTable.setPreferredScrollableViewportSize(new java.awt.Dimension(300, 350));
143
144        TableColumnModel assignmentColumnModel = assignmentTable.getColumnModel();
145        TableColumn bitColumn = assignmentColumnModel.getColumn(AssignmentTableModel.BIT_COLUMN);
146        bitColumn.setMinWidth(20);
147        bitColumn.setMaxWidth(40);
148        bitColumn.setResizable(true);
149        TableColumn addressColumn = assignmentColumnModel.getColumn(AssignmentTableModel.ADDRESS_COLUMN);
150        addressColumn.setMinWidth(40);
151        addressColumn.setMaxWidth(85);
152        addressColumn.setResizable(true);
153        TableColumn sysColumn = assignmentColumnModel.getColumn(AssignmentTableModel.SYSNAME_COLUMN);
154        sysColumn.setMinWidth(90);
155        sysColumn.setMaxWidth(150);
156        sysColumn.setResizable(true);
157        TableColumn userColumn = assignmentColumnModel.getColumn(AssignmentTableModel.USERNAME_COLUMN);
158        userColumn.setMinWidth(90);
159        userColumn.setMaxWidth(250);
160        userColumn.setResizable(true);
161        assignmentColumnModel.getColumn(AssignmentTableModel.COMMENT_COLUMN);
162        userColumn.setMinWidth(90);
163        userColumn.setMaxWidth(250);
164        userColumn.setResizable(true);
165        JScrollPane assignmentScrollPane = new JScrollPane(assignmentTable);
166        assignmentPanel.add(assignmentScrollPane, BorderLayout.CENTER);
167        if (inputSelected) {
168            assignmentPanel.setBorder(inputBorderTitled);
169        } else {
170            assignmentPanel.setBorder(outputBorderTitled);
171        }
172        contentPane.add(assignmentPanel);
173
174        // Set up Print button
175        JPanel panel3 = new JPanel();
176        panel3.setLayout(new FlowLayout());
177        printButton.setVisible(true);
178        printButton.setToolTipText(Bundle.getMessage("PrintButtonTip"));
179        if (cmriNode.size() > 0) {
180            printButton.addActionListener(new java.awt.event.ActionListener() {
181                @Override
182                public void actionPerformed(java.awt.event.ActionEvent e) {
183                    printButtonActionPerformed(e);
184                }
185            });
186        }
187        panel3.add(printButton);
188        contentPane.add(panel3);
189
190        doneButton.setVisible(true);
191        doneButton.setToolTipText(Bundle.getMessage("DoneButtonTip"));
192        if (cmriNode.size() > 0) {
193            doneButton.addActionListener(new java.awt.event.ActionListener() {
194                @Override
195                public void actionPerformed(java.awt.event.ActionEvent e) {
196                    doneButtonActionPerformed();
197                }
198            });
199        }
200        panel3.add(doneButton);
201
202        addHelpMenu("package.jmri.jmrix.cmri.serial.assignment.ListFrame", true);
203
204        // pack for display
205        pack();
206    }
207
208    /**
209     * Method to initialize configured nodes and set up the node select combo
210     * box
211     */
212    public void initializeNodes() {
213        // get all configured nodes
214
215        SerialNode node = (SerialNode) _memo.getTrafficController().getNode(0);
216
217        int index = 0;  //1
218        while (node != null) {
219            cmriNode.add(node);
220            node = (SerialNode) _memo.getTrafficController().getNode(index);
221            index++;
222        }
223//            node = (SerialNode) SerialTrafficController.instance().getNode(index);
224//            index ++;
225    }
226
227    /**
228     * Method to handle selection of a Node for info display.
229     * @param nodeID node ID number.
230     */
231    public void displayNodeIOBits(int nodeID) {
232        nodeLabel.setText("Node: " + nodeID + "     ");
233        selNodeNum = nodeID;
234        nodeDesc.setText("");
235        for (int i = 0; i < cmriNode.size(); i++) {
236            if (cmriNode.get(i).getNodeAddress() == nodeID) {
237                selNode = cmriNode.get(i);
238            }
239        }
240        String nodeType = "";
241        int type = selNode.getNodeType();
242        int bitsPerCard = selNode.getNumBitsPerCard();
243        int numInputCards = selNode.numInputCards();
244        int numOutputCards = selNode.numOutputCards();
245
246        switch (type) {
247            case SerialNode.SMINI:
248                numInputBits = 24;
249                numOutputBits = 48;
250                nodeType = "SMINI - ";
251                break;
252            case SerialNode.USIC_SUSIC:
253                numInputBits = bitsPerCard * numInputCards;
254                numOutputBits = bitsPerCard * numOutputCards;
255                nodeType = "USIC_SUSIC - ";
256                break;
257            case SerialNode.CPNODE:
258                numInputBits = bitsPerCard * numInputCards;
259                numOutputBits = bitsPerCard * numOutputCards;
260                nodeType = "CPNODE - ";
261                break;
262            case SerialNode.CPMEGA:
263                numInputBits = bitsPerCard * numInputCards;
264                numOutputBits = bitsPerCard * numOutputCards;
265                nodeType = "CPMEGA - ";
266                break;
267            case SerialNode.ESP32NODE:
268                numInputBits = bitsPerCard * numInputCards;
269                numOutputBits = bitsPerCard * numOutputCards;
270                nodeType = "ESP32Node - ";
271                break;
272            default:
273                break;
274        }
275        nodeInfoText.setText(nodeType + bitsPerCard + " " + Bundle.getMessage("BitsPerCard") + ", "
276                + numInputBits + " " + Bundle.getMessage("InputBitsAnd") + " "
277                + numOutputBits + " " + Bundle.getMessage("OutputBits"));
278        String name = selNode.getcmriNodeDesc();
279        if (name != null && (!name.isEmpty())) {
280            nodeDesc.setText(Bundle.getMessage("NodeDesc") + " " + name);
281            nodeDesc.setVisible(true);
282        }
283
284        // initialize for input or output assignments
285        if (inputSelected) {
286            numBits = numInputBits;
287            assignmentPanel.setBorder(inputBorderTitled);
288        } else {
289            numBits = numOutputBits;
290            assignmentPanel.setBorder(outputBorderTitled);
291        }
292        ((AssignmentTableModel) assignmentListModel).fireTableDataChanged();
293    }
294
295    /*  Done button handler */
296    public void doneButtonActionPerformed() {
297        setVisible(false);
298        dispose();
299    }
300
301    /**
302     * Method to handle print button in List Frame.
303     * @param e unused.
304     */
305    public void printButtonActionPerformed(java.awt.event.ActionEvent e) {
306        int[] colWidth = new int[5];
307        // initialize column widths
308        TableColumnModel assignmentColumnModel = assignmentTable.getColumnModel();
309        colWidth[0] = assignmentColumnModel.getColumn(AssignmentTableModel.BIT_COLUMN).getWidth();
310        colWidth[1] = assignmentColumnModel.getColumn(AssignmentTableModel.ADDRESS_COLUMN).getWidth();
311        colWidth[2] = assignmentColumnModel.getColumn(AssignmentTableModel.SYSNAME_COLUMN).getWidth();
312        colWidth[3] = assignmentColumnModel.getColumn(AssignmentTableModel.USERNAME_COLUMN).getWidth();
313        colWidth[4] = assignmentColumnModel.getColumn(AssignmentTableModel.COMMENT_COLUMN).getWidth();
314        // set up a page title
315        String head;
316        if (inputSelected) {
317            head = Bundle.getMessage("Connection") +" "+ _memo.getUserName() + "  "+ Bundle.getMessage("AssignmentPanelInputName") + " "
318                    + Bundle.getMessage("NodeBoxLabel") + " " + selNodeNum + "  ";
319        } else {
320            head = Bundle.getMessage("Connection") +" "+ _memo.getUserName() + "  " + Bundle.getMessage("AssignmentPanelOutputName") + " "
321                    + Bundle.getMessage("NodeBoxLabel") + " " + selNodeNum + "  ";
322        }
323        // initialize a printer writer
324        CompatibleHardcopyWriter writer = null;
325        try {
326            writer = new CompatibleHardcopyWriter(curFrame, head, 10, .8, .5, .5, .5, false);
327        } catch (CompatibleHardcopyWriter.PrintCanceledException ex) {
328            //log.debug("Print cancelled");
329            return;
330        }
331        writer.increaseLineSpacing(20);
332        // print the assignments
333        ((AssignmentTableModel) assignmentListModel).printTable(writer, colWidth);
334    }
335
336    /**
337     * Set up table for displaying bit assignments
338     */
339    public class AssignmentTableModel extends AbstractTableModel {
340
341        private String free = Bundle.getMessage("AssignmentFree");
342        private int curRow = -1;
343        private String curRowSysName = "";
344
345        @Override
346        public String getColumnName(int c) {
347            return assignmentTableColumnNames[c];
348        }
349
350        @Override
351        public Class<?> getColumnClass(int c) {
352            return String.class;
353        }
354
355        @Override
356        public boolean isCellEditable(int r, int c) {
357            return false;
358        }
359
360        @Override
361        public int getColumnCount() {
362            return MAX_COLS;
363        }
364
365        @Override
366        public int getRowCount() {
367            return numBits;
368        }
369
370        @Override
371        public Object getValueAt(int r, int c) {
372            if (c == BIT_COLUMN) {
373                return Integer.toString(r + 1);
374            } else if (c == ADDRESS_COLUMN) {
375                return Integer.toString((selNodeNum * 1000) + r + 1);
376            } else if (c == SYSNAME_COLUMN) {
377                String sName = null;
378                if (curRow != r) {
379
380                    if (inputSelected) {
381                        sName = _memo.isInputBitFree(selNodeNum, (r + 1));
382                    } else {
383                        sName = _memo.isOutputBitFree(selNodeNum, (r + 1));
384                    }
385                    curRow = r;
386                    curRowSysName = sName;
387                } else {
388                    sName = curRowSysName;
389                }
390                if (sName == null) {
391                    return (free);
392                } else {
393                    return sName;
394                }
395            } else if (c == USERNAME_COLUMN) {
396                String sName = null;
397                if (curRow != r) {
398
399                    if (inputSelected) {
400                        sName = _memo.isInputBitFree(selNodeNum, (r + 1));
401                    } else {
402                        sName = _memo.isOutputBitFree(selNodeNum, (r + 1));
403                    }
404
405                    curRow = r;
406                    curRowSysName = sName;
407                } else {
408                    sName = curRowSysName;
409                }
410                if (sName == null) {
411                    return ("");
412                } else {
413                    return (_memo.getUserNameFromSystemName(sName));
414                }
415            } else if (c == COMMENT_COLUMN) {
416                String sName = null;
417                if (curRow != r) {
418                    if (inputSelected) {
419                        sName = _memo.isInputBitFree(selNodeNum, (r + 1));
420                    } else {
421                        sName = _memo.isOutputBitFree(selNodeNum, (r + 1));
422                    }
423                    curRow = r;
424                    curRowSysName = sName;
425                } else {
426                    sName = curRowSysName;
427                }
428
429                if (sName == null) {
430                    return ("");
431                }
432
433                if (inputSelected) {
434                    jmri.Sensor s = null;
435                    s = jmri.InstanceManager.sensorManagerInstance().getBySystemName(sName);
436                    if (s != null) {
437                        return s.getComment();
438                    }
439                } else {
440                    jmri.Turnout t = null;
441                    t = jmri.InstanceManager.turnoutManagerInstance().getBySystemName(sName);
442                    if (t != null) {
443                        return t.getComment();
444                    }
445                }
446
447            }
448
449            return "";  // fall through
450        }
451
452        @Override
453        public void setValueAt(Object type, int r, int c) {
454            // nothing is stored here
455        }
456
457        public static final int BIT_COLUMN = 0;
458        public static final int ADDRESS_COLUMN = 1;
459        public static final int SYSNAME_COLUMN = 2;
460        public static final int USERNAME_COLUMN = 3;
461        public static final int COMMENT_COLUMN = 4;
462        public static final int MAX_COLS = COMMENT_COLUMN + 1;
463
464        /**
465         * Method to print or print preview the assignment table. Printed in
466         * proportionately sized columns across the page with headings and
467         * vertical lines between each column. Data is word wrapped within a
468         * column. Can only handle 4 columns of data as strings. Adapted from
469         * routines in BeanTableDataModel.java by Bob Jacobsen and Dennis Miller
470         * @param w hard copy writer instance.
471         * @param colWidth column width array.
472         */
473        public void printTable(CompatibleHardcopyWriter w, int colWidth[]) {
474            // determine the column sizes - proportionately sized, with space between for lines
475            int[] columnSize = new int[MAX_COLS];
476            int charPerLine = w.getCharactersPerLine();
477            int tableLineWidth = 0;  // table line width in characters
478            int totalColWidth = 0;
479            for (int j = 0; j < MAX_COLS; j++) {
480                totalColWidth += colWidth[j];
481            }
482            float ratio = ((float) charPerLine) / ((float) totalColWidth);
483            for (int j = 0; j < MAX_COLS; j++) {
484                columnSize[j] = ((int) (colWidth[j] * ratio)) - 1;
485                tableLineWidth += (columnSize[j] + 1);
486            }
487
488            // Draw horizontal dividing line
489            w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(),
490                    tableLineWidth);
491
492            // print the column header labels
493            String[] columnStrings = new String[MAX_COLS];
494            // Put each column header in the array
495            for (int i = 0; i < MAX_COLS; i++) {
496                columnStrings[i] = this.getColumnName(i);
497            }
498            //w.setFontStyle(Font.BOLD);
499            printColumns(w, columnStrings, columnSize);
500            w.setFontStyle(Font.PLAIN);
501            // draw horizontal line
502            w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(),
503                    tableLineWidth);
504
505            // now print each row of data
506            String[] spaces = new String[MAX_COLS];
507            // create base strings the width of each of the columns
508            for (int k = 0; k < MAX_COLS; k++) {
509                spaces[k] = "";
510                for (int i = 0; i < columnSize[k]; i++) {
511                    spaces[k] = spaces[k] + " ";
512                }
513            }
514            for (int i = 0; i < this.getRowCount(); i++) {
515                for (int j = 0; j < MAX_COLS; j++) {
516                    //check for special, null contents
517                    if (this.getValueAt(i, j) == null) {
518                        columnStrings[j] = spaces[j];
519                    } else {
520                        columnStrings[j] = (String) this.getValueAt(i, j);
521                    }
522                }
523                printColumns(w, columnStrings, columnSize);
524                // draw horizontal line
525                w.write(w.getCurrentLineNumber(), 0, w.getCurrentLineNumber(),
526                        tableLineWidth);
527            }
528            w.close();
529        }
530
531        protected void printColumns(CompatibleHardcopyWriter w, String columnStrings[], int columnSize[]) {
532            String columnString = "";
533            StringBuilder lineString = new StringBuilder("");
534            String[] spaces = new String[MAX_COLS];
535            // create base strings the width of each of the columns
536            for (int k = 0; k < MAX_COLS; k++) {
537                spaces[k] = "";
538                for (int i = 0; i < columnSize[k]; i++) {
539                    spaces[k] = spaces[k] + " ";
540                }
541            }
542            // loop through each column
543            boolean complete = false;
544            while (!complete) {
545                complete = true;
546                for (int i = 0; i < MAX_COLS; i++) {
547                    // if the column string is too wide cut it at word boundary (valid delimiters are space, - and _)
548                    // use the initial part of the text,pad it with spaces and place the remainder back in the array
549                    // for further processing on next line
550                    // if column string isn't too wide, pad it to column width with spaces if needed
551                    if (columnStrings[i].length() > columnSize[i]) {
552                        // this column string will not fit on one line
553                        boolean noWord = true;
554                        for (int k = columnSize[i]; k >= 1; k--) {
555                            if (columnStrings[i].substring(k - 1, k).equals(" ")
556                                    || columnStrings[i].substring(k - 1, k).equals("-")
557                                    || columnStrings[i].substring(k - 1, k).equals("_")) {
558                                columnString = columnStrings[i].substring(0, k)
559                                        + spaces[i].substring(columnStrings[i].substring(0, k).length());
560                                columnStrings[i] = columnStrings[i].substring(k);
561                                noWord = false;
562                                complete = false;
563                                break;
564                            }
565                        }
566                        if (noWord) {
567                            columnString = columnStrings[i].substring(0, columnSize[i]);
568                            columnStrings[i] = columnStrings[i].substring(columnSize[i]);
569                            complete = false;
570                        }
571                    } else {
572                        // this column string will fit on one line
573                        columnString = columnStrings[i] + spaces[i].substring(columnStrings[i].length());
574                        columnStrings[i] = "";
575                    }
576                    lineString.append(columnString).append(" ");
577                }
578                try {
579                    w.write(lineString.toString());
580                    //write vertical dividing lines
581                    int iLine = w.getCurrentLineNumber();
582                    for (int i = 0, k = 0; i < w.getCharactersPerLine(); k++) {
583                        w.write(iLine, i, iLine + 1, i);
584                        if (k < MAX_COLS) {
585                            i = i + columnSize[k] + 1;
586                        } else {
587                            i = w.getCharactersPerLine();
588                        }
589                    }
590                    w.write("\n");
591                    lineString = new StringBuilder("");
592                } catch (IOException e) {
593                    log.warn("error during printing", e);
594                }
595            }
596        }
597    }
598    private String[] assignmentTableColumnNames = {Bundle.getMessage("HeadingBit"),
599        Bundle.getMessage("HeadingAddress"),
600        Bundle.getMessage("HeadingSystemName"),
601        Bundle.getMessage("HeadingUserName"),
602        Bundle.getMessage("HeadingComment")};
603
604    private static final Logger log = LoggerFactory.getLogger(NodeIOListFrame.class);
605
606}
607