001package jmri.jmrix; 002 003import java.io.*; 004import java.util.Enumeration; 005import java.util.Vector; 006 007import jmri.SystemConnectionMemo; 008 009/** 010 * Provide an abstract base for *PortController classes. 011 * <p> 012 * The intent is to hide, to the extent possible, all the references to the 013 * actual serial library in use within this class. Subclasses then 014 * rely on methods here to maniplate the content of the 015 * protected currentSerialPort variable/ 016 * 017 * @see jmri.jmrix.SerialPortAdapter 018 * 019 * @author Bob Jacobsen Copyright (C) 2001, 2002, 2023 020 */ 021abstract public class AbstractSerialPortController extends AbstractPortController implements SerialPortAdapter { 022 023 protected AbstractSerialPortController(SystemConnectionMemo connectionMemo) { 024 super(connectionMemo); 025 } 026 027 protected SerialPort currentSerialPort = null; 028 029 /** 030 * Standard error handling for purejavacomm port-busy case. 031 * 032 * @param p the exception being handled, if additional information 033 * from it is desired 034 * @param portName name of the port being accessed 035 * @param log where to log a status message 036 * @return Localized message, in case separate presentation to user is 037 * desired 038 */ 039 //@Deprecated(forRemoval=true) // with PureJavaComm 040 @Override 041 public String handlePortBusy(purejavacomm.PortInUseException p, String portName, org.slf4j.Logger log) { 042 log.error("{} port is in use: {}", portName, p.getMessage()); 043 /*JmriJOptionPane.showMessageDialog(null, "Port is in use", 044 "Error", JmriJOptionPane.ERROR_MESSAGE);*/ 045 ConnectionStatus.instance().setConnectionState(this.getSystemPrefix(), portName, ConnectionStatus.CONNECTION_DOWN); 046 return Bundle.getMessage("SerialPortInUse", portName); 047 } 048 049 /** 050 * Specific error handling for purejavacomm port-not-found case. 051 * @param p no such port exception. 052 * @param portName port name. 053 * @param log system log. 054 * @return human readable string with error detail. 055 */ 056 //@Deprecated(forRemoval=true) // with PureJavaComm 057 public String handlePortNotFound(purejavacomm.NoSuchPortException p, String portName, org.slf4j.Logger log) { 058 log.error("Serial port {} not found", portName); 059 ConnectionStatus.instance().setConnectionState(this.getSystemPrefix(), portName, ConnectionStatus.CONNECTION_DOWN); 060 return Bundle.getMessage("SerialPortNotFound", portName); 061 } 062 063 /** 064 * Standard error handling for the general port-not-found case. 065 * @param systemPrefix the system prefix 066 * @param portName port name. 067 * @param log system log, passed so logging comes from bottom level class 068 * @param ex Underlying Exception that caused this failure 069 * @return human readable string with error detail. 070 */ 071 public static String handlePortNotFound(String systemPrefix, String portName, org.slf4j.Logger log, Exception ex) { 072 log.error("Serial port {} not found: {}", portName, ex.getMessage()); 073 if (systemPrefix != null) { 074 ConnectionStatus.instance().setConnectionState(systemPrefix, portName, ConnectionStatus.CONNECTION_DOWN); 075 } 076 return Bundle.getMessage("SerialPortNotFound", portName); 077 } 078 079 /** 080 * {@inheritDoc} 081 */ 082 @Override 083 public void connect() throws java.io.IOException { 084 openPort(mPort, "JMRI app"); 085 } 086 087 /** 088 * Do the formal opening of the port, 089 * set the port for blocking reads without timeout, 090 * set the port to 8 data bits, 1 stop bit, no parity 091 * and purge the port's input stream. 092 * <p> 093 * Does not do the rest of the setup implied in the {@link #openPort} method. 094 * This is usually followed by calls to 095 * {@link #setBaudRate}, {@link #configureLeads} and {@link #setFlowControl}. 096 * 097 * @param portName local system name for the desired port 098 * @param log Logger to use for errors, passed so that errors are logged from low-level class 099 * @return the serial port object for later use 100 */ 101 final protected SerialPort activatePort(String portName, org.slf4j.Logger log) { 102 return activatePort(this.getSystemPrefix(), portName, log, 1, Parity.NONE); 103 } 104 105 /** 106 * Do the formal opening of the port, 107 * set the port for blocking reads without timeout, 108 * set the port to 8 data bits, the indicated number of stop bits, no parity, 109 * and purge the port's input stream. 110 * <p> 111 * Does not do the rest of the setup implied in the {@link #openPort} method. 112 * This is usually followed by calls to 113 * {@link #setBaudRate}, {@link #configureLeads} and {@link #setFlowControl}. 114 * 115 * @param portName local system name for the desired port 116 * @param log Logger to use for errors, passed so that errors are logged from low-level class' 117 * @param stop_bits The number of stop bits, either 1 or 2 118 * @return the serial port object for later use 119 */ 120 final protected SerialPort activatePort(String portName, org.slf4j.Logger log, int stop_bits) { 121 return activatePort(this.getSystemPrefix(), portName, log, stop_bits, Parity.NONE); 122 } 123 124 /** 125 * Do the formal opening of the port, 126 * set the port for blocking reads without timeout, 127 * set the port to 8 data bits, the indicated number of stop bits and parity, 128 * and purge the port's input stream. 129 * <p> 130 * Does not do the rest of the setup implied in the {@link #openPort} method. 131 * This is usually followed by calls to 132 * {@link #setBaudRate}, {@link #configureLeads} and {@link #setFlowControl}. 133 * 134 * @param systemPrefix the system prefix 135 * @param portName local system name for the desired port 136 * @param log Logger to use for errors, passed so that errors are logged from low-level class' 137 * @param stop_bits The number of stop bits, either 1 or 2 138 * @param parity one of the defined parity contants 139 * @return the serial port object for later use 140 */ 141 public static SerialPort activatePort(String systemPrefix, String portName, org.slf4j.Logger log, int stop_bits, Parity parity) { 142 com.fazecast.jSerialComm.SerialPort serialPort; 143 144 // convert the 1 or 2 stop_bits argument to the proper jSerialComm code value 145 int stop_bits_code; 146 switch (stop_bits) { 147 case 1: 148 stop_bits_code = com.fazecast.jSerialComm.SerialPort.ONE_STOP_BIT; 149 break; 150 case 2: 151 stop_bits_code = com.fazecast.jSerialComm.SerialPort.TWO_STOP_BITS; 152 break; 153 default: 154 throw new IllegalArgumentException("Incorrect stop_bits argument: "+stop_bits); 155 } 156 157 try { 158 serialPort = com.fazecast.jSerialComm.SerialPort.getCommPort(portName); 159 serialPort.openPort(); 160 serialPort.setComPortTimeouts(com.fazecast.jSerialComm.SerialPort.TIMEOUT_READ_BLOCKING, 0, 0); 161 serialPort.setNumDataBits(8); 162 serialPort.setNumStopBits(stop_bits_code); 163 serialPort.setParity(parity.getValue()); 164 purgeStream(serialPort.getInputStream()); 165 } catch (java.io.IOException | com.fazecast.jSerialComm.SerialPortInvalidPortException ex) { 166 // IOException includes 167 // com.fazecast.jSerialComm.SerialPortIOException 168 handlePortNotFound(systemPrefix, portName, log, ex); 169 return null; 170 } 171 return new SerialPort(serialPort); 172 } 173 174 final protected void setComPortTimeouts(SerialPort serialPort, Blocking blocking, int timeout) { 175 serialPort.serialPort.setComPortTimeouts(blocking.getValue(), timeout, 0); 176 } 177 178 /** 179 * {@inheritDoc} 180 */ 181 @Override 182 public void setPort(String port) { 183 log.debug("Setting port to {}", port); 184 mPort = port; 185 } 186 protected String mPort = null; 187 188 /** 189 * {@inheritDoc} 190 * 191 * Overridden in simulator adapter classes to return ""; 192 */ 193 @Override 194 public String getCurrentPortName() { 195 if (mPort == null) { 196 if (getPortNames() == null) { 197 // this shouldn't happen in normal operation 198 // but in the tests this can happen if the receive thread has been interrupted 199 log.error("Port names returned as null"); 200 return null; 201 } 202 if (getPortNames().size() <= 0) { 203 log.error("No usable ports returned"); 204 return null; 205 } 206 return null; 207 // return (String)getPortNames().elementAt(0); 208 } 209 return mPort; 210 } 211 212 /** 213 * Provide the actual serial port names. 214 * As a public static method, this can be accessed outside the jmri.jmrix 215 * package to get the list of names for e.g. context reports. 216 * 217 * @return the port names in the form they can later be used to open the port 218 */ 219// @SuppressWarnings("UseOfObsoleteCollectionType") // historical interface 220 public static Vector<String> getActualPortNames() { 221 // first, check that the comm package can be opened and ports seen 222 var portNameVector = new Vector<String>(); 223 224 com.fazecast.jSerialComm.SerialPort[] portIDs = com.fazecast.jSerialComm.SerialPort.getCommPorts(); 225 // find the names of suitable ports 226 for (com.fazecast.jSerialComm.SerialPort portID : portIDs) { 227 portNameVector.addElement(portID.getSystemPortName()); 228 } 229 return portNameVector; 230 } 231 232 /** 233 * Set the control leads and flow control for purejavacomm. This handles any necessary 234 * ordering. 235 * 236 * @param serialPort Port to be updated 237 * @param flow flow control mode from (@link purejavacomm.SerialPort} 238 * @param rts set RTS active if true 239 * @param dtr set DTR active if true 240 */ 241 //@Deprecated(forRemoval=true) // Removed with PureJavaComm 242 protected void configureLeadsAndFlowControl(purejavacomm.SerialPort serialPort, int flow, boolean rts, boolean dtr) { 243 // (Jan 2018) PJC seems to mix termios and ioctl access, so it's not clear 244 // what's preserved and what's not. Experimentally, it seems necessary 245 // to write the control leads, set flow control, and then write the control 246 // leads again. 247 serialPort.setRTS(rts); 248 serialPort.setDTR(dtr); 249 250 try { 251 if (flow != purejavacomm.SerialPort.FLOWCONTROL_NONE) { 252 serialPort.setFlowControlMode(flow); 253 } 254 } catch (purejavacomm.UnsupportedCommOperationException e) { 255 log.warn("Could not set flow control, ignoring"); 256 } 257 if (flow!=purejavacomm.SerialPort.FLOWCONTROL_RTSCTS_OUT) serialPort.setRTS(rts); // not connected in some serial ports and adapters 258 serialPort.setDTR(dtr); 259 } 260 261 /** 262 * Set the baud rate on the port 263 * 264 * @param serialPort Port to be updated 265 * @param baud baud rate to be set 266 */ 267 final protected void setBaudRate(SerialPort serialPort, int baud) { 268 serialPort.serialPort.setBaudRate(baud); 269 } 270 271 /** 272 * Set the control leads. 273 * 274 * @param serialPort Port to be updated 275 * @param rts set RTS active if true 276 * @param dtr set DTR active if true 277 */ 278 final protected void configureLeads(SerialPort serialPort, boolean rts, boolean dtr) { 279 if (rts) { 280 serialPort.serialPort.setRTS(); 281 } else { 282 serialPort.serialPort.clearRTS(); 283 } 284 if (dtr) { 285 serialPort.serialPort.setDTR(); 286 } else { 287 serialPort.serialPort.clearDTR(); 288 } 289 290 } 291 292 /** 293 * Configure the port's parity 294 * 295 * @param serialPort Port to be updated 296 * @param parity the desired parity as one of the define static final constants 297 */ 298 final protected void setParity(com.fazecast.jSerialComm.SerialPort serialPort, Parity parity) { 299 serialPort.setParity(parity.getValue()); // constants are defined with values for the specific port class 300 } 301 302 /** 303 * Enumerate the possible flow control choices 304 */ 305 public enum FlowControl { 306 NONE, 307 RTSCTS, 308 XONXOFF 309 } 310 311 /** 312 * Enumerate the possible parity choices 313 */ 314 public enum Parity { 315 NONE(com.fazecast.jSerialComm.SerialPort.NO_PARITY), 316 EVEN(com.fazecast.jSerialComm.SerialPort.EVEN_PARITY), 317 ODD(com.fazecast.jSerialComm.SerialPort.ODD_PARITY); 318 319 private final int value; 320 321 Parity(int value) { 322 this.value = value; 323 } 324 325 public int getValue() { 326 return value; 327 } 328 329 public static Parity getParity(int parity) { 330 for (Parity p : Parity.values()) { 331 if (p.value == parity) { 332 return p; 333 } 334 } 335 throw new IllegalArgumentException("Unknown parity"); 336 } 337 } 338 339 /** 340 * Enumerate the possible timeout choices 341 */ 342 public enum Blocking { 343 NONBLOCKING(com.fazecast.jSerialComm.SerialPort.TIMEOUT_NONBLOCKING), 344 READ_BLOCKING(com.fazecast.jSerialComm.SerialPort.TIMEOUT_READ_BLOCKING), 345 READ_SEMI_BLOCKING(com.fazecast.jSerialComm.SerialPort.TIMEOUT_READ_SEMI_BLOCKING); 346 347 private final int value; 348 349 Blocking(int value) { 350 this.value = value; 351 } 352 353 public int getValue() { 354 return value; 355 } 356 } 357 358 /** 359 * Configure the flow control settings. Keep this in synch with the 360 * FlowControl enum. 361 * 362 * @param serialPort Port to be updated 363 * @param flow set which kind of flow control to use 364 */ 365 final protected void setFlowControl(SerialPort serialPort, FlowControl flow) { 366 lastFlowControl = flow; 367 368 boolean result = true; 369 370 if (null == flow) { 371 log.error("Invalid null FlowControl enum member"); 372 } else switch (flow) { 373 case RTSCTS: 374 result = serialPort.serialPort.setFlowControl(com.fazecast.jSerialComm.SerialPort.FLOW_CONTROL_RTS_ENABLED 375 | com.fazecast.jSerialComm.SerialPort.FLOW_CONTROL_CTS_ENABLED ); 376 break; 377 case XONXOFF: 378 result = serialPort.serialPort.setFlowControl(com.fazecast.jSerialComm.SerialPort.FLOW_CONTROL_XONXOFF_IN_ENABLED 379 | com.fazecast.jSerialComm.SerialPort.FLOW_CONTROL_XONXOFF_OUT_ENABLED); 380 break; 381 case NONE: 382 result = serialPort.serialPort.setFlowControl(com.fazecast.jSerialComm.SerialPort.FLOW_CONTROL_DISABLED); 383 break; 384 default: 385 log.error("Invalid FlowControl enum member: {}", flow); 386 break; 387 } 388 389 if (!result) log.error("Port did not accept flow control setting {}", flow); 390 } 391 392 private FlowControl lastFlowControl = FlowControl.NONE; 393 /** 394 * get the flow control mode back from the actual port. 395 * @param serialPort Port to be examined 396 * @return flow control setting observed in the port 397 */ 398 final protected FlowControl getFlowControl(SerialPort serialPort) { 399 // do a cross-check, just in case there's an issue 400 int nowFlow = serialPort.serialPort.getFlowControlSettings(); 401 402 switch (lastFlowControl) { 403 404 case NONE: 405 if (nowFlow != com.fazecast.jSerialComm.SerialPort.FLOW_CONTROL_DISABLED) 406 log.error("Expected flow {} but found {}", lastFlowControl, nowFlow); 407 break; 408 case RTSCTS: 409 if (nowFlow != (com.fazecast.jSerialComm.SerialPort.FLOW_CONTROL_RTS_ENABLED 410 | com.fazecast.jSerialComm.SerialPort.FLOW_CONTROL_CTS_ENABLED)) 411 log.error("Expected flow {} but found {}", lastFlowControl, nowFlow); 412 break; 413 case XONXOFF: 414 if (nowFlow != (com.fazecast.jSerialComm.SerialPort.FLOW_CONTROL_XONXOFF_IN_ENABLED 415 | com.fazecast.jSerialComm.SerialPort.FLOW_CONTROL_XONXOFF_OUT_ENABLED)) 416 log.error("Expected flow {} but found {}", lastFlowControl, nowFlow); 417 break; 418 default: 419 log.warn("Unexpected FlowControl mode: {}", lastFlowControl); 420 } 421 422 return lastFlowControl; 423 } 424 425 /** 426 * Add a data listener to the specified port 427 * @param serialPort Port to be updated 428 * @param serialPortDataListener the listener to add 429 */ 430 final protected void setDataListener(SerialPort serialPort, SerialPortDataListener serialPortDataListener){ 431 serialPort.serialPort.addDataListener(new com.fazecast.jSerialComm.SerialPortDataListener() { 432 @Override 433 public int getListeningEvents() { 434 return serialPortDataListener.getListeningEvents(); 435 } 436 437 @Override 438 public void serialEvent(com.fazecast.jSerialComm.SerialPortEvent event) { 439 serialPortDataListener.serialEvent(new SerialPortEvent(event)); 440 } 441 }); 442 } 443 444 /** 445 * Cleanly close the specified port 446 * @param serialPort Port to be closed 447 */ 448 final protected void closeSerialPort(SerialPort serialPort){ 449 serialPort.serialPort.closePort(); 450 } 451 452 /** 453 * Set the flow control for purejavacomm, while also setting RTS and DTR to active. 454 * 455 * @param serialPort Port to be updated 456 * @param flow flow control mode from (@link purejavacomm.SerialPort} 457 */ 458 //@Deprecated(forRemoval=true) // with PureJavaComm 459 final protected void configureLeadsAndFlowControl(purejavacomm.SerialPort serialPort, int flow) { 460 configureLeadsAndFlowControl(serialPort, flow, true, true); 461 } 462 463 /** 464 * Report the connection status. 465 * Typically used after the connection is complete 466 * @param log The low-level logger to get this reported against the right class 467 * @param portName low-level name of selected port 468 */ 469 final protected void reportPortStatus(org.slf4j.Logger log, String portName) { 470 if (log.isInfoEnabled()) { 471 log.info("Port {} {} opened at {} baud, sees DTR: {} RTS: {} DSR: {} CTS: {} DCD: {} flow: {}", 472 portName, currentSerialPort.serialPort.getDescriptivePortName(), 473 currentSerialPort.serialPort.getBaudRate(), currentSerialPort.serialPort.getDTR(), 474 currentSerialPort.serialPort.getRTS(), currentSerialPort.serialPort.getDSR(), currentSerialPort.serialPort.getCTS(), 475 currentSerialPort.serialPort.getDCD(), getFlowControl(currentSerialPort)); 476 } 477 if (log.isDebugEnabled()) { 478 String stopBits; 479 switch (currentSerialPort.serialPort.getNumStopBits()) { 480 case com.fazecast.jSerialComm.SerialPort.TWO_STOP_BITS: 481 stopBits = "2"; 482 break; 483 case com.fazecast.jSerialComm.SerialPort.ONE_STOP_BIT: 484 stopBits = "1"; 485 break; 486 default: 487 stopBits = "unknown"; 488 break; 489 } 490 log.debug(" {} data bits, {} stop bits", 491 currentSerialPort.serialPort.getNumDataBits(), stopBits); 492 } 493 494 } 495 496 497 // When PureJavaComm is removed, set this to 'final' to find 498 // identical implementations in the subclasses - but note simulators are now overriding 499 @Override 500 public DataInputStream getInputStream() { 501 if (!opened) { 502 log.error("getInputStream called before open, stream not available"); 503 return null; 504 } 505 return new DataInputStream(currentSerialPort.serialPort.getInputStream()); 506 } 507 508 // When PureJavaComm is removed, set this to 'final' to find 509 // identical implementations in the subclasses - but note simulators are now overriding 510 @Override 511 public DataOutputStream getOutputStream() { 512 if (!opened) { 513 log.error("getOutputStream called before open, stream not available"); 514 } 515 516 return new DataOutputStream(currentSerialPort.serialPort.getOutputStream()); 517 } 518 519 520 /** 521 * {@inheritDoc} 522 */ 523 @Override 524 final public void configureBaudRate(String rate) { 525 mBaudRate = rate; 526 } 527 528 /** 529 * {@inheritDoc} 530 */ 531 @Override 532 final public void configureBaudRateFromNumber(String indexString) { 533 int baudNum; 534 int index = 0; 535 final String[] rates = validBaudRates(); 536 final int[] numbers = validBaudNumbers(); 537 if ((numbers == null) || (numbers.length == 0)) { // simulators return null TODO for SpotBugs make that into an empty array 538 mBaudRate = null; 539 log.debug("no serial port speed values received (OK for simulator)"); 540 return; 541 } 542 if (numbers.length != rates.length) { 543 mBaudRate = null; 544 log.error("arrays wrong length in currentBaudNumber: {}, {}", numbers.length, rates.length); 545 return; 546 } 547 if (indexString.isEmpty()) { 548 mBaudRate = null; // represents "(none)" 549 log.debug("empty baud rate received"); 550 return; 551 } 552 try { 553 // since 4.16 first try to convert loaded value directly to integer 554 baudNum = Integer.parseInt(indexString); // new storage format, will throw ex on old format 555 log.debug("new profile format port speed value"); 556 } catch (NumberFormatException ex) { 557 // old pre 4.15.8 format is i18n string including thousand separator and whatever suffix like "18,600 bps (J1)" 558 log.warn("old profile format port speed value converted"); 559 // filter only numerical characters from indexString 560 StringBuilder baudNumber = new StringBuilder(); 561 boolean digitSeen = false; 562 for (int n = 0; n < indexString.length(); n++) { 563 if (Character.isDigit(indexString.charAt(n))) { 564 digitSeen = true; 565 baudNumber.append(indexString.charAt(n)); 566 } else if ((indexString.charAt(n) == ' ') && digitSeen) { 567 break; // break on first space char encountered after at least 1 digit was found 568 } 569 } 570 if (baudNumber.toString().equals("")) { // no number found in indexString e.g. "(automatic)" 571 baudNum = 0; 572 } else { 573 try { 574 baudNum = Integer.parseInt(baudNumber.toString()); 575 } catch (NumberFormatException e2) { 576 mBaudRate = null; // represents "(none)" 577 log.error("error in filtering old profile format port speed value"); 578 return; 579 } 580 log.debug("old format baud number: {}", indexString); 581 } 582 } 583 // fetch baud rate description from validBaudRates[] array copy and set 584 for (int i = 0; i < numbers.length; i++) { 585 if (numbers[i] == baudNum) { 586 index = i; 587 log.debug("found new format baud value at index {}", i); 588 break; 589 } 590 } 591 mBaudRate = validBaudRates()[index]; 592 log.debug("mBaudRate set to: {}", mBaudRate); 593 } 594 595 /** 596 * {@inheritDoc} 597 * Invalid indexes are ignored. 598 */ 599 @Override 600 final public void configureBaudRateFromIndex(int index) { 601 if (validBaudRates().length > index && index > -1 ) { 602 mBaudRate = validBaudRates()[index]; 603 log.debug("mBaudRate set by index to: {}", mBaudRate); 604 } else { 605 // expected for simulators extending serialPortAdapter, mBaudRate already null 606 log.debug("no baud rate index {} in array size {}", index, validBaudRates().length); 607 } 608 } 609 610 protected String mBaudRate = null; 611 612 @Override 613 public int defaultBaudIndex() { 614 return -1; 615 } 616 617 /** 618 * {@inheritDoc} 619 */ 620 @Override 621 public String getCurrentBaudRate() { 622 if (mBaudRate == null) { 623 return ""; 624 } 625 return mBaudRate; 626 } 627 628 /** 629 * {@inheritDoc} 630 */ 631 @Override 632 final public String getCurrentBaudNumber() { 633 int[] numbers = validBaudNumbers(); 634 String[] rates = validBaudRates(); 635 if (numbers == null || rates == null || numbers.length != rates.length) { // entries in arrays should correspond 636 return ""; 637 } 638 String baudNumString = ""; 639 // first try to find the configured baud rate value 640 if (mBaudRate != null) { 641 for (int i = 0; i < numbers.length; i++) { 642 if (rates[i].equals(mBaudRate)) { 643 baudNumString = Integer.toString(numbers[i]); 644 break; 645 } 646 } 647 } else if (defaultBaudIndex() > -1) { 648 // use default 649 baudNumString = Integer.toString(numbers[defaultBaudIndex()]); 650 log.debug("using default port speed {}", baudNumString); 651 } 652 log.debug("mBaudRate = {}, matched to string {}", mBaudRate, baudNumString); 653 return baudNumString; 654 } 655 656 @Override 657 final public int getCurrentBaudIndex() { 658 if (mBaudRate != null) { 659 String[] rates = validBaudRates(); 660 // find the configured baud rate value 661 for (int i = 0; i < rates.length; i++) { 662 if (rates[i].equals(mBaudRate)) { 663 return i; 664 } 665 } 666 } 667 return defaultBaudIndex(); // default index or -1 if port speed not supported 668 } 669 670 /** 671 * {@inheritDoc} 672 */ 673 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS", 674 justification = "null signals incorrect implementation of portcontroller") 675 @Override 676 public String[] validBaudRates() { 677 log.error("default validBaudRates implementation should not be used", new Exception()); 678 return null; 679 } 680 681 /** 682 * {@inheritDoc} 683 */ 684 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS", 685 justification = "null signals incorrect implementation of portcontroller") 686 @Override 687 public int[] validBaudNumbers() { 688 log.error("default validBaudNumbers implementation should not be used", new Exception()); 689 return null; 690 } 691 692 /** 693 * Convert a baud rate I18N String to an int number, e.g. "9,600 baud" to 9600. 694 * <p> 695 * Uses the validBaudNumbers() and validBaudRates() methods to do this. 696 * 697 * @param currentBaudRate a rate from validBaudRates() 698 * @return baudrate as integer if available and matching first digits in currentBaudRate, 699 * 0 if baudrate not supported by this adapter, 700 * -1 if no match (configuration system should prevent this) 701 */ 702 final public int currentBaudNumber(String currentBaudRate) { 703 String[] rates = validBaudRates(); 704 int[] numbers = validBaudNumbers(); 705 706 // return if arrays invalid 707 if (numbers == null) { 708 log.error("numbers array null in currentBaudNumber()"); 709 return -1; 710 } 711 if (rates == null) { 712 log.error("rates array null in currentBaudNumber()"); 713 return -1; 714 } 715 if (numbers.length != rates.length) { 716 log.error("arrays are of different length in currentBaudNumber: {} vs {}", numbers.length, rates.length); 717 return -1; 718 } 719 if (numbers.length < 1) { 720 log.warn("baudrate is not supported by adapter"); 721 return 0; 722 } 723 // find the baud rate value 724 for (int i = 0; i < numbers.length; i++) { 725 if (rates[i].equals(currentBaudRate)) { 726 return numbers[i]; 727 } 728 } 729 730 // no match 731 log.error("no match to ({}) in currentBaudNumber", currentBaudRate); 732 return -1; 733 } 734 735 /** 736 * Set event logging. 737 * @param port Serial port to configure 738 */ 739 //@Deprecated(forRemoval=true) // with PureJavaComm 740 protected void setPortEventLogging(purejavacomm.SerialPort port) { 741 // arrange to notify later 742 try { 743 port.addEventListener(new purejavacomm.SerialPortEventListener() { 744 @Override 745 public void serialEvent(purejavacomm.SerialPortEvent e) { 746 int type = e.getEventType(); 747 switch (type) { 748 case purejavacomm.SerialPortEvent.DATA_AVAILABLE: 749 log.info("SerialEvent: DATA_AVAILABLE is {}", e.getNewValue()); // NOI18N 750 return; 751 case purejavacomm.SerialPortEvent.OUTPUT_BUFFER_EMPTY: 752 log.info("SerialEvent: OUTPUT_BUFFER_EMPTY is {}", e.getNewValue()); // NOI18N 753 return; 754 case purejavacomm.SerialPortEvent.CTS: 755 log.info("SerialEvent: CTS is {}", e.getNewValue()); // NOI18N 756 return; 757 case purejavacomm.SerialPortEvent.DSR: 758 log.info("SerialEvent: DSR is {}", e.getNewValue()); // NOI18N 759 return; 760 case purejavacomm.SerialPortEvent.RI: 761 log.info("SerialEvent: RI is {}", e.getNewValue()); // NOI18N 762 return; 763 case purejavacomm.SerialPortEvent.CD: 764 log.info("SerialEvent: CD is {}", e.getNewValue()); // NOI18N 765 return; 766 case purejavacomm.SerialPortEvent.OE: 767 log.info("SerialEvent: OE (overrun error) is {}", e.getNewValue()); // NOI18N 768 return; 769 case purejavacomm.SerialPortEvent.PE: 770 log.info("SerialEvent: PE (parity error) is {}", e.getNewValue()); // NOI18N 771 return; 772 case purejavacomm.SerialPortEvent.FE: 773 log.info("SerialEvent: FE (framing error) is {}", e.getNewValue()); // NOI18N 774 return; 775 case purejavacomm.SerialPortEvent.BI: 776 log.info("SerialEvent: BI (break interrupt) is {}", e.getNewValue()); // NOI18N 777 return; 778 default: 779 log.info("SerialEvent of unknown type: {} value: {}", type, e.getNewValue()); // NOI18N 780 } 781 } 782 } 783 ); 784 } catch (java.util.TooManyListenersException ex) { 785 log.warn("cannot set listener for SerialPortEvents; was one already set?"); 786 } 787 788 try { 789 port.notifyOnFramingError(true); 790 } catch (Exception e) { 791 log.debug("Could not notifyOnFramingError", e); // NOI18N 792 } 793 794 try { 795 port.notifyOnBreakInterrupt(true); 796 } catch (Exception e) { 797 log.debug("Could not notifyOnBreakInterrupt", e); // NOI18N 798 } 799 800 try { 801 port.notifyOnParityError(true); 802 } catch (Exception e) { 803 log.debug("Could not notifyOnParityError", e); // NOI18N 804 } 805 806 try { 807 port.notifyOnOverrunError(true); 808 } catch (Exception e) { 809 log.debug("Could not notifyOnOverrunError", e); // NOI18N 810 } 811 812 port.notifyOnCarrierDetect(true); 813 port.notifyOnCTS(true); 814 port.notifyOnDSR(true); 815 } 816 817 /** 818 * {@inheritDoc} 819 * Each serial port adapter should handle this and it should be abstract. 820 */ 821 @Override 822 protected void closeConnection(){} 823 824 /** 825 * Re-setup the connection. 826 * Called when the physical connection has reconnected and can be linked to 827 * this connection. 828 * Each port adapter should handle this and it should be abstract. 829 */ 830 @Override 831 protected void resetupConnection(){} 832 833 /** 834 * {@inheritDoc} 835 * Attempts a re-connection to the serial port from the main reconnect 836 * thread. 837 */ 838 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value="SLF4J_FORMAT_SHOULD_BE_CONST", 839 justification="I18N of Info Message") 840 //@Deprecated(forRemoval=true) // with purejavacomm 841 @Override 842 protected void reconnectFromLoop(int retryNum){ 843 try { 844 log.info("Retrying Connection attempt {} for {}", retryNum,mPort); 845 Enumeration<purejavacomm.CommPortIdentifier> portIDs = purejavacomm.CommPortIdentifier.getPortIdentifiers(); 846 while (portIDs.hasMoreElements()) { 847 purejavacomm.CommPortIdentifier id = portIDs.nextElement(); 848 // filter out line printers 849 if (id.getPortType() != purejavacomm.CommPortIdentifier.PORT_PARALLEL) // accumulate the names in a vector 850 { 851 if (id.getName().equals(mPort)) { 852 log.info(Bundle.getMessage("ReconnectPortReAppear", mPort)); 853 openPort(mPort, "jmri"); 854 } 855 } 856 } 857 if (retryNum % 10==0) { 858 log.info(Bundle.getMessage("ReconnectSerialTip")); 859 } 860 } catch (RuntimeException e) { 861 log.warn(Bundle.getMessage("ReconnectFail",(mPort == null ? "null" : mPort))); 862 863 } 864 } 865 866 867 public static class SerialPort { 868 869 public static final int LISTENING_EVENT_DATA_AVAILABLE = 870 com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_DATA_AVAILABLE; 871 872 public static final int ONE_STOP_BIT = com.fazecast.jSerialComm.SerialPort.ONE_STOP_BIT; 873 public static final int NO_PARITY = com.fazecast.jSerialComm.SerialPort.NO_PARITY; 874 875 private final com.fazecast.jSerialComm.SerialPort serialPort; 876 877 private SerialPort(com.fazecast.jSerialComm.SerialPort serialPort) { 878 this.serialPort = serialPort; 879 } 880 881 public void addDataListener(SerialPortDataListener listener) { 882 this.serialPort.addDataListener(new com.fazecast.jSerialComm.SerialPortDataListener() { 883 @Override 884 public int getListeningEvents() { 885 return listener.getListeningEvents(); 886 } 887 888 @Override 889 public void serialEvent(com.fazecast.jSerialComm.SerialPortEvent event) { 890 listener.serialEvent(new SerialPortEvent(event)); 891 } 892 }); 893 } 894 895 public InputStream getInputStream() { 896 return this.serialPort.getInputStream(); 897 } 898 899 public OutputStream getOutputStream() { 900 return this.serialPort.getOutputStream(); 901 } 902 903 public void setRTS() { 904 this.serialPort.setRTS(); 905 } 906 907 public void setBaudRate(int baudrate) { 908 this.serialPort.setBaudRate(baudrate); 909 } 910 911 public int getBaudRate() { 912 return this.serialPort.getBaudRate(); 913 } 914 915 public void setNumDataBits(int bits) { 916 this.serialPort.setNumDataBits(bits); 917 } 918 919 public void setDTR() { 920 this.serialPort.setDTR(); 921 } 922 923 public boolean getDTR() { 924 return this.serialPort.getDTR(); 925 } 926 927 public boolean getRTS() { 928 return this.serialPort.getRTS(); 929 } 930 931 public boolean getDSR() { 932 return this.serialPort.getDSR(); 933 } 934 935 public boolean getCTS() { 936 return this.serialPort.getCTS(); 937 } 938 939 public boolean getDCD() { 940 return this.serialPort.getDCD(); 941 } 942 943 public void setBreak() { 944 this.serialPort.setBreak(); 945 } 946 947 public void clearBreak() { 948 this.serialPort.clearBreak(); 949 } 950 951 public void closePort() { 952 this.serialPort.closePort(); 953 } 954 955 public String getDescriptivePortName() { 956 return this.serialPort.getDescriptivePortName(); 957 } 958 959 @Override 960 public String toString() { 961 return this.serialPort.toString(); 962 } 963 964 } 965 966 967 public static interface SerialPortDataListener { 968 969 void serialEvent(SerialPortEvent serialPortEvent); 970 971 public int getListeningEvents(); 972 973 } 974 975 public static class SerialPortEvent { 976 977 private final com.fazecast.jSerialComm.SerialPortEvent event; 978 979 private SerialPortEvent(com.fazecast.jSerialComm.SerialPortEvent event) { 980 this.event = event; 981 } 982 983 public int getEventType() { 984 return event.getEventType(); 985 } 986 } 987 988 989 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractSerialPortController.class); 990 991}