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}