package org.myrobotlab.serial;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.logging.Logging;
import org.slf4j.Logger;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.RXTXPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;
/**
* @author GroG
*
* A necessary class to wrap references to rxtxLib in something which
* can be dynamically loaded. Without this abstraction any platform
* which did was not supported for by rxtx would not be able to use the
* Serial service or ports.
*
*/
public class PortRXTX extends Port implements SerialControl, SerialPortEventListener {
public final static Logger log = LoggerFactory.getLogger(PortRXTX.class);
transient private gnu.io.RXTXPort port;
transient private CommPortIdentifier commPortId;
transient private InputStream in;
transient private OutputStream out;
public PortRXTX() {
super();
}
public PortRXTX(String portName, int rate, int databits, int stopbits, int parity)
throws IOException, PortInUseException, UnsupportedCommOperationException, NoSuchPortException {
super(portName, rate, databits, stopbits, parity);
commPortId = CommPortIdentifier.getPortIdentifier(portName);
}
public int available() throws IOException {
return in.available();
}
public int getBaudRate() {
return port.getBaudRate();
}
public String getCurrentOwner() {
if (commPortId != null)
return commPortId.getCurrentOwner();
return null;
}
public int getDataBits() {
return port.getDataBits();
}
public InputStream getInputStream() {
return port.getInputStream();
}
@Override
public String getName() {
return commPortId.getName();
}
public OutputStream getOutputStream() {
return port.getOutputStream();
}
public int getParity() {
return port.getParity();
}
@Override
public List<String> getPortNames() {
ArrayList<String> ret = new ArrayList<String>();
try {
CommPortIdentifier portId;
Enumeration<?> portList = CommPortIdentifier.getPortIdentifiers();
while (portList.hasMoreElements()) {
portId = (CommPortIdentifier) portList.nextElement();
String inPortName = portId.getName();
log.info(inPortName);
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
ret.add(portId.getName());
}
}
} catch (Exception e) {
Logging.logError(e);
}
return ret;
}
public int getPortType() {
return commPortId.getPortType();
}
public int getStopBits() {
return port.getStopBits();
}
public boolean isCD() {
return port.isCD();
}
public boolean isCTS() {
return port.isCTS();
}
public boolean isCurrentlyOwned() {
return commPortId.isCurrentlyOwned();
}
public boolean isDSR() {
return port.isDSR();
}
public boolean isDTR() {
return port.isDTR();
}
public boolean isRI() {
return port.isRI();
}
public boolean isRTS() {
return port.isRTS();
}
@Override
public void open() throws IOException {
try {
if (port != null) {
log.info(String.format("port %s already open", portName));
return;
}
log.info(String.format("opening %s", portName));
port = (RXTXPort) commPortId.open(portName, 1000);
port.setSerialPortParams(rate, dataBits, stopBits, parity);
in = port.getInputStream();
out = port.getOutputStream();
setParams(rate, dataBits, stopBits, parity);
port.addEventListener(this);
port.notifyOnDataAvailable(true);
listening = true;
isOpen = true;
log.info(String.format("opened %s", portName));
} catch (Exception e) {
throw new IOException(e);
}
}
public void close() {
port.removeEventListener();
// port.notifyOnDataAvailable(false);
listening = false;
readingThread = null;// is dead anyway
port.close();
out = null;
in = null;
/*
* seen strategy of closing rxtxLib ports in a different thread to keep from
* infinite blocking .. but new Thread(){ public void run(){ log.info(
* "closing streams begin");
*
* try { port.close(); } catch(Exception e){ Logging.logError(e); }
*
* try { out.flush(); out.close(); } catch(Exception e){
* Logging.logError(e); }
*
* try { in.close(); } catch(Exception e){ Logging.logError(e); }
*
*
* log.info("closing streams end"); } }.start();
*/
port = null;
}
@Override
public int read() throws IOException {
return in.read();
}
// WORTHLESS INPUTSTREAM FUNCTION !! -- because if the size of the buffer
// is ever bigger than the read and no end of stream has occurred
// it will block forever :P
public int read(byte[] data) throws IOException {
return in.read(data);
}
@Override
public void setDTR(boolean state) {
port.setDTR(state);
}
@Override
public boolean setParams(int rate, int databits, int stopbits, int parity) throws IOException {
log.debug(String.format("setSerialPortParams %d %d %d %d", rate, databits, stopbits, parity));
try {
port.setSerialPortParams(rate, databits, stopbits, parity);
return true;
} catch (UnsupportedCommOperationException e) {
new IOException(e);
}
return false;
}
public void setRTS(boolean state) {
port.setRTS(state);
}
@Override
public void write(int data) throws IOException {
out.write(data);
}
// FIXME - check to make sure these are the same as InputStream &
// OutputStream
public void write(int[] data) throws IOException {
for (int i = 0; i < data.length; ++i) {
out.write(data[i]);
}
}
@Override
public boolean isHardware() {
return true;
}
@Override
public void run() {
// we don't use countDown - because rxtx manages its own threads(sortof :P)
log.info("no port thread in rxtxlib");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
}
// allow the .listen() in Port
// to proceed
// opened.countDown();
}
/**
* rxtxlib's "serial event handling" - would be more simple if they just
* implemented InputStream correctly :P
*/
@Override
public void serialEvent(SerialPortEvent event) {
log.info(String.format("rxtx event on port %s", portName));
Integer newByte = -1;
try {
while (listening && ((newByte = read()) > -1)) {
// listener.onByte(newByte); // <-- FIXME ?? onMsg() < ???
for (String key : listeners.keySet()) {
listeners.get(key).onByte(newByte);
}
++stats.total;
if (stats.total % stats.interval == 0) {
stats.ts = System.currentTimeMillis();
log.error(String.format("===stats - dequeued total %d - %d bytes in %d ms %d Kbps", stats.total, stats.interval, stats.ts - stats.lastTS,
8 * stats.interval / (stats.ts - stats.lastTS)));
// publishQueueStats(stats);
stats.lastTS = stats.ts;
}
// log.info(String.format("%d",newByte));
// rxtx leave whenever it has no new data to delver with a -1
// which is not what an Java InputStream is supposed to do..
}
log.info(String.format("%d", newByte));
} catch (Exception e) {
++rxErrors;
Logging.logError(e);
}
}
}