package lejos.nxt.rcxcomm; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import lejos.util.Delay; /* * WARNING: THIS CLASS IS SHARED BETWEEN THE classes AND pccomms PROJECTS. * DO NOT EDIT THE VERSION IN pccomms AS IT WILL BE OVERWRITTEN WHEN THE PROJECT IS BUILT. */ /** * RCXAbstractPort provides an interface similar to java.net.Socket * Adapted from original code created by the LEGO3 Team at DTU-IAU * RCXAbstractPort implements input and output stream handling and input * buffering. It uses a packet handler for sending and receivng packets. * This version is abstract because it has no packet handler defined. * Specific versions of RCXAbstractPort override the constructor and * set up the packet handler to use a specific protocol stack. * * @author Brian Bagnall * @author Lawrie Griffiths * */ public abstract class RCXAbstractPort { private boolean portOpen = true; private Listener listener; private int timeOut = 0; private RCXInputStream rcxin; private RCXOutputStream rcxout; protected PacketHandler packetHandler; /** * Constructor for the RCXAbstractPort. * Opens the port, and sets the protocol packet handler. * @param handler the packet handler */ public RCXAbstractPort(PacketHandler handler) throws IOException { packetHandler = handler; rcxin = new RCXInputStream(this); rcxout = new RCXOutputStream(packetHandler); listener = new Listener(); listener.setDaemon(true); listener.start(); } /** Returns an input stream for this RCXPort. * @return an input stream for reading bytes from this RCXPort. */ public InputStream getInputStream() { return (InputStream) rcxin; } /** Returns an output stream for this RCXPort. * @return an output stream for writing bytes to this RCXPort. */ public OutputStream getOutputStream() { return (OutputStream) rcxout; } /** * Resets sequence numbers for this port */ public void reset() { packetHandler.reset(); } /** Closes this RCXPort, stopping the Listener thread. */ public void close() { portOpen = false; } /** Getter for property timeOut. * @return Value of property timeOut. */ public int getTimeOut() { return timeOut; } /** Setter for property timeOut. * @param timeOut New value of property timeOut. */ public void setTimeOut(int timeOut) { this.timeOut = timeOut; } private byte [] inPacket = new byte[2]; /** Listener class runs a thread that reads and buffers bytes. * Allows a maximum of two bytes in a packet. */ private class Listener extends Thread { public void run() { while (portOpen) { if (packetHandler.isPacketAvailable()) { int r = packetHandler.receivePacket(inPacket); for(int i=0;i<r;i++) rcxin.add(inPacket[i]); } Delay.msDelay(10); } } } /** * Hidden inner class extending InputStream. */ private class RCXInputStream extends InputStream { /** The default buffer size for the InputStream */ public static final int bufferSize = 32; private byte[] buffer = new byte[bufferSize]; private int current = 0, last = 0; private RCXAbstractPort dataPort; private IOException ioe = new IOException(); /** Creates new RCXInputStream * @param port The RCXAbstractPort which should deliver data for to this InputStream */ public RCXInputStream(RCXAbstractPort port) { dataPort = port; } /** Checks if there is any data avaliable on the InputStream * @throws IOException is never thrown * @return The number of bytes avaliable on the InputStream */ public int available() throws IOException { if (last < current) return bufferSize-(current-last); else return last-current; } /** Read a single byte from the InputStream. Returns value as * an int value between 0 and 255. * @throws IOException is thrown when the read is timed out * @return A data byte from the stream */ public synchronized int read() throws IOException { int time1 = (int)System.currentTimeMillis(); int timeOut = dataPort.getTimeOut(); while (available() == 0) { if (timeOut != 0 && ((int)System.currentTimeMillis()-time1 > timeOut)) { throw ioe; } Delay.msDelay(10); } synchronized (buffer) { int b = buffer[current++]; if (current == bufferSize) current = 0; if(b < 0) b = b + 256; return b; } } /** Add a data byte to the stream * This method should only be called by the RCXPort that * created the RCXInputStream * @param b The data byte */ void add(byte b) { synchronized (buffer) { buffer[last++] = b; if (last == bufferSize) last = 0; } } } /** Hidden inner class extending OutputStream. */ private class RCXOutputStream extends OutputStream { private PacketHandler packetHandler; private IOException ioe = new IOException(); /** Creates new RCXOutputStream * @param handler the packet handler used to send data */ public RCXOutputStream(PacketHandler handler) { packetHandler = handler; } private byte [] bytePacket = new byte[1]; /** Write a byte to the OutputStream. * @param b The byte. * @throws IOException if the byte could not be written to the stream */ public synchronized void write(int b) throws IOException { bytePacket[0] = (byte) b; if (!packetHandler.sendPacket(bytePacket,1)) throw ioe; } } }