package org.myrobotlab.serial; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.Logging; import org.slf4j.Logger; import jssc.SerialPort; import jssc.SerialPortEvent; import jssc.SerialPortEventListener; import jssc.SerialPortException; import jssc.SerialPortList; /** * @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 PortJSSC extends Port implements SerialControl, SerialPortEventListener, Serializable { private static final long serialVersionUID = 1L; public final static Logger log = LoggerFactory.getLogger(PortJSSC.class); // private gnu.io.RXTXPort port; // private CommPortIdentifier commPortId; transient SerialPort port = null; public boolean debug = true; public boolean debugTX = true; public boolean debugRX = false; public PortJSSC() { super(); } public PortJSSC(String portName, int rate, int dataBits, int stopBits, int parity) throws IOException { super(portName, rate, dataBits, stopBits, parity); // commPortId = CommPortIdentifier.getPortIdentifier(portName); } /* * public int available() throws IOException { port. return in.available(); } */ public boolean isOpen() { if (port != null) { return port.isOpened(); } return false; } public int getBaudRate() { return rate; } public int getDataBits() { return dataBits; } @Override public String getName() { return portName; } public int getParity() { return parity; } @Override public List<String> getPortNames() { ArrayList<String> ret = new ArrayList<String>(); try { String[] portNames = SerialPortList.getPortNames(); for (int i = 0; i < portNames.length; i++) { ret.add(portNames[i]); System.out.println(portNames[i]); } } catch (Exception e) { Logging.logError(e); } return ret; } public int getStopBits() { return stopBits; } public boolean isCTS() throws SerialPortException { return port.isCTS(); } public boolean isDSR() throws SerialPortException { return port.isDSR(); } @Override public void open() throws IOException { try { port = new SerialPort(portName); port.openPort(); port.setParams(rate, dataBits, stopBits, parity); } catch (Exception e) { throw new IOException(String.format("could not open port %s rate %d dataBits %d stopBits %d parity %d", portName, rate, dataBits, stopBits, parity), e); } } public void close() { try { listening = false; readingThread = null;// is dead anyway port.closePort(); // FIXME - JSSC issue (IMHO) // if a listener doesn't exist it throws ? meh :P // port.removeEventListener(); // port.notifyOnDataAvailable(false); } catch (Exception e) { Logging.logError(e); } port = null; } // / FIXME KLUDGY !!!!! @Override public int read() throws Exception { int data = port.readIntArray(1)[0]; if (debug && debugRX) { log.info("Read : {}", data ); } return data; /* * if (port == null) { return -1; } int[] ret = port.readIntArray(1); if * (ret != null) { return ret[0]; } else { return -1; } */ } @Override public void setDTR(boolean state) { try { port.setDTR(state); } catch (Exception e) { Logging.logError(e); } } @Override public boolean setParams(int rate, int dataBits, int stopBits, int parity) throws Exception { log.debug(String.format("setSerialPortParams %d %d %d %d", rate, dataBits, stopBits, parity)); try { if (port == null || !port.isOpened()) { log.error("port not opened or is null"); return false; } return port.setParams(rate, dataBits, stopBits, parity); } catch (Exception e) { throw new IOException(e); } } public void setRTS(boolean state) { try { port.setRTS(state); } catch (Exception e) { Logging.logError(e); } } @Override public void write(int data) throws Exception { port.writeInt(data); } // FIXME - check to make sure these are the same as InputStream & // OutputStream public void write(int[] data) throws Exception { // use the writeIntArray method to batch this operation. if (debug && debugTX) { StringBuilder b = new StringBuilder(); for (int i = 0; i < data.length; i++) { b.append("" + Integer.toString(data[i]) + ""); if (i != data.length-1) b.append(","); } log.info("Sending Int Array: {}", b); // a log file of all the mrl comm messages sent. (one per line.) // FileOutputStream fos = new FileOutputStream(new File("c:\\Python27\\playback.run.log"),true); // b.append("\n"); // fos.write(b.toString().getBytes()); // fos.close(); } port.writeIntArray(data); } @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); } } }