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