
com.embeddedunveiled.serial.SerialComManager Maven / Gradle / Ivy
Show all versions of scm Show documentation
/**
* Author : Rishi Gupta
*
* This file is part of 'serial communication manager' library.
*
* The 'serial communication manager' is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* The 'serial communication manager' is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with serial communication manager. If not, see .
*/
package com.embeddedunveiled.serial;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
/**
* This class is the root of scm library. The applications should call methods defined in this class only.
*/
public final class SerialComManager {
public static final String JAVA_LIB_VERSION = "1.0.3";
public static boolean DEBUG = true;
private static int osType = -1;
public static final int OS_LINUX = 1;
public static final int OS_WINDOWS = 2;
public static final int OS_SOLARIS = 3;
public static final int OS_MAC_OS_X = 4;
public static final int DEFAULT_READBYTECOUNT = 1024;
/** Pre-defined constants for baud rate values. */
public enum BAUDRATE {
B0(0), B50(50), B75(75), B110(110), B134(134), B150(150), B200(200), B300(300), B600(600), B1200(1200),
B1800(1800), B2400(2400), B4800(4800), B9600(9600), B14400(14400), B19200(19200), B28800(28800), B38400(38400),
B56000(56000), B57600(57600), B115200(115200), B128000(128000), B153600(153600), B230400(230400), B256000(256000),
B460800(460800), B500000(500000), B576000(576000), B921600(921600), B1000000(1000000), B1152000(1152000),
B1500000(1500000),B2000000(2000000), B2500000(2500000), B3000000(3000000), B3500000(3500000), B4000000(4000000),
BCUSTOM(251);
private int value;
private BAUDRATE(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined constants for number of data bits in a serial frame. */
public enum DATABITS {
DB_5(5), DB_6(6), DB_7(7), DB_8(8);
private int value;
private DATABITS(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined constants for number of stop bits in a serial frame. */
// SB_1_5(4) is 1.5 stop bits.
public enum STOPBITS {
SB_1(1), SB_1_5(4), SB_2(2);
private int value;
private STOPBITS(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined constants for enabling type of parity in a serial frame. */
public enum PARITY {
P_NONE(1), P_ODD(2), P_EVEN(3), P_MARK(4), P_SPACE(5);
private int value;
private PARITY(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined constants for controlling data flow between DTE and DCE. */
public enum FLOWCONTROL {
NONE(1), HARDWARE(2), SOFTWARE(3);
private int value;
private FLOWCONTROL(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined constants for defining endianness of data to be sent over serial port. */
public enum ENDIAN {
E_LITTLE(1), E_BIG(2), E_DEFAULT(3);
private int value;
private ENDIAN(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined constants for defining number of bytes given data can be represented in. */
public enum NUMOFBYTES {
NUM_2(2), NUM_4(4);
private int value;
private NUMOFBYTES(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Pre-defined constants for defining file transfer protocol to use. */
public enum FILETXPROTO {
XMODEM(1);
private int value;
private FILETXPROTO(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
/** Mask bits for UART control lines. */
public static final int CTS = 0x01; // 0000001
public static final int DSR = 0x02; // 0000010
public static final int DCD = 0x04; // 0000100
public static final int RI = 0x08; // 0001000
public static final int LOOP = 0x10; // 0010000
public static final int RTS = 0x20; // 0100000
public static final int DTR = 0x40; // 1000000
/** These properties are used to load OS specific native library. */
public static final String osName = System.getProperty("os.name");
public static final String osArch = System.getProperty("os.arch").toLowerCase();
public static final String userHome = System.getProperty("user.home");
public static final String javaTmpDir = System.getProperty("java.io.tmpdir");
public static final String fileSeparator = System.getProperty("file.separator");
public static final String javaLibPath = System.getProperty("java.library.path").toLowerCase();
private static final String HEXNUM = "0123456789ABCDEF";
/** Maintain integrity and consistency among all operations, therefore synchronize them for
* making structural changes. This array can be sorted array if scaled to large scale. */
private ArrayList handleInfo = new ArrayList();
private List mPortHandleInfo = Collections.synchronizedList(handleInfo);
private SerialComJNINativeInterface mNativeInterface = null;
private SerialComErrorMapper mErrMapper = null;
private SerialComCompletionDispatcher mEventCompletionDispatcher = null;
private Object lock = new Object();
/**
* Allocates a new SerialComManager object. Constructor, initialize various classes and load native libraries.
*/
public SerialComManager() {
String osNameMatch = osName.toLowerCase();
if(osNameMatch.contains("linux")) {
osType = OS_LINUX;
}else if(osNameMatch.contains("windows")) {
osType = OS_WINDOWS;
}else if(osNameMatch.contains("solaris") || osNameMatch.contains("sunos")) {
osType = OS_SOLARIS;
}else if(osNameMatch.contains("mac os") || osNameMatch.contains("macos") || osNameMatch.contains("darwin")) {
osType = OS_MAC_OS_X;
}else {
//TODO
}
mErrMapper = new SerialComErrorMapper();
mNativeInterface = new SerialComJNINativeInterface();
mEventCompletionDispatcher = new SerialComCompletionDispatcher(mNativeInterface, mErrMapper, mPortHandleInfo);
}
/**
* Gives library versions of java and native modules.
*
* @return Java and C library versions implementing this library.
*/
public String getLibraryVersions() {
String version = null;
String nativeLibversion = mNativeInterface.getNativeLibraryVersion();
if(nativeLibversion != null) {
version = "Java lib version: " + JAVA_LIB_VERSION + "\n" + "Native lib version: " + nativeLibversion;
}else {
version = "Java lib version: " + JAVA_LIB_VERSION + "\n" + "Native lib version: " + "Could not be determined !";
}
return version;
}
/**
* Gives operating system type as identified by this library. To interpret return integer see constants defined
* SerialComManager class.
*
* @return Operating system type as identified by the scm library
* @throws IllegalStateException if application calls this method without first creating an instance of SerialComManager class
*/
public static int getOSType() throws IllegalStateException {
if(osType == -1) {
throw new IllegalStateException("getOSType() " + SerialComErrorMapper.ERR_SCM_DOES_NOT_INSTANTIATED);
}
return osType;
}
/**
* Returns all available UART style ports available on this system, otherwise an empty array of strings, if no serial style port is
* found in the system.
*
* This method may be used to find valid serial ports for communications before opening them for developing more robust code.
*
* This should find regular UART ports, hw/sw virtual COM ports, port server, USB-UART converter, bluetooth/3G dongles,
* ports connected through USB hub/expander etc.
*
* Note : The BIOS may ignore UART ports on a PCI card and therefore BIOS settings has to be corrected if you modified
* default BIOS in custom OS.
*
* @return Available UART style ports name for windows, full path with name for Unix like OS, returns empty array if no ports found.
* @throws SerialComException if an I/O error occurs.
*/
public String[] listAvailableComPorts() throws SerialComException {
SerialComPortsList scpl = new SerialComPortsList(this.mNativeInterface);
SerialComRetStatus retStatus = new SerialComRetStatus(1);
String[] availablePorts = scpl.listAvailableComPorts(retStatus);
if(availablePorts != null) {
return availablePorts;
}else {
if(retStatus.status == 1) {
return new String[]{};
}else if(retStatus.status < 0) {
throw new SerialComException("listAvailableComPorts", mErrMapper.getMappedError(retStatus.status));
}else {
}
}
return null;
}
/**
* This opens a serial port for communication. If an attempt is made to open a port which is already opened exception in throw.
*
* For Linux and Mac OS X, if exclusiveOwnerShip is true, before this method return, the caller will either be exclusive owner
* or not. If the caller is successful in becoming exclusive owner than all the attempt to open the same port again will cause
* native code to return error. Note that a root owned process (root user) will still be able to open the port.
*
* The exclusiveOwnerShip must be true for Windows as it does not allow sharing COM ports. An exception is thrown if
* exclusiveOwnerShip is set to false.
*
* For Solaris, exclusiveOwnerShip should be set to false as of now.
*
* DTR acts as a modem on-hook/off-hook control sometimes. By default when port is opened both DTR and RTS are enabled.
* Modern modems are highly flexible in their dependency, working and configurations. Please consult modem manual.
*
* This method is thread safe.
*
* @param portName name of the port to be opened for communication
* @param enableRead allows application to read bytes from this port
* @param enableWrite allows application to write bytes to this port
* @param exclusiveOwnerShip application wants to become exclusive owner of this port or not
* @return handle of the port successfully opened
* @throws SerialComException if both enableWrite and enableRead are false, trying to become exclusive owner when port is already opened
* @throws IllegalArgumentException if portName is null or invalid length
*/
public long openComPort(String portName, boolean enableRead, boolean enableWrite, boolean exclusiveOwnerShip) throws SerialComException {
long handle = 0;
if(portName == null) {
throw new IllegalArgumentException("openComPort(), " + SerialComErrorMapper.ERR_PORT_NAME_FOR_PORT_OPENING);
}
portName = portName.trim();
if(portName.length() == 0) {
throw new IllegalArgumentException("openComPort(), " + SerialComErrorMapper.ERR_PORT_NAME_FOR_PORT_OPENING);
}
if((enableRead == false) && (enableWrite == false)) {
throw new SerialComException(portName, "openComPort()", "Enable at-least read, write or both.");
}
// For windows COM port can not be shared, so throw exception
if(getOSType() == OS_WINDOWS) {
if(exclusiveOwnerShip == false) {
throw new SerialComException(portName, "openComPort()", SerialComErrorMapper.ERR_WIN_OWNERSHIP);
}
}
synchronized(lock) {
/* Try to reduce transitions from java to JNI layer as it is possible here by performing check in java layer itself. */
if(exclusiveOwnerShip == true) {
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsPort(portName)) {
throw new SerialComException(portName, "openComPort()", SerialComErrorMapper.ERR_PORT_ALREADY_OPEN);
}
}
}
handle = mNativeInterface.openComPort(portName, enableRead, enableWrite, exclusiveOwnerShip);
if(handle < 0) {
throw new SerialComException(portName, "openComPort()", mErrMapper.getMappedError(handle));
}
boolean added = mPortHandleInfo.add(new SerialComPortHandleInfo(portName, handle, null, null, null));
if(added != true) {
closeComPort(handle);
throw new SerialComException(portName, "openComPort()", SerialComErrorMapper.ERR_SCM_NOT_STORE_PORTINFO);
}
}
return handle;
}
/**
* Close the serial port. Application should unregister listeners if it has registered any.
*
* DTR line is dropped when port is closed.
*
* This method is thread safe.
*
* @param handle of the port to be closed
* @return Return true on success in closing the port false otherwise
* @throws SerialComException if invalid handle is passed or when it fails in closing the port
* @throws IllegalStateException if application tries to close port while data/event listener exist
*/
public boolean closeComPort(long handle) throws SerialComException {
boolean handlefound = false;
SerialComPortHandleInfo mHandleInfo = null;
synchronized(lock) {
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
mHandleInfo = mInfo;
break;
}
}
if(handlefound == false) {
throw new SerialComException("closeComPort()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
if(mHandleInfo.getDataListener() != null) {
/* Proper clean up requires that, native thread should be destroyed before closing port. */
throw new IllegalStateException("closeComPort() " + SerialComErrorMapper.ERR_CLOSE_WITHOUT_UNREG_DATA);
}
if(mHandleInfo.getEventListener() != null) {
throw new IllegalStateException("closeComPort() " + SerialComErrorMapper.ERR_CLOSE_WITHOUT_UNREG_EVENT);
}
int ret = mNativeInterface.closeComPort(handle);
// native close() returns 0 on success
if(ret != 0) {
throw new SerialComException("closeComPort()", mErrMapper.getMappedError(ret));
}
/* delete info about this port/handle from global arraylist */
mPortHandleInfo.remove(mHandleInfo);
}
return true;
}
/**
* This method writes bytes from the specified byte type buffer. If the method returns false, the application
* should try to re-send bytes. The data has been transmitted out of serial port when this method returns.
*
* If large amount of data need to be written, consider breaking it into chunks of data of size for example
* 2KB each.
*
* Writing empty buffer i.e. zero length array is not allowed.
*
* It should be noted that on Linux system reading from the terminal after a disconnect causes an end-of-file
* condition, and writing causes an EIO error to be returned. The terminal device must be closed and reopened to
* clear the condition.
*
* @param handle handle of the opened port on which to write bytes
* @param buffer byte type buffer containing bytes to be written to port
* @param delay interval to be maintained between writing two consecutive bytes
* @return true on success, false on failure or if empty buffer is passed
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if buffer is null
*/
public boolean writeBytes(long handle, byte[] buffer, int delay) throws SerialComException {
if(buffer == null) {
throw new IllegalArgumentException("writeBytes(), " + SerialComErrorMapper.ERR_WRITE_NULL_DATA_PASSED);
}
if(buffer.length == 0) {
return false;
}
int ret = mNativeInterface.writeBytes(handle, buffer, delay);
if(ret < 0) {
throw new SerialComException("write", mErrMapper.getMappedError(ret));
}
return true;
}
/**
* Utility method to call writeBytes without delay between successive bytes.
* The writeBytes(handle, buffer) method for class SerialComManager has the same effect
* as:
* writeBytes(handle, buffer, 0)
*
* @param handle handle of the opened port on which to write bytes
* @param buffer byte type buffer containing bytes to be written to port
* @return true on success, false on failure or if empty buffer is passed
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if buffer is null
*/
public boolean writeBytes(long handle, byte[] buffer) throws SerialComException {
return writeBytes(handle, buffer, 0);
}
/**
* This method writes a single byte to the specified port. The data has been transmitted out of serial port when
* this method returns.
*
* @param handle handle of the opened port on which to write byte
* @param data byte to be written to port
* @return true on success false otherwise
* @throws SerialComException if an I/O error occurs.
*/
public boolean writeSingleByte(long handle, byte data) throws SerialComException {
return writeBytes(handle, new byte[] { data }, 0);
}
/**
* This method writes a string to the specified port. The library internally converts string to byte buffer.
* The data has been transmitted out of serial port when this method returns.
*
* @param handle handle of the opened port on which to write byte
* @param data the string to be send to port
* @param delay interval between two successive bytes while sending string
* @return true on success false otherwise
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if data is null
*/
public boolean writeString(long handle, String data, int delay) throws SerialComException {
if(data == null) {
throw new IllegalArgumentException("writeString(), " + SerialComErrorMapper.ERR_WRITE_NULL_DATA_PASSED);
}
return writeBytes(handle, data.getBytes(), delay);
}
/**
* This method writes a string to the specified port. The library internally converts string to byte buffer.
* The data has been transmitted out of serial port when this method returns.
*
* @param handle handle of the opened port on which to write byte
* @param data the string to be send to port
* @param charset the character set into which given string will be encoded
* @return true on success false otherwise
* @throws SerialComException if an I/O error occurs.
* @throws IllegalArgumentException if data is null
*/
public boolean writeString(long handle, String data, Charset charset, int delay) throws UnsupportedEncodingException, SerialComException {
if(data == null) {
throw new IllegalArgumentException("writeString(), " + SerialComErrorMapper.ERR_WRITE_NULL_DATA_PASSED);
}
return writeBytes(handle, data.getBytes(charset), delay);
}
/**
* Different CPU and OS will have different endianness. It is therefore we handle the endianness conversion
* as per the requirement. If the given integer is in range −32,768 to 32,767, only two bytes will be needed.
* In such case we might like to send only 2 bytes to serial port. On the other hand application might be implementing
* some custom protocol so that the data must be 4 bytes (irrespective of its range) in order to be interpreted
* correctly by the receiver terminal. This method assumes that integer value can be represented by 32 or less
* number of bits. On x86_64 architecture, loss of precision will occur if the integer value is of more than 32 bit.
*
* The data has been transmitted physically out of serial port when this method returns.
*
* In java numbers are represented in 2's complement, so number 650 whose binary representation is 0000001010001010
* is printed byte by byte, then will be printed as 1 and -118, because 10001010 in 2's complement is negative number.
*
* @param handle handle of the opened port on which to write byte
* @param data an integer number to be sent to port
* @param delay interval between two successive bytes
* @param endianness big or little endian sequence to be followed while sending bytes representing this integer
* @param numOfBytes number of bytes this integer can be represented in
* @return true on success false otherwise
* @throws SerialComException if an I/O error occurs.
*/
public boolean writeSingleInt(long handle, int data, int delay, ENDIAN endianness, NUMOFBYTES numOfBytes) throws SerialComException {
byte[] buffer = null;
if(numOfBytes.getValue() == 2) { // conversion to two bytes data
buffer = new byte[2];
if(endianness.getValue() == 1) { // Little endian
buffer[1] = (byte) (data >>> 8);
buffer[0] = (byte) data;
}else { // big endian/default (java is big endian by default)
buffer[1] = (byte) data;
buffer[0] = (byte) (data >>> 8);
}
return writeBytes(handle, buffer, delay);
}else { // conversion to four bytes data
buffer = new byte[4];
if(endianness.getValue() == 1) { // Little endian
buffer[3] = (byte) (data >>> 24);
buffer[2] = (byte) (data >>> 16);
buffer[1] = (byte) (data >>> 8);
buffer[0] = (byte) data;
}else { // big endian/default (java is big endian by default)
buffer[3] = (byte) data;
buffer[2] = (byte) (data >>> 8);
buffer[1] = (byte) (data >>> 16);
buffer[0] = (byte) (data >>> 24);
}
return writeBytes(handle, buffer, delay);
}
}
/**
* This method send an array of integers on the specified port. The data has been transmitted out of serial
* port when this method returns.
*
* @param handle handle of the opened port on which to write byte
* @param buffer an array of integers to be sent to port
* @param delay interval between two successive bytes
* @param endianness big or little endian sequence to be followed while sending bytes representing this integer
* @param numOfBytes number of bytes this integer can be represented in
* @return true on success false otherwise
* @throws SerialComException if an I/O error occurs.
*/
public boolean writeIntArray(long handle, int[] buffer, int delay, ENDIAN endianness, NUMOFBYTES numOfBytes) throws SerialComException {
byte[] localBuf = null;
if(numOfBytes.getValue() == 2) {
localBuf = new byte[2 * buffer.length];
if(endianness.getValue() == 1) { // little endian
int a = 0;
for(int b=0; b>> 8);
a++;
}
}else { // big/default endian
int c = 0;
for(int d=0; d>> 8);
c++;
localBuf[c] = (byte) buffer[d];
c++;
}
}
return writeBytes(handle, localBuf, delay);
}else {
localBuf = new byte[4 * buffer.length];
if(endianness.getValue() == 1) { // little endian
int e = 0;
for(int f=0; f>> 8);
e++;
localBuf[e] = (byte) (buffer[f] >>> 16);
e++;
localBuf[e] = (byte) (buffer[f] >>> 24);
e++;
}
}else { // big/default endian
int g = 0;
for(int h=0; h>> 8);
g++;
localBuf[g] = (byte) (buffer[h] >>> 16);
g++;
localBuf[g] = (byte) (buffer[h] >>> 24);
g++;
}
}
return writeBytes(handle, localBuf, delay);
}
}
/**
* Read specified number of bytes from given serial port and stay blocked till bytes arrive at serial port.
* 1. If data is read from serial port, array of bytes containing data is returned.
* 2. If there was no data in serial port to read, null is returned.
*
* The number of bytes to read must be greater than or equal to 1 and less than or equal to 2048 (1 <= byteCount <= 2048).
* This method may return less than the requested number of bytes due to reasons like, there is less data in operating system
* buffer (serial port) or operating system returned less data which is also legal.
*
* @param handle of the serial port from which to read bytes
* @param byteCount number of bytes to read from serial port
* @return array of bytes read from port or null
* @throws SerialComException if an I/O error occurs.
*/
public byte[] readBytesBlocking(long handle, int byteCount) throws SerialComException {
byte[] buffer = null;
SerialComReadStatus retStatus = new SerialComReadStatus(1);
int osType = SerialComManager.getOSType();
if(osType == SerialComManager.OS_WINDOWS) {
buffer = mNativeInterface.readBytesBlocking(handle, byteCount, retStatus);
}else {
buffer = mNativeInterface.readBytes(handle, byteCount, retStatus);
}
// data read from serial port, pass to application
if(buffer != null) {
return buffer;
}
// reaching here means JNI layer passed null indicating either no data read or an error
if(retStatus.status < 0) {
throw new SerialComException("readBytesBlocking()", mErrMapper.getMappedError(retStatus.status));
}else if(retStatus.status == 1) {
return null; // not possible for blocking call
}else {
}
return null;
}
/**
* Read specified number of bytes from given serial port.
* 1. If data is read from serial port, array of bytes containing data is returned.
* 2. If there was no data in serial port to read, null is returned.
*
* The number of bytes to read must be greater than or equal to 1 and less than or equal to 2048 (1 <= byteCount <= 2048).
* This method may return less than the requested number of bytes due to reasons like, there is less data in operating system
* buffer (serial port) or operating system returned less data which is also legal.
*
* @param handle of the serial port from which to read bytes
* @param byteCount number of bytes to read from serial port
* @return array of bytes read from port or null
* @throws SerialComException if an I/O error occurs.
*/
public byte[] readBytes(long handle, int byteCount) throws SerialComException {
SerialComReadStatus retStatus = new SerialComReadStatus(1);
byte[] buffer = mNativeInterface.readBytes(handle, byteCount, retStatus);
// data read from serial port, pass to application
if(buffer != null) {
return buffer;
}
// reaching here means JNI layer passed null indicating either no data read or an error
if(retStatus.status == 1) {
return null; // serial port does not have any data
}else if(retStatus.status < 0) {
throw new SerialComException("reading", mErrMapper.getMappedError(retStatus.status));
}else {
}
return null;
}
/**
* If user does not specify any count, library try to read DEFAULT_READBYTECOUNT (1024 bytes) bytes as default value.
*
* It has same effect as readBytes(handle, 1024)
*
* @param handle of the port from which to read bytes
* @return array of bytes read from port or null
* @throws SerialComException if an I/O error occurs.
*/
public byte[] readBytes(long handle) throws SerialComException {
return readBytes(handle, DEFAULT_READBYTECOUNT);
}
/**
* This method reads data from serial port and converts it into string. Caller has more finer control over the byte operation.
*
* It Constructs a new string by decoding the specified array of bytes using the platform's default charset. The length of the new
* string is a function of the charset, and hence may not be equal to the length of the byte array read from serial port.
*
* @param handle of port from which to read bytes
* @param byteCount number of bytes to read from this port
* @return string constructed from data read from serial port or null
* @throws SerialComException if an I/O error occurs.
*/
public String readString(long handle, int byteCount) throws SerialComException {
byte[] buffer = readBytes(handle, byteCount);
if(buffer != null) {
return new String(buffer);
}
return null;
}
/**
* This method reads data from serial port and converts it into string. Caller has more finer control over the byte operation.
*
* It Constructs a new string by decoding the specified array of bytes using the platform's default charset. The length of the new
* string is a function of the charset, and hence may not be equal to the length of the byte array read from serial port.
*
* Note that the length of data bytes read using this method can not be greater than DEFAULT_READBYTECOUNT i.e. 1024.
*
* @param handle of the port from which to read bytes
* @return string constructed from data read from serial port or null
* @throws SerialComException if an I/O error occurs.
*/
public String readString(long handle) throws SerialComException {
return readString(handle, DEFAULT_READBYTECOUNT);
}
/**
* This is a utility method to read a single byte from serial port.
*
* Its effect is same as readBytes(handle, 1)
*
* @param handle of the port from which to read bytes
* @return array of length 1 representing 1 byte data read from serial port or null
* @throws SerialComException if an I/O error occurs.
*/
public byte[] readSingleByte(long handle) throws SerialComException {
return readBytes(handle, 1);
}
/**
* This method configures the rate at which communication will occur and the format of data frame. Note that, most of the DTE/DCE (hardware)
* does not support different baud rates for transmission and reception and therefore we take only single value applicable to both transmission and
* reception. Further, all the hardware and OS does not support all the baud rates (maximum change in signal per second). It is the applications
* responsibility to consider these factors when writing portable software.
*
* If parity is enabled, the parity bit will be removed from frame before passing it library.
*
* Note: (1) some restrictions apply in case of Windows. Please refer http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx
* for details.
*
* (2) Some drivers especially windows driver for usb to serial converters support non-standard baud rates. They either supply a text file that can be used for
* configuration or user may edit windows registry directly to enable this support. The user supplied standard baud rate is translated to custom baud rate as
* specified in vendor specific configuration file.
*
* Take a look at http://www.ftdichip.com/Support/Documents/AppNotes/AN232B-05_BaudRates.pdf to understand using custom baud rates with USB-UART chips.
*
* @param handle of opened port to which this configuration applies to
* @param dataBits number of data bits in one frame (refer DATABITS enum for this)
* @param stopBits number of stop bits in one frame (refer STOPBITS enum for this)
* @param parity of the frame (refer PARITY enum for this)
* @param baudRate of the frame (refer BAUDRATE enum for this)
* @param custBaud custom baudrate if the desired rate is not included in BAUDRATE enum
* @return true on success false otherwise
* @throws SerialComException if invalid handle is passed or an error occurs in configuring the port
*/
public boolean configureComPortData(long handle, DATABITS dataBits, STOPBITS stopBits, PARITY parity, BAUDRATE baudRate, int custBaud) throws SerialComException {
int baudRateTranslated = 0;
int custBaudTranslated = 0;
int baudRateGiven = baudRate.getValue();
boolean handlefound = false;
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
break;
}
}
if(handlefound == false) {
throw new SerialComException("configureComPortData()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
if(baudRateGiven != 251) {
baudRateTranslated = baudRateGiven;
custBaudTranslated = 0;
}else {
// custom baud rate
baudRateTranslated = baudRateGiven;
custBaudTranslated = custBaud;
}
int ret = mNativeInterface.configureComPortData(handle, dataBits.getValue(), stopBits.getValue(), parity.getValue(), baudRateTranslated, custBaudTranslated);
if(ret < 0) {
throw new SerialComException("configureComPortData()", mErrMapper.getMappedError(ret));
}
return true;
}
/**
* This method configures the way data communication will be controlled between DTE and DCE. This specifies flow control and actions that will
* be taken when an error is encountered in communication.
*
* @param handle of opened port to which this configuration applies to
* @param flowctrl flow control, how data flow will be controlled (refer FLOWCONTROL enum for this)
* @param xon character representing on condition if software flow control is used
* @param xoff character representing off condition if software flow control is used
* @param ParFraError true if parity and frame errors are to be checked false otherwise
* @param overFlowErr true if overflow error is to be detected false otherwise
* @return true on success false otherwise
* @throws SerialComException if invalid handle is passed or an error occurs in configuring the port
*/
public boolean configureComPortControl(long handle, FLOWCONTROL flowctrl, char xon, char xoff, boolean ParFraError, boolean overFlowErr) throws SerialComException {
boolean handlefound = false;
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
break;
}
}
if(handlefound == false) {
throw new SerialComException("configureComPortControl()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
int ret = mNativeInterface.configureComPortControl(handle, flowctrl.getValue(), xon, xoff, ParFraError, overFlowErr);
if(ret < 0) {
throw new SerialComException("configureComPortControl()", mErrMapper.getMappedError(ret));
}
return true;
}
/**
* This method gives currently applicable settings associated with particular serial port.
* The values are bit mask so that application can manipulate them to get required information.
*
* For Linux the order is : c_iflag, c_oflag, c_cflag, c_lflag, c_line, c_cc[0], c_cc[1], c_cc[2], c_cc[3]
* c_cc[4], c_cc[5], c_cc[6], c_cc[7], c_cc[8], c_cc[9], c_cc[10], c_cc[11], c_cc[12], c_cc[13], c_cc[14],
* c_cc[15], c_cc[16], c_ispeed and c_ospeed.
*
* For Windows the order is :DCBlength, BaudRate, fBinary, fParity, fOutxCtsFlow, fOutxDsrFlow, fDtrControl,
* fDsrSensitivity, fTXContinueOnXoff, fOutX, fInX, fErrorChar, fNull, fRtsControl, fAbortOnError, fDummy2,
* wReserved, XonLim, XoffLim, ByteSize, Parity, StopBits, XonChar, XoffChar, ErrorChar, StopBits, EvtChar,
* wReserved1.
*
* @param handle of the opened port
* @return array of string giving configuration
* @throws SerialComException if invalid handle is passed or an error occurs while reading current settings
*/
public String[] getCurrentConfiguration(long handle) throws SerialComException {
boolean handlefound = false;
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
break;
}
}
if(handlefound == false) {
throw new SerialComException("getCurrentConfiguration()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
if(getOSType() != OS_WINDOWS) {
// for unix-like os
int[] config = mNativeInterface.getCurrentConfigurationU(handle);
String[] configuration = new String[config.length];
if(config[0] < 0) {
throw new SerialComException("getCurrentConfiguration()", mErrMapper.getMappedError(config[0]));
}
// if an error occurs, config[0] will contain error code, otherwise actual data
for(int x=0; xThis method assert/de-assert RTS line of serial port. Set "true" for asserting signal, false otherwise. This changes the state of RTS line electrically.
*
* The RS-232 standard defines the voltage levels that correspond to logical one and logical zero levels for the data
* transmission and the control signal lines. Valid signals are either in the range of +3 to +15 volts or the range
* −3 to −15 volts with respect to the ground/common pin; consequently, the range between −3 to +3 volts is not a
* valid RS-232 level.
*
* In asserted condition, voltage at pin number 7 (RTS signal) will be greater than 3 volts. Voltage 5.0 volts
* was observed when using USB-UART converter http://www.amazon.in/Bafo-USB-Serial-Converter-DB9/dp/B002SCRCDG.
*
* On some hardware IC, signals may be active low and therefore for actual voltage datasheet should be consulted.
*
* @param handle of the opened port
* @param enabled if true RTS will be asserted and vice-versa
* @return true on success false otherwise
* @throws SerialComException if system is unable to complete requested operation
*/
public boolean setRTS(long handle, boolean enabled) throws SerialComException {
int ret = mNativeInterface.setRTS(handle, enabled);
if(ret < 0) {
throw new SerialComException("setRTS()", mErrMapper.getMappedError(ret));
}
return true;
}
/**
*
This method assert/de-assert DTR line of serial port. Set "true" for asserting signal, false otherwise. This changes the state of RTS line electrically.
*
* @param handle of the opened port
* @param enabled if true DTR will be asserted and vice-versa
* @return true on success false otherwise
* @throws SerialComException if system is unable to complete requested operation
*/
public boolean setDTR(long handle, boolean enabled) throws SerialComException {
int ret = mNativeInterface.setDTR(handle, enabled);
if(ret < 0) {
throw new SerialComException("setDTR()", mErrMapper.getMappedError(ret));
}
return true;
}
/**
* This method associate a data looper with the given listener. This looper will keep delivering new data whenever
* it is made available from native data collection and dispatching subsystem.
* Note that listener will start receiving new data, even before this method returns.
*
* Application (listener) should implement ISerialComDataListener and override onNewSerialDataAvailable method.
*
* The scm library can manage upto 1024 listeners corresponding to 1024 port handles.
* This method is thread safe.
*
* @param handle of the port opened
* @param dataListener instance of class which implements ISerialComDataListener interface
* @return true on success false otherwise
* @throws SerialComException if invalid handle passed, handle is null or data listener already exist for this handle
* @throws IllegalArgumentException if dataListener is null
*/
public boolean registerDataListener(long handle, ISerialComDataListener dataListener) throws SerialComException {
boolean handlefound = false;
SerialComPortHandleInfo mHandleInfo = null;
if(dataListener == null) {
throw new IllegalArgumentException("registerDataListener(), " + SerialComErrorMapper.ERR_NULL_POINTER_FOR_LISTENER);
}
synchronized(lock) {
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
if(mInfo.getDataListener() != null) {
throw new SerialComException("registerDataListener()", SerialComErrorMapper.ERR_DATA_LISTENER_ALREADY_EXIST);
}else {
mHandleInfo = mInfo;
}
break;
}
}
if(handlefound == false) {
throw new SerialComException("registerDataListener()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
return mEventCompletionDispatcher.setUpDataLooper(handle, mHandleInfo, dataListener);
}
}
/**
* This method destroys complete java and native looper subsystem associated with this particular data listener. This has no
* effect on event looper subsystem. This method returns only after native thread has been terminated successfully.
*
* This method is thread safe.
*
* @param dataListener instance of class which implemented ISerialComDataListener interface
* @return true on success false otherwise
* @throws SerialComException if null value is passed in dataListener field
* @throws IllegalArgumentException if dataListener is null
*/
public boolean unregisterDataListener(ISerialComDataListener dataListener) throws SerialComException {
if(dataListener == null) {
throw new IllegalArgumentException("unregisterDataListener(), " + SerialComErrorMapper.ERR_NULL_POINTER_FOR_LISTENER);
}
synchronized(lock) {
if(mEventCompletionDispatcher.destroyDataLooper(dataListener)) {
return true;
}
}
return false;
}
/**
* This method associate a event looper with the given listener. This looper will keep delivering new event whenever
* it is made available from native event collection and dispatching subsystem.
*
* Application (listener) should implement ISerialComEventListener and override onNewSerialEvent method.
*
* By default all four events are dispatched to listener. However, application can mask events through setEventsMask()
* method. In current implementation, native code sends all the events irrespective of mask and we actually filter
* them in java layers, to decide whether this should be sent to application or not (as per the mask set by
* setEventsMask() method).
*
* Before calling this method, make sure that port has been configured for hardware flow control using configureComPortControl
* method.
* This method is thread safe.
*
* @param handle of the port opened
* @param eventListener instance of class which implements ISerialComEventListener interface
* @return true on success false otherwise
* @throws SerialComException if invalid handle passed, handle is null or event listener already exist for this handle
* @throws IllegalArgumentException if eventListener is null
*/
public boolean registerLineEventListener(long handle, ISerialComEventListener eventListener) throws SerialComException {
boolean handlefound = false;
SerialComPortHandleInfo mHandleInfo = null;
if(eventListener == null) {
throw new IllegalArgumentException("registerLineEventListener(), " + SerialComErrorMapper.ERR_NULL_POINTER_FOR_LISTENER);
}
synchronized(lock) {
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
if(mInfo.getEventListener() != null) {
throw new SerialComException("registerLineEventListener()", SerialComErrorMapper.ERR_LISTENER_ALREADY_EXIST);
}else {
mHandleInfo = mInfo;
}
break;
}
}
if(handlefound == false) {
throw new SerialComException("registerLineEventListener()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
return mEventCompletionDispatcher.setUpEventLooper(handle, mHandleInfo, eventListener);
}
}
/**
* This method destroys complete java and native looper subsystem associated with this particular event listener. This has no
* effect on data looper subsystem.
* This method is thread safe.
*
* @param eventListener instance of class which implemented ISerialComEventListener interface
* @return true on success false otherwise
* @throws SerialComException if null value is passed in eventListener field
* @throws IllegalArgumentException if eventListener is null
*/
public boolean unregisterLineEventListener(ISerialComEventListener eventListener) throws SerialComException {
if(eventListener == null) {
throw new IllegalArgumentException("unregisterLineEventListener(), " + SerialComErrorMapper.ERR_NULL_POINTER_FOR_LISTENER);
}
synchronized(lock) {
if(mEventCompletionDispatcher.destroyEventLooper(eventListener)) {
return true;
}
}
return false;
}
/**
* The user don't need data for some time or he may be managing data more efficiently.
*
* @param eventListener instance of class which implemented ISerialComEventListener interface
* @return true on success false otherwise
* @throws SerialComException if null is passed for eventListener field
* @throws IllegalArgumentException if eventListener is null
*/
public boolean pauseListeningEvents(ISerialComEventListener eventListener) throws SerialComException {
if(eventListener == null) {
throw new IllegalArgumentException("pauseListeningEvents(), " + SerialComErrorMapper.ERR_NULL_POINTER_FOR_LISTENER);
}
if(mEventCompletionDispatcher.pauseListeningEvents(eventListener)) {
return true;
}
return false;
}
/**
* The user don't need data for some time or he may be managing data more efficiently.
* Note that the native thread will continue to receive events and data, it will pass this data to
* java layer. User must be careful that new data will exist in queue if received after pausing, but
* it will not get delivered to application.
*
* @param eventListener is an instance of class which implements ISerialComEventListener
* @return true on success false otherwise
* @throws SerialComException if error occurs
* @throws IllegalArgumentException if eventListener is null
*/
public boolean resumeListeningEvents(ISerialComEventListener eventListener) throws SerialComException {
if(eventListener == null) {
throw new IllegalArgumentException("pauseListeningEvents(), " + SerialComErrorMapper.ERR_NULL_POINTER_FOR_LISTENER);
}
if(mEventCompletionDispatcher.resumeListeningEvents(eventListener)) {
return true;
}
return false;
}
/**
* This method gives more fine tune control to application for tuning performance and behavior of read
* operations to leverage OS specific facility for read operation. The read operations can be optimized for
* receiving for example high volume data speedily or low volume data but received in burst mode.
*
* If more than one client has opened the same port, then all the clients will be affected by new settings.
*
* When this method is called application should make sure that previous read or write operation is not in progress.
*
* @param handle of the opened port
* @param vmin c_cc[VMIN] field of termios structure
* @param vtime c_cc[VTIME] field of termios structure
* @param rit ReadIntervalTimeout field of COMMTIMEOUTS structure
* @param rttm ReadTotalTimeoutMultiplier field of COMMTIMEOUTS structure
* @param rttc ReadTotalTimeoutConstant field of COMMTIMEOUTS structure
* @return true on success false otherwise
* @throws SerialComException if wrong handle is passed or operation can not be done successfully
* @throws IllegalArgumentException if invalid combination of arguments is passed
*/
public boolean fineTuneRead(long handle, int vmin, int vtime, int rit, int rttm, int rttc) throws SerialComException {
boolean handlefound = false;
int osType = SerialComManager.getOSType();
if(osType == SerialComManager.OS_WINDOWS) {
if((rit < 0) || (rttm < 0) || (rttc < 0)) {
throw new IllegalArgumentException("fineTuneRead(), " + SerialComErrorMapper.ERR_ARG_CAN_NOT_NEGATIVE);
}
}else {
if((vmin == 0) && (vtime == 0)) {
throw new IllegalArgumentException("fineTuneRead(), " + SerialComErrorMapper.ERR_INVALID_COMBINATION_ARG);
}
if((vmin < 0) || (vtime < 0)) {
throw new IllegalArgumentException("fineTuneRead(), " + SerialComErrorMapper.ERR_ARG_CAN_NOT_NEGATIVE);
}
}
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
break;
}
}
if(handlefound == false) {
throw new SerialComException("fineTuneRead()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
int ret = mNativeInterface.fineTuneRead(handle, vmin, vtime, rit, rttm, rttc);
if(ret < 0) {
throw new SerialComException("fineTuneRead()", mErrMapper.getMappedError(ret));
}
return true;
}
/**
* Defines for which line events registered event listener will be called.
*
* In future we may shift modifying mask in the native code itself, so as to prevent JNI transitions.
* This filters what events should be sent to application. Note that, although we sent only those event
* for which user has set mask, however native code send all the events to java layer as of now.
*
* @param eventListener instance of class which implemented ISerialComEventListener interface
* @return true on success false otherwise
* @throws SerialComException if null is passed for listener field or invalid listener is passed
* @throws IllegalArgumentException if eventListener is null
*/
public boolean setEventsMask(ISerialComEventListener eventListener, int newMask) throws SerialComException {
SerialComLooper looper = null;
ISerialComEventListener mEventListener = null;
if(eventListener == null) {
throw new IllegalArgumentException("setEventsMask(), " + SerialComErrorMapper.ERR_NULL_POINTER_FOR_LISTENER);
}
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsEventListener(eventListener)) {
looper = mInfo.getLooper();
mEventListener = mInfo.getEventListener();
break;
}
}
if(looper != null && mEventListener != null) {
looper.setEventsMask(newMask);
return true;
}else {
throw new SerialComException("setEventsMask()", SerialComErrorMapper.ERR_WRONG_LISTENER_PASSED);
}
}
/**
* This method return currently applicable mask for events on serial port.
*
* @param eventListener instance of class which implemented ISerialComEventListener interface
* @return an integer containing bit fields representing mask
* @throws SerialComException if null or wrong listener is passed
* @throws IllegalArgumentException if eventListener is null
*/
public int getEventsMask(ISerialComEventListener eventListener) throws SerialComException {
SerialComLooper looper = null;
ISerialComEventListener mEventListener = null;
if(eventListener == null) {
throw new IllegalArgumentException("getEventsMask(), " + SerialComErrorMapper.ERR_NULL_POINTER_FOR_LISTENER);
}
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsEventListener(eventListener)) {
looper = mInfo.getLooper();
mEventListener = mInfo.getEventListener();
break;
}
}
if(looper != null && mEventListener != null) {
return looper.getEventsMask();
}else {
throw new SerialComException("setEventsMask()", SerialComErrorMapper.ERR_WRONG_LISTENER_PASSED);
}
}
/**
* Discards data sent to port but not transmitted, or data received but not read. Some device/OS/driver might
* not have support for this, but most of them may have.
* If there is some data to be pending for transmission, it will be discarded and therefore no longer sent.
* If the application wants to make sure that all data has been transmitted before discarding anything, it must
* first flush data and then call this method.
*
* @param handle of the opened port
* @param clearRxPort if true receive buffer will be cleared otherwise will be left untouched
* @param clearTxPort if true transmit buffer will be cleared otherwise will be left untouched
* @return true on success false otherwise
* @throws SerialComException if invalid handle is passed or operation can not be completed successfully
*/
public synchronized boolean clearPortIOBuffers(long handle, boolean clearRxPort, boolean clearTxPort) throws SerialComException {
boolean handlefound = false;
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
break;
}
}
if(handlefound == false) {
throw new SerialComException("clearPortIOBuffers()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
if(clearRxPort == true || clearTxPort == true) {
int ret = mNativeInterface.clearPortIOBuffers(handle, clearRxPort, clearTxPort);
if(ret < 0) {
throw new SerialComException("clearPortIOBuffers()", mErrMapper.getMappedError(ret));
}
return true;
}
return false;
}
/**
* Assert a break condition on the specified port for the duration expressed in milliseconds.
* If the line is held in the logic low condition (space in UART jargon) for longer than a character
* time, this is a break condition that can be detected by the UART.
*
* A "break condition" occurs when the receiver input is at the "space" level for longer than some duration
* of time, typically, for more than a character time. This is not necessarily an error, but appears to the
* receiver as a character of all zero bits with a framing error. The term "break" derives from current loop
* Signaling, which was the traditional signaling used for tele-typewriters. The "spacing" condition of a
* current loop line is indicated by no current flowing, and a very long period of no current flowing is often
* caused by a break or other fault in the line.
*
* @param handle of the opened port
* @param duration the time in milliseconds for which break will be active
* @return true on success
* @throws SerialComException if invalid handle is passed or operation can not be successfully completed
*/
public synchronized boolean sendBreak(long handle, int duration) throws SerialComException {
boolean handlefound = false;
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
break;
}
}
if(handlefound == false) {
throw new SerialComException("sendBreak()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
int ret = mNativeInterface.sendBreak(handle, duration);
if(ret < 0) {
throw new SerialComException("sendBreak()", mErrMapper.getMappedError(ret));
}
return true;
}
/**
* This method gives the number of serial line interrupts that have occurred. The interrupt count is in following
* order in array beginning from index 0 and ending with 11th index :
* CTS, DSR, RING, CARRIER DETECT, RECEIVER BUFFER, TRANSMIT BUFFER, FRAME ERROR, OVERRUN ERROR, PARITY ERROR,
* BREAK AND BUFFER OVERRUN.
*
* Note: It is supported on Linux OS only. For other operating systems, this will return 0 for all the indexes.
*
* @param handle of the port opened on which interrupts might have occurred
* @return array of integers containing values corresponding to each interrupt source
* @throws SerialComException if invalid handle is passed or operation can not be completed
*/
public int[] getInterruptCount(long handle) throws SerialComException {
int x = 0;
boolean handlefound = false;
int[] ret = null;
int[] interruptsCount = new int[11];
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
break;
}
}
if(handlefound == false) {
throw new SerialComException("getInterruptCount()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
ret = mNativeInterface.getInterruptCount(handle);
if(ret[0] < 0) {
throw new SerialComException("getInterruptCount()", mErrMapper.getMappedError(ret[0]));
}
for(x=0; x<11; x++) {
interruptsCount[x] = ret[x];
}
return interruptsCount;
}
/**
* Gives status of serial port's control lines as supported by underlying operating system.
*
* The sequence of status in returned array is :
* Linux : CTS, DSR, DCD, RI, LOOP, RTS, DTR respectively.
* MAC OS X : CTS, DSR, DCD, RI, 0, RTS, DTR respectively.
* Windows : CTS, DSR, DCD, RI, 0, 0, 0 respectively.
*
* @param handle of the port opened
* @return status of control lines
* @throws SerialComException if invalid handle is passed or operation can not be completed successfully
*/
public int[] getLinesStatus(long handle) throws SerialComException {
int x = 0;
boolean handlefound = false;
int[] ret = null;
int[] status = {0,0,0,0,0,0,0};
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
break;
}
}
if(handlefound == false) {
throw new SerialComException("getLinesStatus()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
ret = mNativeInterface.getLinesStatus(handle);
if(ret[0] < 0) {
throw new SerialComException("getLinesStatus()", mErrMapper.getMappedError(ret[0]));
}
for(x=0; x<7; x++) {
status[x] = ret[x+1];
}
return status;
}
/**
* Get number of bytes in input and output port buffers used by operating system for instance tty buffers
* in Unix like systems. Sequence of data in array is : Input count, Output count.
*
* It should be noted that some chipset specially USB to UART converters might have FIFO buffers in chipset
* itself. For example FT232R has internal buffers controlled by FIFO CONTROLLERS. For this reason this method
* should be tested carefully if application is using USB-UART converters. This is driver and OS specific scenario.
*
* @param handle of the opened port
* @return array containing number of bytes in input and output buffer
* @throws SerialComException if invalid handle is passed or operation can not be completed successfully
*/
public int[] getByteCountInPortIOBuffer(long handle) throws SerialComException {
boolean handlefound = false;
int[] ret = null;
int[] numBytesInfo = {0,0};
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
break;
}
}
if(handlefound == false) {
throw new SerialComException("getByteCountInPortIOBuffer()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
// sequence returned : ret[0]=error info, ret[1]=byte count in input buffer, ret[2]=byte count in output buffer
ret = mNativeInterface.getByteCount(handle);
if(ret[0] < 0) {
throw new SerialComException("getByteCountInPortIOBuffer()", mErrMapper.getMappedError(ret[0]));
}
numBytesInfo[0] = ret[1]; // Input buffer count
numBytesInfo[1] = ret[2]; // Output buffer count
return numBytesInfo;
}
/**
* This registers a listener who will be invoked whenever a port has been plugged or un-plugged in system.
* Initially, the port has to be present into system, as that is only when we will be able to open port.
*
* Application must implement ISerialComPortMonitor interface and override onPortMonitorEvent method. An event value
* 1 represents addition of device while event value 2 represents removal (unplugging) of device from system.
*
* @param handle which will be monitored
* @return true on success false otherwise
* @throws SerialComException - if invalid handle is passed or registration fails due to some reason
* @throws IllegalArgumentException if portMonitor is null
*/
public boolean registerPortMonitorListener(long handle, ISerialComPortMonitor portMonitor) throws SerialComException {
boolean handlefound = false;
String portName = null;
int ret = 0;
if(portMonitor == null) {
throw new IllegalArgumentException("registerPortMonitorListener(), " + SerialComErrorMapper.ERR_NULL_POINTER_FOR_MONITOR);
}
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
portName = mInfo.getOpenedPortName();
handlefound = true;
break;
}
}
if(handlefound == false) {
throw new SerialComException("registerPortMonitorListener()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
ret = mNativeInterface.registerPortMonitorListener(handle, portName, portMonitor);
if(ret < 0) {
throw new SerialComException("registerPortMonitorListener()", mErrMapper.getMappedError(ret));
}
return true;
}
/**
* This unregisters listener and terminate native thread used for monitoring hot plugging of port.
*
* @param handle for which listener will be unregistered
* @return true on success false otherwise
* @throws SerialComException if invalid handle is passed or un-registration fails due to some reason
*/
public boolean unregisterPortMonitorListener(long handle) throws SerialComException {
boolean handlefound = false;
int ret = 0;
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
break;
}
}
if(handlefound == false) {
throw new SerialComException("unregisterPortMonitorListener()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
ret = mNativeInterface.unregisterPortMonitorListener(handle);
if(ret < 0) {
throw new SerialComException("unregisterPortMonitorListener()", mErrMapper.getMappedError(ret));
}
return true;
}
/**
* Enable printing debugging messages and stack trace for development and debugging purpose.
*
* @param enable if true debugging messages will be printed on console otherwise not
*/
public void enableDebugging(boolean enable) {
mNativeInterface.debug(enable);
SerialComManager.DEBUG = enable;
}
/**
* This method gives the port name with which given handle is associated. If the given handle is
* unknown to scm, null is returned. The port is known to scm if it was opened using scm.
*
* @param handle for which the port name is to be found
* @return port name if port found or null if not found
*/
public String getPortName(long handle) {
String portName = null;
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
portName = mInfo.getOpenedPortName();
break;
}
}
if(portName == null) {
return null;
}
return portName;
}
/**
* TODO
*
* Application should carefully examine that before calling this method input and output buffer does not have any pending
* data. As scm flushes data out of serial port upon every write operation, so output buffer will not have any pending data.
* However input data is completely dependent how application read data.
*
* @param handle of the port on which file is to be sent
* @param fileToSend File instance representing file to be sent
* @param fTxProto file transfer protocol to use for communication over serial port
* @return true on success false otherwise
* @throws SerialComException if invalid handle is passed
* @throws SecurityException If a security manager exists and its SecurityManager.checkRead(java.lang.String) method denies read access to the file
* @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some other reason cannot be opened for reading.
* @throws SerialComTimeOutException if timeout occurs as per file transfer protocol
* @throws IOException - if error occurs while reading data from file to be sent
* @throws IllegalArgumentException if fileToSend is null
*/
public boolean sendFile(long handle, java.io.File fileToSend, FILETXPROTO fTxProto) throws SerialComException,
SecurityException, FileNotFoundException, SerialComTimeOutException, IOException {
int protocol = 0;
boolean handlefound = false;
boolean result = false;
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
break;
}
}
if(handlefound == false) {
throw new SerialComException("sendFile()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
if(fileToSend == null) {
throw new IllegalArgumentException("sendFile()" + SerialComErrorMapper.ERR_NULL_POINTER_FOR_FILE_SEND);
}
protocol = fTxProto.getValue();
if(protocol == 1){
SerialComXModem xmodem = new SerialComXModem(this, handle, fileToSend);
result = xmodem.sendFileX();
}
return result;
}
/**
* TODO
*
* Application should carefully examine that before calling this method input and output buffer does not have any pending
* data. As scm flushes data out of serial port upon every write operation, so output buffer will not have any pending data.
* However input data is completely dependent how application read data.
*
* @param handle of the port on which file is to be sent
* @param fileToReceive File instance representing file to be sent
* @param fTxProto file transfer protocol to use for communication over serial port
* @return true on success false otherwise
* @throws SerialComException if invalid handle is passed
* @throws SecurityException If a security manager exists and its SecurityManager.checkRead(java.lang.String) method denies read access to the file
* @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some other reason cannot be opened for reading.
* @throws SerialComTimeOutException if timeout occurs as per file transfer protocol
* @throws IOException if error occurs while reading data from file to be sent
* @throws IllegalArgumentException if fileToReceive is null
*/
public boolean receiveFile(long handle, java.io.File fileToReceive, FILETXPROTO fTxProto) throws SerialComException,
SecurityException, FileNotFoundException, SerialComTimeOutException, IOException {
int protocol = 0;
boolean handlefound = false;
boolean result = false;
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
break;
}
}
if(handlefound == false) {
throw new SerialComException("receiveFile()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
if(fileToReceive == null) {
throw new IllegalArgumentException("receiveFile()" + SerialComErrorMapper.ERR_NULL_POINTER_FOR_FILE_RECEIVE);
}
protocol = fTxProto.getValue();
if(protocol == 1){
SerialComXModem xmodem = new SerialComXModem(this, handle, fileToReceive);
result = xmodem.receiveFileX();
}
return result;
}
/**
* This method writes bytes from the specified byte type buffer. If the method returns false, the application
* should try to re-send bytes. The data has been transmitted out of serial port when this method returns.
*
* This method may be used for Internet of things applications, large data transfer, implementing userspace drivers,
* middleware frameworks, quick prototyping of Wifi/BT modules connected to UART port.
*
* @param handle handle of the opened port on which to write bytes
* @param buffer byte type buffer containing bytes to be written to port
* @return true on success, false on failure or if empty buffer is passed
* @throws SerialComException - if an I/O error occurs.
* @throws IllegalArgumentException if buffer is null
*/
public boolean writeBytesBulk(long handle, ByteBuffer buffer) throws SerialComException {
if(buffer == null) {
throw new IllegalArgumentException("writeBytesBulk(), " + SerialComErrorMapper.ERR_WRITE_NULL_DATA_PASSED);
}
int ret = mNativeInterface.writeBytesBulk(handle, buffer);
if(ret < 0) {
throw new SerialComException("writeBytesBulk()", mErrMapper.getMappedError(ret));
}
return true;
}
/**
* Prepares context and returns an input streams of bytes for receiving data bytes from the
* serial port.
*
* A handle can have only one input stream. After it has been used it need to be closed.
*
* @param handle handle of the opened port from which to read data bytes
* @return reference to an object of type SerialComInByteStream
* @throws SerialComException if input stream already exist for this handle or invalid handle is passed
*/
public SerialComInByteStream createInputByteStream(long handle) throws SerialComException {
boolean handlefound = false;
SerialComInByteStream scis = null;
SerialComPortHandleInfo mHandleInfo = null;
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
scis = mInfo.getSerialComInByteStream();
mHandleInfo = mInfo;
break;
}
}
if(handlefound == false) {
throw new SerialComException("createInputByteStream()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
if(scis == null) {
scis = new SerialComInByteStream(this, handle);
mHandleInfo.setSerialComInByteStream(scis);
}else {
// if 2nd attempt is made to create already existing input stream, throw exception
throw new SerialComException("createInputByteStream()", SerialComErrorMapper.ERR_IN_STREAM_ALREADY_EXIST);
}
return scis;
}
/**
* Prepares context and returns an output streams of bytes for transferring data bytes out of
* serial port.
*
* A handle can have only one output stream. After it has been used it need to be closed.
*
* Using SerialComOutByteStream for writing data while not using SerialComInByteStream for
* reading is a valid use case.
*
* @param handle handle of the opened port on which to write data bytes
* @return reference to an object of type SerialComOutByteStream
* @throws SerialComException if output stream already exist for this handle or invalid handle is passed
*/
public SerialComOutByteStream createOutputByteStream(long handle) throws SerialComException {
boolean handlefound = false;
SerialComOutByteStream scos = null;
SerialComPortHandleInfo mHandleInfo = null;
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.containsHandle(handle)) {
handlefound = true;
scos = mInfo.getSerialComOutByteStream();
mHandleInfo = mInfo;
break;
}
}
if(handlefound == false) {
throw new SerialComException("createOutputByteStream()", SerialComErrorMapper.ERR_WRONG_HANDLE);
}
if(scos == null) {
scos = new SerialComOutByteStream(this, handle);
mHandleInfo.setSerialComOutByteStream(scos);
}else {
// if 2nd attempt is made to create already existing output stream, throw exception
throw new SerialComException("createOutputByteStream()", SerialComErrorMapper.ERR_OUT_STREAM_ALREADY_EXIST);
}
return scos;
}
/** Internal use */
public void destroyInputByteStream(SerialComInByteStream scis) {
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.getSerialComInByteStream() == scis) {
mInfo.setSerialComInByteStream(null);
break;
}
}
}
/** Internal use */
public void destroyOutputByteStream(SerialComOutByteStream scos) {
for(SerialComPortHandleInfo mInfo: mPortHandleInfo){
if(mInfo.getSerialComOutByteStream() == scos) {
mInfo.setSerialComOutByteStream(null);
break;
}
}
}
/**
* This method creates hex string from byte array. This is useful in bluetooth low energy applications where characteristics
* returned are to be interpreted or for example Internet of things applications where sensor data is getting exchanged.
*
* @param data byte array to be converted into string
* @param separator to be inserted after each hex value
* @return constructed hex string if data.length > 0 otherwise empty string
* @throws IllegalArgumentException if data is null
*/
public String byteArrayToHexStr(byte[] data, String separator) {
if(data == null) {
throw new IllegalArgumentException("byteArrayToHexStr(), " + SerialComErrorMapper.ERR_CAN_NOT_BE_NULL);
}
if(data.length > 0) {
if(separator != null) {
final StringBuilder sBuilder = new StringBuilder(2 * data.length);
for (final byte b : data) {
sBuilder.append(HEXNUM.charAt((b & 0xF0) >> 4)).append(HEXNUM.charAt((b & 0x0F)));
sBuilder.append(separator);
}
return sBuilder.toString();
}else {
final StringBuilder sBuilder = new StringBuilder(2 * data.length);
for (final byte b : data) {
sBuilder.append(HEXNUM.charAt((b & 0xF0) >> 4)).append(HEXNUM.charAt((b & 0x0F)));
}
return sBuilder.toString();
}
}
return new String();
}
}