001package jmri.jmrix.ncemonitor; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004 005import java.awt.Dimension; 006import java.awt.FlowLayout; 007import java.io.DataInputStream; 008import java.io.IOException; 009import java.io.OutputStream; 010import java.util.Vector; 011 012import javax.swing.BoxLayout; 013import javax.swing.ButtonGroup; 014import javax.swing.JCheckBox; 015import javax.swing.JComboBox; 016import javax.swing.JLabel; 017import javax.swing.JPanel; 018import javax.swing.JRadioButton; 019import javax.swing.JScrollPane; 020import javax.swing.JSeparator; 021 022import jmri.jmrix.AbstractSerialPortController; 023import jmri.jmrix.AbstractSerialPortController.SerialPort; 024import jmri.jmrix.nce.NceSystemConnectionMemo; 025import jmri.jmrix.nce.swing.NcePanelInterface; 026 027/** 028 * Simple GUI for access to an NCE monitor card 029 * <p> 030 * When opened, the user must first select a serial port and click "Start". The 031 * rest of the GUI then appears. 032 * 033 * @author Ken Cameron Copyright (C) 2010 derived from - 034 * @author Bob Jacobsen Copyright (C) 2001, 2002, 2023 035 * @author Ken Cameron Copyright (C) 2023 036 */ 037@SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC", justification = "serialStream is access from separate thread, and this class isn't used much") 038public class NcePacketMonitorPanel extends jmri.jmrix.AbstractMonPane implements NcePanelInterface { 039 040 Vector<String> portNameVector = null; 041 SerialPort activeSerialPort = null; 042 NceSystemConnectionMemo memo = null; 043 044 protected JCheckBox dupFilterCheckBox = new JCheckBox(Bundle.getMessage("DupFilterCheckBoxLabel")); 045 protected JComboBox<String> portBox = new javax.swing.JComboBox<String>(); 046 protected javax.swing.JButton openButton = new javax.swing.JButton(Bundle.getMessage("OpenButtonLabel")); 047 protected javax.swing.JButton closePortButton = new javax.swing.JButton(Bundle.getMessage("CloseButtonLabel")); 048 protected JRadioButton verboseButton = new JRadioButton(Bundle.getMessage("VerboseButtonLabel")); 049 protected JRadioButton origHex0Button = new JRadioButton(Bundle.getMessage("OrigHex0Label")); 050 protected JRadioButton origHex1Button = new JRadioButton(Bundle.getMessage("OrigHex1Label")); 051 protected JRadioButton origHex2Button = new JRadioButton(Bundle.getMessage("OrigHex2Label")); 052 protected JRadioButton origHex3Button = new JRadioButton(Bundle.getMessage("OrigHex3Label")); 053 protected JRadioButton origHex4Button = new JRadioButton(Bundle.getMessage("OrigHex4Label")); 054 protected JRadioButton origHex5Button = new JRadioButton(Bundle.getMessage("OrigHex5Label")); 055 protected JRadioButton newHex0Button = new JRadioButton(Bundle.getMessage("NewHex0Label")); 056 protected JRadioButton newHex1Button = new JRadioButton(Bundle.getMessage("NewHex1Label")); 057 protected JRadioButton accOnButton = new JRadioButton(Bundle.getMessage("AccOnLabel")); 058 protected JRadioButton idleOnButton = new JRadioButton(Bundle.getMessage("IdleOnLabel")); 059 protected JRadioButton locoOnButton = new JRadioButton(Bundle.getMessage("LocoOnLabel")); 060 protected JRadioButton resetOnButton = new JRadioButton(Bundle.getMessage("ResetOnLabel")); 061 protected JRadioButton signalOnButton = new JRadioButton(Bundle.getMessage("SignalOnLabel")); 062 protected JRadioButton accSingleButton = new JRadioButton(Bundle.getMessage("AccSingleLabel")); 063 protected JRadioButton accPairedButton = new JRadioButton(Bundle.getMessage("AccPairedLabel")); 064 065 protected JComboBox<String> modelBox = new JComboBox<>(); 066 protected JLabel modelBoxLabel; 067 private String[] validModelNames = new String[]{Bundle.getMessage("PacketAnalyzer"), Bundle.getMessage("DccMeter/Analyzer")}; 068 private final static int MODELORIG = 0; 069 private final static int MODELNEW = 1; 070 private int[] validModelValues = new int[]{MODELORIG, MODELNEW}; 071 private int[] modelBaudRates = new int[]{38400, 115200}; 072 // For old model, Doc says 7 bits, but 8 seems needed, new calls for 115,200 n 8 1 073 private int[] modelBitValues = new int[] {8, 8}; 074 private int[] modelStopValues = new int[] {SerialPort.ONE_STOP_BIT, SerialPort.ONE_STOP_BIT}; 075 private int[] modelParityValues = new int[] {SerialPort.NO_PARITY, SerialPort.NO_PARITY}; 076 077 public NcePacketMonitorPanel() { 078 super(); 079 } 080 081 /** 082 * {@inheritDoc} 083 */ 084 @Override 085 public void init() { 086 } 087 088 /** 089 * {@inheritDoc} 090 */ 091 @Override 092 public void initContext(Object context) { 093 if (context instanceof NceSystemConnectionMemo) { 094 initComponents((NceSystemConnectionMemo) context); 095 } 096 } 097 098 /** 099 * {@inheritDoc} 100 */ 101 @Override 102 public String getHelpTarget() { 103 return "package.jmri.jmrix.nce.analyzer.NcePacketMonitorFrame"; 104 } 105 106 /** 107 * {@inheritDoc} 108 */ 109 @Override 110 public String getTitle() { 111 StringBuilder x = new StringBuilder(); 112 if (memo != null) { 113 x.append(memo.getUserName()); 114 } else { 115 x.append("NCE_"); 116 } 117 x.append(": "); 118 x.append(Bundle.getMessage("Title")); 119 return x.toString(); 120 } 121 122 /** 123 * {@inheritDoc} 124 */ 125 @Override 126 public void initComponents(NceSystemConnectionMemo m) { 127 this.memo = m; 128 129 // populate the GUI, invoked as part of startup 130 enableDisableWhenOpen(false); 131 // load the port selection part 132 portBox.setToolTipText(Bundle.getMessage("PortBoxToolTip")); 133 portBox.setAlignmentX(JLabel.LEFT_ALIGNMENT); 134 Vector<String> v = getPortNames(); 135 for (int i = 0; i < v.size(); i++) { 136 portBox.addItem(v.elementAt(i)); 137 } 138 // offer model choice 139 modelBox.setToolTipText(Bundle.getMessage("ModelBoxToolTip")); 140 modelBox.setAlignmentX(LEFT_ALIGNMENT); 141 for (int i = 0; i < validModelNames.length; i++) { 142 modelBox.addItem(validModelNames[i]); 143 } 144 openButton.setToolTipText(Bundle.getMessage("OpenButtonToolTip")); 145 openButton.addActionListener(new java.awt.event.ActionListener() { 146 @Override 147 public void actionPerformed(java.awt.event.ActionEvent evt) { 148 try { 149 openPortButtonActionPerformed(evt); 150 } catch (java.lang.UnsatisfiedLinkError ex) { 151 log.error("Error while opening port. Did you select the right one?\nException: ", ex); 152 } 153 } 154 }); 155 closePortButton.setToolTipText(Bundle.getMessage("CloseButtonToolTip")); 156 closePortButton.addActionListener(new java.awt.event.ActionListener() { 157 @Override 158 public void actionPerformed(java.awt.event.ActionEvent evt) { 159 try { 160 closePortButtonActionPerformed(); 161 } catch (java.lang.UnsatisfiedLinkError ex) { 162 log.error("Error while closing port. Did you select the right one?\\nException: ", ex); 163 } 164 } 165 }); 166 { 167 JSeparator js = new JSeparator(); 168 js.setMaximumSize(new Dimension(10000, 10)); 169 add(js); 170 } 171 { 172 JPanel p1 = new JPanel(); 173 p1.setLayout(new FlowLayout()); 174 p1.add(new JLabel(Bundle.getMessage("SerialPortLabel"))); 175 p1.add(portBox); 176 p1.add(new JLabel(Bundle.getMessage("ModelBoxLabel"))); 177 p1.add(modelBox); 178 p1.add(openButton); 179 p1.add(closePortButton); 180 //p1.setMaximumSize(p1.getPreferredSize()); 181 add(p1); 182 } 183 184 // add user part of GUI 185 { 186 JSeparator js = new JSeparator(); 187 js.setMaximumSize(new Dimension(10000, 10)); 188 add(js); 189 } 190 JPanel p2 = new JPanel(); 191 JPanel p2A = new JPanel(); 192 p2A.setLayout(new BoxLayout(p2A, BoxLayout.Y_AXIS)); 193 JPanel p2B = new JPanel(); 194 JPanel p2C = new JPanel(); 195 JPanel p2D = new JPanel(); 196 ButtonGroup gD = new ButtonGroup(); 197 { // begin dup group 198 JPanel p = new JPanel(); 199 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 200 dupFilterCheckBox.setToolTipText(Bundle.getMessage("DupFilterCheckBoxToolTip")); 201 p.add(dupFilterCheckBox); 202 p2.add(p); 203 } // end dup group 204 205 { // begin verbose group 206 JPanel p = new JPanel(); 207 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 208 verboseButton.setToolTipText(Bundle.getMessage("VerboseButtonToolTip")); 209 gD.add(verboseButton); 210 p.add(verboseButton); 211 verboseButton.addActionListener(new java.awt.event.ActionListener() { 212 @Override 213 public void actionPerformed(java.awt.event.ActionEvent evt) { 214 sendBytes(new byte[]{(byte) 'V'}); 215 } 216 }); 217 p2A.add(p); 218 } // end verbose group 219 220 { // begin old hex group 221 JPanel p = new JPanel(); 222 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 223 origHex0Button.setToolTipText(Bundle.getMessage("OrigHex0ButtonToolTip")); 224 gD.add(origHex0Button); 225 p.add(origHex0Button); 226 origHex0Button.addActionListener(new java.awt.event.ActionListener() { 227 @Override 228 public void actionPerformed(java.awt.event.ActionEvent evt) { 229 sendBytes(new byte[]{(byte) 'H', (byte) '0'}); 230 } 231 }); 232 p2B.add(p); 233 origHex1Button.setToolTipText(Bundle.getMessage("OrigHex1ButtonToolTip")); 234 gD.add(origHex1Button); 235 p.add(origHex1Button); 236 origHex1Button.addActionListener(new java.awt.event.ActionListener() { 237 @Override 238 public void actionPerformed(java.awt.event.ActionEvent evt) { 239 sendBytes(new byte[]{(byte) 'H', (byte) '1'}); 240 } 241 }); 242 p2B.add(p); 243 origHex2Button.setToolTipText(Bundle.getMessage("OrigHex2ButtonToolTip")); 244 gD.add(origHex2Button); 245 p.add(origHex2Button); 246 origHex2Button.addActionListener(new java.awt.event.ActionListener() { 247 @Override 248 public void actionPerformed(java.awt.event.ActionEvent evt) { 249 sendBytes(new byte[]{(byte) 'H', (byte) '2'}); 250 } 251 }); 252 p2.add(p); 253 origHex3Button.setToolTipText(Bundle.getMessage("OrigHex3ButtonToolTip")); 254 gD.add(origHex3Button); 255 p.add(origHex3Button); 256 origHex3Button.addActionListener(new java.awt.event.ActionListener() { 257 @Override 258 public void actionPerformed(java.awt.event.ActionEvent evt) { 259 sendBytes(new byte[]{(byte) 'H', (byte) '3'}); 260 } 261 }); 262 p2B.add(p); 263 origHex4Button.setToolTipText(Bundle.getMessage("OrigHex4ButtonToolTip")); 264 gD.add(origHex4Button); 265 p.add(origHex4Button); 266 origHex4Button.addActionListener(new java.awt.event.ActionListener() { 267 @Override 268 public void actionPerformed(java.awt.event.ActionEvent evt) { 269 sendBytes(new byte[]{(byte) 'H', (byte) '4'}); 270 } 271 }); 272 p2.add(p); 273 origHex5Button.setToolTipText(Bundle.getMessage("OrigHex5ButtonToolTip")); 274 gD.add(origHex5Button); 275 p.add(origHex5Button); 276 origHex5Button.addActionListener(new java.awt.event.ActionListener() { 277 @Override 278 public void actionPerformed(java.awt.event.ActionEvent evt) { 279 sendBytes(new byte[]{(byte) 'H', (byte) '5'}); 280 } 281 }); 282 p2B.add(p); 283 } // end old hex group 284 285 { // begin new hex group 286 JPanel p = new JPanel(); 287 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 288 newHex0Button.setToolTipText(Bundle.getMessage("NewHex0ButtonToolTip")); 289 gD.add(newHex0Button); 290 p.add(newHex0Button); 291 newHex0Button.addActionListener(new java.awt.event.ActionListener() { 292 @Override 293 public void actionPerformed(java.awt.event.ActionEvent evt) { 294 sendBytes(new byte[]{(byte) 'H', (byte) '0'}); 295 } 296 }); 297 p2C.add(p); 298 newHex1Button.setToolTipText(Bundle.getMessage("NewHex1ButtonToolTip")); 299 gD.add(newHex1Button); 300 p.add(newHex1Button); 301 newHex1Button.addActionListener(new java.awt.event.ActionListener() { 302 @Override 303 public void actionPerformed(java.awt.event.ActionEvent evt) { 304 sendBytes(new byte[]{(byte) 'H', (byte) '1'}); 305 } 306 }); 307 p2C.add(p); 308 } // end new hex group 309 p2D.setLayout(new BoxLayout(p2D, BoxLayout.X_AXIS)); 310 p2D.add(p2B); 311 p2D.add(p2C); 312 p2A.add(p2D); 313 p2.add(p2A); 314 315 { // start on 316 JPanel p = new JPanel(); 317 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 318 accOnButton.setToolTipText(Bundle.getMessage("AccOnButtonToolTip")); 319 p.add(accOnButton); 320 accOnButton.addActionListener(new java.awt.event.ActionListener() { 321 @Override 322 public void actionPerformed(java.awt.event.ActionEvent evt) { 323 if (accOnButton.isSelected()) { 324 sendBytes(new byte[]{(byte) 'A', (byte) '+'}); 325 } else { 326 sendBytes(new byte[]{(byte) 'A', (byte) '-'}); 327 } 328 } 329 }); 330 idleOnButton.setToolTipText(Bundle.getMessage("IdleOnButtonToolTip")); 331 p.add(idleOnButton); 332 idleOnButton.addActionListener(new java.awt.event.ActionListener() { 333 @Override 334 public void actionPerformed(java.awt.event.ActionEvent evt) { 335 if (idleOnButton.isSelected()) { 336 sendBytes(new byte[]{(byte) 'I', (byte) '+'}); 337 } else { 338 sendBytes(new byte[]{(byte) 'I', (byte) '-'}); 339 } 340 } 341 }); 342 locoOnButton.setToolTipText(Bundle.getMessage("LocoOnButtonToolTip")); 343 p.add(locoOnButton); 344 locoOnButton.addActionListener(new java.awt.event.ActionListener() { 345 @Override 346 public void actionPerformed(java.awt.event.ActionEvent evt) { 347 if (locoOnButton.isSelected()) { 348 sendBytes(new byte[]{(byte) 'L', (byte) '+'}); 349 } else { 350 sendBytes(new byte[]{(byte) 'L', (byte) '-'}); 351 } 352 } 353 }); 354 resetOnButton.setToolTipText(Bundle.getMessage("ResetOnButtonToolTip")); 355 p.add(resetOnButton); 356 resetOnButton.addActionListener(new java.awt.event.ActionListener() { 357 @Override 358 public void actionPerformed(java.awt.event.ActionEvent evt) { 359 if (resetOnButton.isSelected()) { 360 sendBytes(new byte[]{(byte) 'R', (byte) '+'}); 361 } else { 362 sendBytes(new byte[]{(byte) 'R', (byte) '-'}); 363 } 364 } 365 }); 366 signalOnButton.setToolTipText(Bundle.getMessage("SignalOnButtonToolTip")); 367 p.add(signalOnButton); 368 signalOnButton.addActionListener(new java.awt.event.ActionListener() { 369 @Override 370 public void actionPerformed(java.awt.event.ActionEvent evt) { 371 if (signalOnButton.isSelected()) { 372 sendBytes(new byte[]{(byte) 'S', (byte) '+'}); 373 } else { 374 sendBytes(new byte[]{(byte) 'S', (byte) '-'}); 375 } 376 } 377 }); 378 p2.add(p); 379 } // end on 380 381 { // Monitor command acc single/double 382 JPanel p = new JPanel(); 383 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 384 JLabel t = new JLabel(Bundle.getMessage("MonitorCmdLabel")); 385 p.add(t); 386 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 387 ButtonGroup gA = new ButtonGroup(); 388 accSingleButton.setToolTipText(Bundle.getMessage("AccSingleButtonToolTip")); 389 gA.add(accSingleButton); 390 p.add(accSingleButton); 391 accSingleButton.addActionListener(new java.awt.event.ActionListener() { 392 @Override 393 public void actionPerformed(java.awt.event.ActionEvent evt) { 394 sendBytes(new byte[]{(byte) 'A', (byte) 'S'}); 395 } 396 }); 397 accPairedButton.setToolTipText(Bundle.getMessage("AccPairedButtonToolTip")); 398 gA.add(accPairedButton); 399 p.add(accPairedButton); 400 accPairedButton.addActionListener(new java.awt.event.ActionListener() { 401 @Override 402 public void actionPerformed(java.awt.event.ActionEvent evt) { 403 sendBytes(new byte[]{(byte) 'A', (byte) 'P'}); 404 } 405 }); 406 p2.add(p); 407 } // end acc single/double 408 409 p2.setMaximumSize(p2.getPreferredSize()); 410 JScrollPane ps = new JScrollPane(p2); 411 ps.setMaximumSize(ps.getPreferredSize()); 412 ps.setVisible(true); 413 add(ps); 414 } 415 416 /** 417 * Sends stream of bytes to the command station 418 * 419 * @param bytes array of bytes to send 420 */ 421 synchronized void sendBytes(byte[] bytes) { 422 try { 423 // only attempt to send data if output stream is not null (i.e. it 424 // was opened successfully) 425 if (ostream == null) { 426 throw new IOException( 427 "Unable to send data to command station: output stream is null"); 428 } else { 429 for (int i = 0; i < bytes.length; i++) { 430 ostream.write(bytes[i]); 431 wait(3); 432 } 433 final byte endbyte = 13; 434 ostream.write(endbyte); 435 } 436 } catch (IOException e) { 437 log.error("Exception on output: ", e); 438 } catch (InterruptedException e) { 439 Thread.currentThread().interrupt(); // retain if needed later 440 log.error("Interrupted output: ", e); 441 } 442 } 443 444 /** 445 * Enable/Disable options depending on port open/closed status 446 * @param isOpen enables/disables buttons/checkbox when connection is open/closed 447 */ 448 void enableDisableWhenOpen(boolean isOpen) { 449 openButton.setEnabled(!isOpen); 450 closePortButton.setEnabled(isOpen); 451 portBox.setEnabled(!isOpen); 452 modelBox.setEnabled(!isOpen); 453 verboseButton.setEnabled(isOpen); 454 if (!isOpen || (modelBox.getSelectedIndex() == MODELORIG)) { 455 origHex0Button.setEnabled(isOpen); 456 origHex1Button.setEnabled(isOpen); 457 origHex2Button.setEnabled(isOpen); 458 origHex3Button.setEnabled(isOpen); 459 origHex4Button.setEnabled(isOpen); 460 origHex5Button.setEnabled(isOpen); 461 } 462 if (!isOpen || (modelBox.getSelectedIndex() == MODELNEW)) { 463 newHex0Button.setEnabled(isOpen); 464 newHex1Button.setEnabled(isOpen); 465 } 466 accOnButton.setEnabled(isOpen); 467 idleOnButton.setEnabled(isOpen); 468 locoOnButton.setEnabled(isOpen); 469 resetOnButton.setEnabled(isOpen); 470 signalOnButton.setEnabled(isOpen); 471 accSingleButton.setEnabled(isOpen); 472 accPairedButton.setEnabled(isOpen); 473 } 474 475 /** 476 * Open button has been pushed, create the actual display connection 477 * @param e open button event 478 */ 479 void openPortButtonActionPerformed(java.awt.event.ActionEvent e) { 480 //log.info("Open button pushed"); 481 // can't change this anymore 482 String portName = (String) portBox.getSelectedItem(); 483 int modelValue = validModelValues[modelBox.getSelectedIndex()]; 484 int numDataBits = modelBitValues[modelValue]; 485 int numStopBits = modelStopValues[modelValue]; 486 int parity = modelParityValues[modelValue]; 487 int baudrate = modelBaudRates[modelValue]; 488 activeSerialPort = AbstractSerialPortController.activatePort( 489 null, portName, log, numStopBits, AbstractSerialPortController.Parity.getParity(parity)); 490 491 activeSerialPort.setNumDataBits(numDataBits); 492 activeSerialPort.setBaudRate(baudrate); 493 494 // set RTS high, DTR high 495 activeSerialPort.setRTS(); // not connected in some serial ports and adapters 496 activeSerialPort.setDTR(); // pin 1 in DIN8; on main connector, this is DTR 497 498 // get and save stream 499 serialStream = new DataInputStream(activeSerialPort.getInputStream()); 500 ostream = activeSerialPort.getOutputStream(); 501 502 // report status? 503 if (log.isInfoEnabled()) { 504 log.info("Port {} {} opened at {} baud, sees DTR: {} RTS: {} DSR: {} CTS: {} DCD: {}", 505 portName, activeSerialPort.getDescriptivePortName(), 506 activeSerialPort.getBaudRate(), activeSerialPort.getDTR(), 507 activeSerialPort.getRTS(), activeSerialPort.getDSR(), activeSerialPort.getCTS(), 508 activeSerialPort.getDCD()); 509 } 510 511 // start the reader 512 readerThread = new Thread(new Reader()); 513 readerThread.start(); 514 readerThread.setName("NCE Packet Monitor"); 515 // enable buttons 516 enableDisableWhenOpen(true); 517 //log.info("Open button processing complete"); 518 } 519 520 /** 521 * Open button has been pushed, create the actual display connection 522 */ 523 void closePortButtonActionPerformed() { 524 //log.info("Close button pushed"); 525 if (readerThread != null) { 526 stopThread(readerThread); 527 } 528 529 // release port 530 if (activeSerialPort != null) { 531 activeSerialPort.closePort(); 532 log.info("{} port closed", portBox.getSelectedItem()); 533 } 534 serialStream = null; 535 ostream = null; 536 activeSerialPort = null; 537 portNameVector = null; 538 // enable buttons 539 enableDisableWhenOpen(false); 540 } 541 542 Thread readerThread; 543 544 /* 545 * tell the reader thread to close down 546 */ 547 void stopThread(Thread t) { 548 t.interrupt(); 549 } 550 551 @Override 552 public synchronized void dispose() { 553 // stop operations here. This is a deprecated method, but OK for us. 554 closePortButtonActionPerformed(); 555 556 // and clean up parent 557 super.dispose(); 558 } 559 560 public Vector<String> getPortNames() { 561 return jmri.jmrix.AbstractSerialPortController.getActualPortNames(); 562 } 563 564 DataInputStream serialStream = null; 565 OutputStream ostream = null; 566 567 /** 568 * Internal class to handle the separate character-receive thread 569 * 570 */ 571 class Reader implements Runnable { 572 573 /** 574 * Handle incoming characters. This is a permanent loop, looking for 575 * input messages in character form on the stream connected to the 576 * PortController via <code>connectPort</code>. Terminates with the 577 * input stream breaking out of the try block. 578 */ 579 @Override 580 public void run() { 581 // have to limit verbosity! 582 583 while (true) { // loop permanently, stream close will exit via exception 584 try { 585 handleIncomingData(); 586 } catch (java.io.EOFException e) { 587 log.info("{} thread ending, port closed", Thread.currentThread().getName()); 588 return; 589 } catch (java.io.IOException e) { 590 log.warn("{} thread ending: Exception: {}", Thread.currentThread().getName(), e.toString()); 591 return; 592 } 593 } 594 } 595 596 static final int maxMsg = 80; 597 StringBuffer msg; 598 private int duplicates = 0; 599 String msgString; 600 String matchString = ""; 601 602 void handleIncomingData() throws java.io.IOException { 603 // we sit in this until the message is complete, relying on 604 // threading to let other stuff happen 605 606 // Create output message 607 msg = new StringBuffer(maxMsg); 608 // message exists, now fill it 609 int i; 610 for (i = 0; i < maxMsg; i++) { 611 char char1 = (char) serialStream.readByte(); 612 if (char1 == 13) { // 13 is the CR at the end; done this 613 // way to be coding-independent 614 break; 615 } 616 msg.append(char1); 617 } 618 619 // create the String to display (as String has .equals) 620 msgString = msg.toString(); 621 622 // is this a duplicate? 623 if (msgString.equals(matchString) && dupFilterCheckBox.isSelected()) { 624 // yes, keep count 625 duplicates++; 626 } else { 627 // no, message is complete, dispatch it!! 628 if (!msgString.equals(matchString) && dupFilterCheckBox.isSelected() && (duplicates > 0)) { 629 // prepend the duplicate info 630 String dupString = matchString + " [" + duplicates + "]\n"; 631 // return a notification via the queue to ensure end 632 Runnable r = new Runnable() { 633 @Override 634 public void run() { 635 nextLine(dupString, ""); 636 } 637 }; 638 javax.swing.SwingUtilities.invokeLater(r); 639 } 640 duplicates = 0; 641 matchString = msgString; 642 msgString = msgString + "\n"; 643 // return a notification via the queue to ensure end 644 Runnable r = new Runnable() { 645 @Override 646 public void run() { 647 nextLine(msgString, ""); 648 } 649 }; 650 javax.swing.SwingUtilities.invokeLater(r); 651 } 652 } 653 654 } // end class Reader 655 656 /** 657 * Nested class to create one of these using old-style defaults 658 */ 659 static public class Default extends jmri.jmrix.nce.swing.NceNamedPaneAction { 660 661 public Default() { 662 super("Open NCE DCC Packet Analyzer", 663 new jmri.util.swing.sdi.JmriJFrameInterface(), 664 NcePacketMonitorPanel.class.getName(), 665 jmri.InstanceManager.getDefault(NceSystemConnectionMemo.class)); 666 } 667 } 668 669 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NcePacketMonitorPanel.class); 670}