001/** 002 * Copyright (c) 2014 Digi International Inc., 003 * All rights not expressly granted are reserved. 004 * 005 * This Source Code Form is subject to the terms of the Mozilla Public 006 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 007 * You can obtain one at http://mozilla.org/MPL/2.0/. 008 * 009 * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343 010 * ======================================================================= 011 */ 012package com.digi.xbee.api.connection.serial; 013 014import gnu.io.CommPortIdentifier; 015import gnu.io.CommPortOwnershipListener; 016import gnu.io.NoSuchPortException; 017import gnu.io.PortInUseException; 018import gnu.io.RXTXPort; 019import gnu.io.SerialPortEvent; 020import gnu.io.SerialPortEventListener; 021import gnu.io.UnsupportedCommOperationException; 022 023import java.io.IOException; 024import java.io.InputStream; 025import java.io.OutputStream; 026import java.util.ArrayList; 027import java.util.Enumeration; 028import java.util.TooManyListenersException; 029 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033import com.digi.xbee.api.exceptions.ConnectionException; 034import com.digi.xbee.api.exceptions.InterfaceInUseException; 035import com.digi.xbee.api.exceptions.InvalidConfigurationException; 036import com.digi.xbee.api.exceptions.InvalidInterfaceException; 037import com.digi.xbee.api.exceptions.PermissionDeniedException; 038 039/** 040 * This class represents a serial port using the RxTx library to communicate 041 * with it. 042 */ 043public class SerialPortRxTx extends AbstractSerialPort implements SerialPortEventListener, CommPortOwnershipListener { 044 045 // Variables. 046 private final Object lock = new Object(); 047 048 private RXTXPort serialPort; 049 050 private InputStream inputStream; 051 052 private OutputStream outputStream; 053 054 private Thread breakThread; 055 056 private boolean breakEnabled = false; 057 058 private CommPortIdentifier portIdentifier = null; 059 060 private Logger logger; 061 062 /** 063 * Class constructor. Instances a new {@code SerialPortRxTx} object using 064 * the given parameters. 065 * 066 * @param port Serial port name to use. 067 * @param parameters Serial port parameters. 068 * 069 * @throws NullPointerException if {@code port == null} or 070 * if {@code parameters == null}. 071 * 072 * @see #SerialPortRxTx(String, int) 073 * @see #SerialPortRxTx(String, int, int) 074 * @see #SerialPortRxTx(String, SerialPortParameters, int) 075 * @see SerialPortParameters 076 */ 077 public SerialPortRxTx(String port, SerialPortParameters parameters) { 078 this(port, parameters, DEFAULT_PORT_TIMEOUT); 079 } 080 081 /** 082 * Class constructor. Instances a new {@code SerialPortRxTx} object using 083 * the given parameters. 084 * 085 * @param port Serial port name to use. 086 * @param parameters Serial port parameters. 087 * @param receiveTimeout Serial port receive timeout in milliseconds. 088 * 089 * @throws IllegalArgumentException if {@code receiveTimeout < 0}. 090 * @throws NullPointerException if {@code port == null} or 091 * if {@code parameters == null}. 092 * 093 * @see #SerialPortRxTx(String, int) 094 * @see #SerialPortRxTx(String, int, int) 095 * @see #SerialPortRxTx(String, SerialPortParameters) 096 * @see SerialPortParameters 097 */ 098 public SerialPortRxTx(String port, SerialPortParameters parameters, int receiveTimeout) { 099 super(port, parameters, receiveTimeout); 100 this.logger = LoggerFactory.getLogger(SerialPortRxTx.class); 101 } 102 103 /** 104 * Class constructor. Instances a new {@code SerialPortRxTx} object using 105 * the given parameters. 106 * 107 * @param port Serial port name to use. 108 * @param baudRate Serial port baud rate, the rest of parameters will be 109 * set by default. 110 * 111 * @throws NullPointerException if {@code port == null}. 112 * 113 * @see #DEFAULT_DATA_BITS 114 * @see #DEFAULT_FLOW_CONTROL 115 * @see #DEFAULT_PARITY 116 * @see #DEFAULT_STOP_BITS 117 * @see #DEFAULT_PORT_TIMEOUT 118 * @see #SerialPortRxTx(String, int, int) 119 * @see #SerialPortRxTx(String, SerialPortParameters) 120 * @see #SerialPortRxTx(String, SerialPortParameters, int) 121 * @see SerialPortParameters 122 */ 123 public SerialPortRxTx(String port, int baudRate) { 124 this(port, baudRate, DEFAULT_PORT_TIMEOUT); 125 } 126 127 /** 128 * Class constructor. Instances a new {@code SerialPortRxTx} object using 129 * the given parameters. 130 * 131 * @param port Serial port name to use. 132 * @param baudRate Serial port baud rate, the rest of parameters will be 133 * set by default. 134 * @param receiveTimeout Serial port receive timeout in milliseconds. 135 * 136 * @throws IllegalArgumentException if {@code receiveTimeout < 0}. 137 * @throws NullPointerException if {@code port == null}. 138 * 139 * @see #DEFAULT_DATA_BITS 140 * @see #DEFAULT_FLOW_CONTROL 141 * @see #DEFAULT_PARITY 142 * @see #DEFAULT_STOP_BITS 143 * @see #SerialPortRxTx(String, int) 144 * @see #SerialPortRxTx(String, SerialPortParameters) 145 * @see #SerialPortRxTx(String, SerialPortParameters, int) 146 * @see SerialPortParameters 147 */ 148 public SerialPortRxTx(String port, int baudRate, int receiveTimeout) { 149 super(port, baudRate, receiveTimeout); 150 this.logger = LoggerFactory.getLogger(SerialPortRxTx.class); 151 } 152 153 /* 154 * (non-Javadoc) 155 * @see com.digi.xbee.api.connection.IConnectionInterface#open() 156 */ 157 @Override 158 public void open() throws InterfaceInUseException, InvalidInterfaceException, InvalidConfigurationException, PermissionDeniedException { 159 // Check that the given serial port exists. 160 try { 161 portIdentifier = CommPortIdentifier.getPortIdentifier(port); 162 } catch (NoSuchPortException e) { 163 throw new InvalidInterfaceException("No such port: " + port, e); 164 } 165 try { 166 // Get the serial port. 167 serialPort = (RXTXPort)portIdentifier.open(PORT_ALIAS + " " + port, receiveTimeout); 168 // Set port as connected. 169 connectionOpen = true; 170 // Configure the port. 171 if (parameters == null) 172 parameters = new SerialPortParameters(baudRate, DEFAULT_DATA_BITS, DEFAULT_STOP_BITS, DEFAULT_PARITY, DEFAULT_FLOW_CONTROL); 173 serialPort.setSerialPortParams(baudRate, parameters.dataBits, parameters.stopBits, parameters.parity); 174 serialPort.setFlowControlMode(parameters.flowControl); 175 176 serialPort.enableReceiveTimeout(receiveTimeout); 177 178 // Set the port ownership. 179 portIdentifier.addPortOwnershipListener(this); 180 181 // Initialize input and output streams before setting the listener. 182 inputStream = serialPort.getInputStream(); 183 outputStream = serialPort.getOutputStream(); 184 // Activate data received event. 185 serialPort.notifyOnDataAvailable(true); 186 // Register serial port event listener to be notified when data is available. 187 serialPort.addEventListener(this); 188 } catch (PortInUseException e) { 189 throw new InterfaceInUseException("Port " + port + " is already in use by other application(s)", e); 190 } catch (UnsupportedCommOperationException e) { 191 throw new InvalidConfigurationException(e.getMessage(), e); 192 } catch (TooManyListenersException e) { 193 throw new InvalidConfigurationException(e.getMessage(), e); 194 } 195 } 196 197 /* 198 * (non-Javadoc) 199 * @see com.digi.xbee.api.connection.IConnectionInterface#close() 200 */ 201 @Override 202 public void close() { 203 try { 204 if (inputStream != null) { 205 inputStream.close(); 206 inputStream = null; 207 } 208 if (outputStream != null) { 209 outputStream.close(); 210 outputStream = null; 211 } 212 } catch (IOException e) { 213 logger.error(e.getMessage(), e); 214 } 215 synchronized (lock) { 216 if (serialPort != null) { 217 try { 218 serialPort.notifyOnDataAvailable(false); 219 serialPort.removeEventListener(); 220 portIdentifier.removePortOwnershipListener(this); 221 serialPort.close(); 222 serialPort = null; 223 connectionOpen = false; 224 } catch (Exception e) { } 225 } 226 } 227 } 228 229 /* 230 * (non-Javadoc) 231 * @see gnu.io.SerialPortEventListener#serialEvent(gnu.io.SerialPortEvent) 232 */ 233 @Override 234 public void serialEvent(SerialPortEvent event) { 235 // Listen only to data available event. 236 switch (event.getEventType()) { 237 case SerialPortEvent.DATA_AVAILABLE: 238 // Check if serial device has been disconnected or not. 239 try { 240 getInputStream().available(); 241 } catch (Exception e) { 242 // Serial device has been disconnected. 243 close(); 244 synchronized (this) { 245 this.notify(); 246 } 247 break; 248 } 249 // Notify data is available by waking up the read thread. 250 try { 251 if (getInputStream().available() > 0) { 252 synchronized (this) { 253 this.notify(); 254 } 255 } 256 } catch (Exception e) { 257 logger.error(e.getMessage(), e); 258 } 259 break; 260 } 261 } 262 263 /* 264 * (non-Javadoc) 265 * @see java.lang.Object#toString() 266 */ 267 @Override 268 public String toString() { 269 return super.toString(); 270 } 271 272 /* 273 * (non-Javadoc) 274 * @see com.digi.xbee.api.connection.serial.AbstractSerialPort#setBreak(boolean) 275 */ 276 @Override 277 public void setBreak(boolean enabled) { 278 breakEnabled = enabled; 279 if(breakEnabled){ 280 if (breakThread == null) { 281 breakThread = new Thread() { 282 public void run() { 283 while (breakEnabled && serialPort != null) 284 serialPort.sendBreak(100); 285 }; 286 }; 287 breakThread.start(); 288 } 289 } else { 290 if (breakThread != null) 291 breakThread.interrupt(); 292 breakThread = null; 293 serialPort.sendBreak(0); 294 } 295 } 296 297 /* 298 * (non-Javadoc) 299 * @see com.digi.xbee.api.connection.IConnectionInterface#getInputStream() 300 */ 301 @Override 302 public InputStream getInputStream() { 303 return inputStream; 304 } 305 306 /* 307 * (non-Javadoc) 308 * @see com.digi.xbee.api.connection.IConnectionInterface#getOutputStream() 309 */ 310 @Override 311 public OutputStream getOutputStream() { 312 return outputStream; 313 } 314 315 /* 316 * (non-Javadoc) 317 * @see com.digi.xbee.api.connection.serial.AbstractSerialPort#setReadTimeout(int) 318 */ 319 @Override 320 public void setReadTimeout(int timeout) { 321 serialPort.disableReceiveTimeout(); 322 serialPort.enableReceiveTimeout(timeout); 323 } 324 325 /* 326 * (non-Javadoc) 327 * @see com.digi.xbee.api.connection.serial.AbstractSerialPort#getReadTimeout() 328 */ 329 @Override 330 public int getReadTimeout() { 331 return serialPort.getReceiveTimeout(); 332 } 333 334 /* 335 * (non-Javadoc) 336 * @see com.digi.xbee.api.connection.serial.AbstractSerialPort#setDTR(boolean) 337 */ 338 @Override 339 public void setDTR(boolean state) { 340 serialPort.setDTR(state); 341 } 342 343 /* 344 * (non-Javadoc) 345 * @see com.digi.xbee.api.connection.serial.AbstractSerialPort#setRTS(boolean) 346 */ 347 @Override 348 public void setRTS(boolean state) { 349 serialPort.setRTS(state); 350 } 351 352 353 /* 354 * (non-Javadoc) 355 * @see com.digi.xbee.api.connection.serial.AbstractSerialPort#setPortParameters(int, int, int, int, int) 356 */ 357 @Override 358 public void setPortParameters(int baudRate, int dataBits, int stopBits, 359 int parity, int flowControl) throws InvalidConfigurationException, ConnectionException { 360 parameters = new SerialPortParameters(baudRate, dataBits, stopBits, parity, flowControl); 361 362 if (serialPort != null) { 363 try { 364 serialPort.setSerialPortParams(baudRate, dataBits, stopBits, parity); 365 serialPort.setFlowControlMode(flowControl); 366 } catch (UnsupportedCommOperationException e) { 367 throw new InvalidConfigurationException(e.getMessage(), e); 368 } 369 } 370 } 371 372 /* 373 * (non-Javadoc) 374 * @see com.digi.xbee.api.connection.serial.AbstractSerialPort#sendBreak(int) 375 */ 376 @Override 377 public void sendBreak(int duration) { 378 if (serialPort != null) 379 serialPort.sendBreak(duration); 380 } 381 382 /* 383 * (non-Javadoc) 384 * @see gnu.io.CommPortOwnershipListener#ownershipChange(int) 385 */ 386 @Override 387 public void ownershipChange(int nType) { 388 switch (nType) { 389 case CommPortOwnershipListener.PORT_OWNERSHIP_REQUESTED: 390 onSerialOwnershipRequested(null); 391 break; 392 } 393 } 394 395 /** 396 * Releases the port on any ownership request in the same application 397 * instance. 398 * 399 * @param data The port requester. 400 */ 401 private void onSerialOwnershipRequested(Object data) { 402 try { 403 throw new Exception(); 404 } catch (Exception e) { 405 StackTraceElement[] elems = e.getStackTrace(); 406 String requester = elems[elems.length - 4].getClassName(); 407 synchronized (this) { 408 this.notify(); 409 } 410 close(); 411 String myPackage = this.getClass().getPackage().getName(); 412 if (requester.startsWith(myPackage)) 413 requester = "another AT connection"; 414 logger.warn("Connection for port {} canceled due to ownership request from {}.", port, requester); 415 } 416 } 417 418 /** 419 * Retrieves the list of available serial ports in the system. 420 * 421 * @return List of available serial ports. 422 * 423 * @see #listSerialPortsInfo() 424 */ 425 public static String[] listSerialPorts() { 426 ArrayList<String> serialPorts = new ArrayList<String>(); 427 428 @SuppressWarnings("unchecked") 429 Enumeration<CommPortIdentifier> comPorts = CommPortIdentifier.getPortIdentifiers(); 430 if (comPorts == null) 431 return serialPorts.toArray(new String[serialPorts.size()]); 432 433 while (comPorts.hasMoreElements()) { 434 CommPortIdentifier identifier = (CommPortIdentifier)comPorts.nextElement(); 435 if (identifier == null) 436 continue; 437 String strName = identifier.getName(); 438 serialPorts.add(strName); 439 } 440 return serialPorts.toArray(new String[serialPorts.size()]); 441 } 442 443 /** 444 * Retrieves the list of available serial ports with their information. 445 * 446 * @return List of available serial ports with their information. 447 * 448 * @see #listSerialPorts() 449 * @see SerialPortInfo 450 */ 451 public static ArrayList<SerialPortInfo> listSerialPortsInfo() { 452 ArrayList<SerialPortInfo> ports = new ArrayList<SerialPortInfo>(); 453 454 @SuppressWarnings("unchecked") 455 Enumeration<CommPortIdentifier> comPorts = CommPortIdentifier.getPortIdentifiers(); 456 if (comPorts == null) 457 return ports; 458 459 while (comPorts.hasMoreElements()) { 460 CommPortIdentifier identifier = (CommPortIdentifier)comPorts.nextElement(); 461 if (identifier == null) 462 continue; 463 ports.add(new SerialPortInfo(identifier.getName())); 464 } 465 return ports; 466 } 467 468 /* 469 * (non-Javadoc) 470 * @see com.digi.xbee.api.connection.serial.AbstractSerialPort#isCTS() 471 */ 472 @Override 473 public boolean isCTS() { 474 return serialPort.isCTS(); 475 } 476 477 /* 478 * (non-Javadoc) 479 * @see com.digi.xbee.api.connection.serial.AbstractSerialPort#isDSR() 480 */ 481 @Override 482 public boolean isDSR() { 483 return serialPort.isDSR(); 484 } 485 486 /* 487 * (non-Javadoc) 488 * @see com.digi.xbee.api.connection.serial.AbstractSerialPort#isCD() 489 */ 490 @Override 491 public boolean isCD() { 492 return serialPort.isCD(); 493 } 494}