001package jmri.jmrix.cmri.serial.cmrinetmanager;
002
003import java.awt.*;
004import java.awt.event.ActionEvent;
005
006import javax.swing.border.Border;
007import javax.swing.*;
008import javax.swing.table.*;
009import java.awt.event.ActionListener;
010import java.awt.event.MouseAdapter;
011import java.awt.event.MouseEvent;
012import java.io.File;
013import java.io.FileOutputStream;
014import java.io.PrintStream;
015import java.text.DateFormat;
016import java.text.SimpleDateFormat;
017import javax.swing.border.TitledBorder;
018import jmri.jmrix.cmri.serial.cmrinetmetrics.CMRInetMetricsData;
019import jmri.jmrix.cmri.CMRISystemConnectionMemo;
020
021/**
022 * Frame for CMRInet Network Metrics.
023 *
024 * @author Chuck Catania Copyright (C) 2016, 2017, 2018
025 */
026public class CMRInetMetricsFrame extends jmri.util.JmriJFrame {
027
028    // node table pane items
029    protected JPanel networkMetricsPanel = null;
030    protected Border networkMetricsBorder = BorderFactory.createEtchedBorder();
031    protected Border networkMetricsBorderTitled;
032    protected JPanel networkMetricsDataPanel = null;
033    protected Border networkMetricsDataBorder = BorderFactory.createEtchedBorder();
034    protected Border networkMetricsDataBorderTitled
035            = BorderFactory.createTitledBorder(networkMetricsDataBorder, "Data Metrics", TitledBorder.LEFT, TitledBorder.ABOVE_TOP);
036
037    protected JTable netMetricsTable = null;
038    protected TableModel netMetricsTableModel = null;
039
040    protected JTable netMetricsDataTable = null;
041    protected TableModel netMetricsDataTableModel = null;
042
043    // button pane items
044    JButton doneButton = new JButton(Bundle.getMessage("DoneButtonText"));
045    JButton saveMetricsButton = new JButton(Bundle.getMessage("SaveMetricsButtonText"));
046    JButton resetAllMetricsButton = new JButton(Bundle.getMessage("ResetAllMetricsButtonText"));
047
048    final JFileChooser metricsSaveChooser = new jmri.util.swing.JmriJFileChooser();
049
050    CMRInetMetricsFrame curFrame;
051    private CMRISystemConnectionMemo _memo = null;
052
053    private CMRInetMetricsData metricsData;
054
055    public CMRInetMetricsFrame(CMRISystemConnectionMemo memo) {
056        super();
057        _memo = memo;
058        curFrame = this;
059    }
060
061    @Override
062    public void initComponents() {
063        // For the class
064        setTitle(Bundle.getMessage("MetricsWindowTitle") + Bundle.getMessage("WindowConnectionMemo") + _memo.getUserName()); // NOI18N
065        setLayout(new FlowLayout(FlowLayout.LEFT));
066        setPreferredSize(new Dimension(860, 410)); // 415 375
067        networkMetricsBorderTitled = BorderFactory.createTitledBorder(networkMetricsBorder, "Error Metrics", TitledBorder.LEFT, TitledBorder.ABOVE_TOP);
068
069        // Set up the CMRInet ERROR metrics table
070        //---------------------------------------       
071        networkMetricsPanel = new JPanel();
072        networkMetricsPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
073
074        networkMetricsPanel.setLayout(new BoxLayout(networkMetricsPanel, BoxLayout.PAGE_AXIS));
075        networkMetricsPanel.setBorder(networkMetricsBorder);
076
077        netMetricsTableModel = new NetMetricsTableModel();
078        netMetricsTable = new JTable(netMetricsTableModel);
079
080        netMetricsTable.setPreferredScrollableViewportSize(new Dimension(400, 150));  //400 150                  
081        netMetricsTable.setFillsViewportHeight(true);
082
083        netMetricsTable.setShowGrid(false);
084        netMetricsTable.setGridColor(Color.BLACK);
085
086        netMetricsTable.setBackground(Color.WHITE);
087        netMetricsTable.setRowSelectionAllowed(false);
088        netMetricsTable.setFont(new Font("Helvetica", Font.BOLD, 13));
089        netMetricsTable.setRowHeight(30);
090        netMetricsTable.getTableHeader().setReorderingAllowed(false);
091        netMetricsTable.addMouseListener(new ErrMetricButtonMouseListener(netMetricsTable));
092
093        JScrollPane netMetricsTableScrollPane = new JScrollPane(netMetricsTable);
094        networkMetricsPanel.add(netMetricsTableScrollPane, BorderLayout.LINE_START);
095
096        TableColumnModel netMetricsTableModel = netMetricsTable.getColumnModel();
097
098        DefaultTableCellRenderer dtcen = new DefaultTableCellRenderer();
099        dtcen.setHorizontalAlignment(SwingConstants.CENTER);
100        DefaultTableCellRenderer dtlft = new DefaultTableCellRenderer();
101        dtlft.setHorizontalAlignment(SwingConstants.LEFT);
102        DefaultTableCellRenderer dtrgt = new DefaultTableCellRenderer();
103        dtrgt.setHorizontalAlignment(SwingConstants.RIGHT);
104        TableCellRenderer rendererFromHeader = netMetricsTable.getTableHeader().getDefaultRenderer();
105
106        JLabel headerLabel = (JLabel) rendererFromHeader;
107        headerLabel.setHorizontalAlignment(JLabel.CENTER);
108
109        TableColumn errorNameColumn = netMetricsTableModel.getColumn(NetMetricsTableModel.ERRORNAME_COLUMN);
110        errorNameColumn.setMinWidth(200);
111        errorNameColumn.setMaxWidth(200);
112        errorNameColumn.setCellRenderer(dtlft);
113        errorNameColumn.setResizable(false);
114
115        TableColumn errorCountColumn = netMetricsTableModel.getColumn(NetMetricsTableModel.ERRORCOUNT_COLUMN);
116        errorCountColumn.setMinWidth(100);
117        errorCountColumn.setMaxWidth(100);
118        errorCountColumn.setCellRenderer(dtrgt);
119        errorCountColumn.setResizable(false);
120
121        TableColumn blankColumn = netMetricsTableModel.getColumn(NetMetricsTableModel.BLANK_COLUMN);
122        blankColumn.setMinWidth(20);
123        blankColumn.setMaxWidth(20);
124        blankColumn.setCellRenderer(dtrgt);
125        blankColumn.setResizable(false);
126
127        TableCellRenderer buttonRenderer = new ErrMetricButtonRenderer();
128        TableColumn resetCountColumn = netMetricsTableModel.getColumn(NetMetricsTableModel.ERRORRESET_COLUMN);
129        resetCountColumn.setMinWidth(80);
130        resetCountColumn.setMaxWidth(80);
131        resetCountColumn.setCellRenderer(buttonRenderer);
132        resetCountColumn.setResizable(false);
133
134        networkMetricsPanel.setBorder(networkMetricsBorderTitled);
135        networkMetricsPanel.setPreferredSize(new Dimension(415, 300));  //425 300
136        networkMetricsPanel.setVisible(true);
137
138        add(networkMetricsPanel);
139
140        //--------------------------------------
141        // Set up the CMRInet metrics DATA table
142        //--------------------------------------       
143        networkMetricsDataPanel = new JPanel();
144        networkMetricsDataPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
145
146        networkMetricsDataPanel.setLayout(new BoxLayout(networkMetricsDataPanel, BoxLayout.LINE_AXIS));
147        networkMetricsDataPanel.setBorder(networkMetricsDataBorder);
148
149        netMetricsDataTableModel = new NetMetricsDataTableModel();
150        netMetricsDataTable = new JTable(netMetricsDataTableModel);
151
152        netMetricsDataTable.setPreferredScrollableViewportSize(new Dimension(400, 150));  //400 150                  
153        netMetricsDataTable.setFillsViewportHeight(true);
154
155        netMetricsDataTable.setShowGrid(false);
156        netMetricsDataTable.setGridColor(Color.BLACK);
157
158        netMetricsDataTable.setBackground(Color.WHITE);
159        netMetricsDataTable.setRowSelectionAllowed(false);
160        netMetricsDataTable.setFont(new Font("Helvetica", Font.BOLD, 13));
161        netMetricsDataTable.setRowHeight(30);
162        netMetricsDataTable.getTableHeader().setReorderingAllowed(false);
163        netMetricsDataTable.addMouseListener(new DataButtonMouseListener(netMetricsDataTable));
164
165        JScrollPane netMetricsDataTableScrollPane = new JScrollPane(netMetricsDataTable);
166        networkMetricsDataPanel.add(netMetricsDataTableScrollPane, BorderLayout.LINE_START);
167
168        TableColumnModel netMetricsDataTableModel = netMetricsDataTable.getColumnModel();
169        TableCellRenderer dataRendererFromHeader = netMetricsDataTable.getTableHeader().getDefaultRenderer();
170
171        JLabel dataHeaderLabel = (JLabel) dataRendererFromHeader;
172        dataHeaderLabel.setHorizontalAlignment(JLabel.CENTER);
173
174        TableColumn dataNameColumn = netMetricsDataTableModel.getColumn(NetMetricsDataTableModel.DATANAME_COLUMN);
175        dataNameColumn.setMinWidth(200);
176        dataNameColumn.setMaxWidth(200);
177        dataNameColumn.setCellRenderer(dtlft);
178        dataNameColumn.setResizable(false);
179
180        TableColumn dataCountColumn = netMetricsDataTableModel.getColumn(NetMetricsDataTableModel.DATACOUNT_COLUMN);
181        dataCountColumn.setMinWidth(100);
182        dataCountColumn.setMaxWidth(100);
183        dataCountColumn.setCellRenderer(dtrgt);
184        dataCountColumn.setResizable(false);
185
186        TableColumn dataBlankColumn = netMetricsDataTableModel.getColumn(NetMetricsDataTableModel.DATABLANK_COLUMN);
187        dataBlankColumn.setMinWidth(20);
188        dataBlankColumn.setMaxWidth(20);
189        dataBlankColumn.setCellRenderer(dtrgt);
190        dataBlankColumn.setResizable(false);
191
192        TableCellRenderer dataButtonRenderer = new DataButtonRenderer();
193        TableColumn dataResetCountColumn = netMetricsDataTableModel.getColumn(NetMetricsDataTableModel.DATARESET_COLUMN);
194        dataResetCountColumn.setMinWidth(80);
195        dataResetCountColumn.setMaxWidth(80);
196        dataResetCountColumn.setCellRenderer(dataButtonRenderer);
197        dataResetCountColumn.setResizable(false);
198
199        networkMetricsDataPanel.setBorder(networkMetricsDataBorderTitled);
200        networkMetricsDataPanel.setPreferredSize(new Dimension(415, 300));  //425 300
201        networkMetricsDataPanel.setVisible(true);
202
203        add(networkMetricsDataPanel);
204
205        // Main button panel
206        //------------------ 
207        JPanel mainButtons = new JPanel();
208        mainButtons.setLayout(new FlowLayout(FlowLayout.CENTER));
209        mainButtons.setPreferredSize(new Dimension(845, 50));
210
211        saveMetricsButton.setVisible(true);
212        saveMetricsButton.setEnabled(true);
213        saveMetricsButton.setToolTipText(Bundle.getMessage("SaveMetricsButtonText"));
214        saveMetricsButton.addActionListener(new java.awt.event.ActionListener() {
215            @Override
216            public void actionPerformed(java.awt.event.ActionEvent e) {
217                saveMetricsButtonActionPerformed(e);
218            }
219        });
220        mainButtons.add(saveMetricsButton);
221
222        resetAllMetricsButton.setVisible(true);
223        resetAllMetricsButton.setToolTipText(Bundle.getMessage("ResetAllMetricsButtonText"));
224        resetAllMetricsButton.addActionListener(new java.awt.event.ActionListener() {
225            @Override
226            public void actionPerformed(java.awt.event.ActionEvent e) {
227                resetAllMetricsButtonActionPerformed(e);
228            }
229        });
230        mainButtons.add(resetAllMetricsButton);
231
232        doneButton.setVisible(true);
233        doneButton.setToolTipText(Bundle.getMessage("DoneButtonTip"));
234        doneButton.addActionListener(new java.awt.event.ActionListener() {
235            @Override
236            public void actionPerformed(java.awt.event.ActionEvent e) {
237                doneButtonActionPerformed();
238            }
239        });
240        mainButtons.add(doneButton);
241        mainButtons.setVisible(true);
242        add(mainButtons);
243
244        addHelpMenu("package.jmri.jmrix.cmri.serial.cmrinetmanager.CMRInetMetricsFrame", true);
245
246        // pack for display
247        //-----------------
248        pack();
249
250        // Start metrics data collector for this connection
251        //-------------------------------------------------
252        metricsData = _memo.getTrafficController().getMetricsData();
253    }
254
255    /**
256     * ---------------------------- Network statistics window
257     * ----------------------------
258     */
259    public void doneButtonActionPerformed() {
260        setVisible(false);
261        dispose();
262    }
263
264    volatile PrintStream logStream = null;
265
266    /**
267     * Save Metrics button handler.
268     * <p>
269     * Metric data is saved to a text file named
270     * CMRInetMetrics_YYYYMMDD_HHMMSS 
271     * The file is text lines, one for each metric displayed and the count.
272     * 
273     * @param e unused.
274     */
275    public void saveMetricsButtonActionPerformed(ActionEvent e) {
276        String fileName = "CMRInetMetrics_";
277        DateFormat df = new SimpleDateFormat("yyyyMMdd_HHmmss");
278        long curTicks = System.currentTimeMillis();
279
280        // Create a unique name and open the save file dialog
281        //---------------------------------------------------
282        fileName = "CMRInetMetrics_" + df.format(curTicks) + ".txt";
283        metricsSaveChooser.setSelectedFile(new File(fileName));
284
285        int retVal = metricsSaveChooser.showSaveDialog(null);
286        if (retVal == JFileChooser.APPROVE_OPTION) {
287            try {
288                // Open the file and write the metric data
289                //----------------------------------------
290                logStream = new PrintStream(new FileOutputStream(metricsSaveChooser.getSelectedFile()));
291                logStream.println("-----  CMRInet Error Metrics  -----");
292                for (int i = 0; i != CMRInetMetricsData.CMRInetMetricErrLAST; i++) {
293                    logStream.println(String.format("%-30s %d",
294                            CMRInetMetricsData.CMRInetMetricErrName[i],
295                            metricsData.getMetricErrValue(i)));
296                }
297                logStream.print("\n" + "\n");
298                logStream.println("-----  CMRInet Data Metrics  -----");
299                for (int i = 0; i != CMRInetMetricsData.CMRInetMetricDataLAST; i++) {
300                    logStream.println(String.format("%-30s %d",
301                            CMRInetMetricsData.CMRInetMetricDataName[i],
302                            metricsData.getMetricDataValue(i)));
303                }
304
305                // Close the metrics log file
306                //---------------------------
307                synchronized (logStream) {
308                    logStream.flush();
309                    logStream.close();
310                }
311
312            } catch (RuntimeException | java.io.IOException ex) {
313                log.error("exception ", ex);
314            }
315        }
316    }
317
318    /**
319     * Reset All Metrics button handler.
320     * @param e unused.
321     */
322    public void resetAllMetricsButtonActionPerformed(ActionEvent e) {
323        metricsData.clearAllDataMetrics();
324        metricsData.clearAllErrMetrics();
325    }
326
327    /**
328     * Set up table for displaying the Error metrics
329     */
330    public class NetMetricsTableModel extends AbstractTableModel {
331
332        @Override
333        public String getColumnName(int c) {
334            return CMRInetMetricsErrColumnsNames[c];
335        }
336
337        public Class<?> getColumnClass(int r, int c) {
338            switch (c) {
339                case ERRORNAME_COLUMN:
340                    return String.class;
341                case ERRORCOUNT_COLUMN:
342                    return Integer.class;
343                case ERRORRESET_COLUMN:
344                    return Object.class;
345                default:
346                    return Object.class;
347            }
348        }
349
350        @Override
351        public boolean isCellEditable(int r, int c) {
352            return false;
353        }
354
355        @Override
356        public int getColumnCount() {
357            return NUMCOLUMNS;
358        }
359
360        @Override
361        public int getRowCount() {
362            return CMRInetMetricsData.CMRInetMetricErrName.length;
363        }
364
365        @Override
366        public Object getValueAt(final int r, int c) {
367            switch (c) {
368                case ERRORNAME_COLUMN:
369                    return CMRInetMetricsData.CMRInetMetricErrName[r];
370                case ERRORCOUNT_COLUMN:
371                    return metricsData.getMetricErrorCount(r);
372                case ERRORRESET_COLUMN:
373                    final JButton button = new JButton(CMRInetMetricsErrColumnsNames[c]);
374                    button.addActionListener(new ActionListener() {
375                        @Override
376                        public void actionPerformed(ActionEvent arg0) {
377                            metricsData.zeroMetricErrValue(r);
378                        }
379                    });
380
381                    fireTableDataChanged();
382                    return button;
383
384                default:
385                    return " ";
386            }
387
388        }
389
390        public void setValueAt(int value, int r, int c) {
391            switch (c) // leave this as a switch in case other columns get added
392            {
393                case ERRORCOUNT_COLUMN:
394                    metricsData.setMetricErrorValue(r, value);
395                    break;
396                default:
397            }
398            fireTableDataChanged();
399        }
400
401        public static final int ERRORNAME_COLUMN = 0;
402        public static final int ERRORCOUNT_COLUMN = 1;
403        public static final int BLANK_COLUMN = 2;
404        public static final int ERRORRESET_COLUMN = 3;
405        public static final int NUMCOLUMNS = ERRORRESET_COLUMN + 1;
406
407    }
408
409    private static class ErrMetricButtonRenderer implements TableCellRenderer {
410
411        @Override
412        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
413            JButton button = (JButton) value;
414            if (isSelected) {
415                button.setForeground(table.getSelectionForeground());
416                button.setBackground(table.getSelectionBackground());
417            } else {
418                button.setForeground(table.getForeground());
419                button.setBackground(UIManager.getColor("Button.background"));
420            }
421
422            return button;
423        }
424    }
425
426    private static class ErrMetricButtonMouseListener extends MouseAdapter {
427
428        private final JTable table;
429
430        public ErrMetricButtonMouseListener(JTable table) {
431            this.table = table;
432        }
433
434        @Override
435        public void mouseClicked(MouseEvent e) {
436            int column = table.getColumnModel().getColumnIndexAtX(e.getX());
437            int row = e.getY() / table.getRowHeight();
438
439            if (row < table.getRowCount() && row >= 0 && column < table.getColumnCount() && column >= 0) {
440                Object value = table.getValueAt(row, column);
441                if (value instanceof JButton) {
442                    ((JButton) value).doClick();
443                }
444            }
445        }
446    }
447
448    /**
449     * Set up table for displaying the Error metrics
450     */
451    public class NetMetricsDataTableModel extends AbstractTableModel {
452
453        @Override
454        public String getColumnName(int c) {
455            return CMRInetMetricsDataColumnsNames[c];
456        }
457
458        public Class<?> getColumnClass(int r, int c) {
459            switch (c) {
460                case DATANAME_COLUMN:
461                    return String.class;
462                case DATACOUNT_COLUMN:
463                    return Integer.class;
464                case DATARESET_COLUMN:
465                    return Object.class;
466                default:
467                    return Object.class;
468            }
469        }
470
471        @Override
472        public boolean isCellEditable(int r, int c) {
473            return false;
474        }
475
476        @Override
477        public int getColumnCount() {
478            return DATANUMCOLUMNS;
479        }
480
481        @Override
482        public int getRowCount() {
483            return CMRInetMetricsData.CMRInetMetricDataName.length;
484        }
485
486        @Override
487        public Object getValueAt(final int r, int c) {
488            switch (c) {
489                case DATANAME_COLUMN:
490                    return CMRInetMetricsData.CMRInetMetricDataName[r];
491                case DATACOUNT_COLUMN:
492                    return metricsData.CMRInetMetricDataCount[r];
493                case DATARESET_COLUMN:
494                    final JButton button = new JButton(CMRInetMetricsDataColumnsNames[c]);
495                    button.addActionListener(new ActionListener() {
496                        @Override
497                        public void actionPerformed(ActionEvent arg0) {
498                            metricsData.zeroMetricDataValue(r);
499                        }
500                    });
501
502                    fireTableDataChanged();
503                    return button;
504
505                default:
506                    return " ";
507            }
508
509        }
510
511        public void setValueAt(int value, int r, int c) {
512            switch (c) { // leave this as a switch in case other columns get added
513                case DATACOUNT_COLUMN:
514                    metricsData.setMetricDataValue(r, value);
515                    break;
516                default:
517            }
518            fireTableDataChanged();
519        }
520
521        public static final int DATANAME_COLUMN = 0;
522        public static final int DATACOUNT_COLUMN = 1;
523        public static final int DATABLANK_COLUMN = 2;
524        public static final int DATARESET_COLUMN = 3;
525        public static final int DATANUMCOLUMNS = DATARESET_COLUMN + 1;
526
527    }
528
529    private static class DataButtonRenderer implements TableCellRenderer {
530
531        @Override
532        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
533            JButton button = (JButton) value;
534            if (isSelected) {
535                button.setForeground(table.getSelectionForeground());
536                button.setBackground(table.getSelectionBackground());
537            } else {
538                button.setForeground(table.getForeground());
539                button.setBackground(UIManager.getColor("Button.background"));
540            }
541
542            return button;
543        }
544    }
545
546    private static class DataButtonMouseListener extends MouseAdapter {
547
548        private final JTable table;
549
550        public DataButtonMouseListener(JTable table) {
551            this.table = table;
552        }
553
554        @Override
555        public void mouseClicked(MouseEvent e) {
556            int column = table.getColumnModel().getColumnIndexAtX(e.getX());
557            int row = e.getY() / table.getRowHeight();
558
559            if (row < table.getRowCount() && row >= 0 && column < table.getColumnCount() && column >= 0) {
560                Object value = table.getValueAt(row, column);
561                if (value instanceof JButton) {
562                    ((JButton) value).doClick();
563                }
564            }
565        }
566    }
567
568    private String[] CMRInetMetricsErrColumnsNames = {"Error", "Count", " ", "Reset"};
569    private String[] CMRInetMetricsDataColumnsNames = {"Metric", "Count", " ", "Reset"};
570
571    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CMRInetMetricsFrame.class);
572
573}