001/** 002 * 003 */ 004package org.openimaj.hardware.serial; 005 006import java.io.IOException; 007import java.io.InputStream; 008import java.io.OutputStream; 009import java.util.ArrayList; 010import java.util.HashSet; 011import java.util.List; 012 013import jssc.SerialPort; 014import jssc.SerialPortException; 015import jssc.SerialPortList; 016import jssc.SerialPortTimeoutException; 017 018/** 019 * Serial device driver. Uses RXTX library underneath. The native parts of the 020 * RXTX library are published to the Maven repository as a JAR and are extracted 021 * and the java.library.path property is flushed and reset. 022 * 023 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 024 * 025 * @created 12 Jul 2011 026 */ 027public class SerialDevice implements SerialDataListener 028{ 029 /** The RXTX serial port we'll be reading from */ 030 private SerialPort serialPort = null; 031 032 /** Listeners for data events coming from the serial port */ 033 private List<SerialDataListener> listeners = new ArrayList<SerialDataListener>(); 034 035 /** The regular expression used to split incoming data for the listeners */ 036 private String regex = "\n"; 037 038 /** The input stream for the port */ 039 private InputStream inputStream = null; 040 041 /** The output stream for the port */ 042 private OutputStream outputStream = null; 043 044 /** The serial reader used to buffer and parse incoming data */ 045 private SerialReader serialReader = null; 046 047 /** The parser being used to parse incoming data */ 048 private RegExParser regexParser = null; 049 050 /** 051 * Constructor that takes the port name to connect to and the rate at which 052 * to connect. The data rate will be set to 19,200 with 8 data bits, 1 stop 053 * bit and no parity. 054 * 055 * @param portName 056 * The port name to connect to. 057 * @throws Exception 058 */ 059 public SerialDevice(String portName) throws Exception 060 { 061 this(portName, 4800, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); 062 } 063 064 /** 065 * Complete constructor that takes all the information required to connect 066 * to a port. 067 * 068 * @param portName 069 * The port name to connect to. 070 * @param dataRate 071 * The data rate to read from the port. 072 * @param dataBits 073 * The number of data bits 074 * @param stopBits 075 * The number of stop bits 076 * @param parity 077 * The bit parity 078 * @throws Exception 079 */ 080 public SerialDevice(String portName, int dataRate, int dataBits, int stopBits, int parity) 081 throws Exception 082 { 083 // Set the serial port information 084 serialPort = new SerialPort(portName); 085 serialPort.openPort(); 086 serialPort.setParams(dataRate, dataBits, stopBits, parity); 087 088 // Get the input and output streams from and to the serial port. 089 outputStream = new OutputStream() { 090 @Override 091 public void write(int b) throws IOException { 092 try { 093 serialPort.writeByte((byte) b); 094 } catch (final SerialPortException e) { 095 throw new IOException(e); 096 } 097 } 098 099 @Override 100 public void write(byte[] b) throws IOException { 101 try { 102 serialPort.writeBytes(b); 103 } catch (final SerialPortException e) { 104 throw new IOException(e); 105 } 106 } 107 }; 108 inputStream = new InputStream() { 109 @Override 110 public int read() throws IOException { 111 while (true) { 112 try { 113 if (!serialPort.isOpened()) 114 return -1; 115 116 return serialPort.readBytes(1, 100)[0]; 117 } catch (final SerialPortTimeoutException e) { 118 // ignore and try again 119 } catch (final SerialPortException e) { 120 if (e.getMessage().contains("Port not opened")) 121 return -1; 122 throw new IOException(e); 123 } 124 } 125 } 126 }; 127 128 // Set up our data listener 129 regexParser = new RegExParser(regex); 130 serialReader = new SerialReader(inputStream, regexParser); 131 serialReader.addSerialDataListener(this); 132 serialPort.addEventListener(serialReader, SerialPort.MASK_RXCHAR); 133 } 134 135 /** 136 * {@inheritDoc} 137 * 138 * @see java.lang.Object#finalize() 139 */ 140 @Override 141 protected void finalize() throws Throwable 142 { 143 if (serialPort.isOpened()) { 144 serialReader.close(); 145 serialPort.removeEventListener(); 146 serialPort.closePort(); 147 } 148 super.finalize(); 149 } 150 151 /** 152 * Close the connection to the serial port. 153 * 154 * @throws IOException 155 */ 156 public void close() throws IOException 157 { 158 try { 159 serialReader.close(); 160 serialPort.removeEventListener(); 161 serialPort.closePort(); 162 } catch (final SerialPortException e) { 163 throw new IOException(e); 164 } 165 } 166 167 /** 168 * Add the given {@link SerialDataListener} to the listener list. 169 * 170 * @param sdl 171 * The {@link SerialDataListener} to add. 172 */ 173 public void addSerialDataListener(SerialDataListener sdl) 174 { 175 listeners.add(sdl); 176 } 177 178 /** 179 * Remove the given {@link SerialDataListener} from the listener list 180 * 181 * @param sdl 182 * The {@link SerialDataListener} to remove. 183 */ 184 public void removeSerialDataListener(SerialDataListener sdl) 185 { 186 listeners.remove(sdl); 187 } 188 189 /** 190 * Fires the serial data event when data is received on the port. 191 * 192 * @param data 193 * The data that was received 194 */ 195 protected void fireSerialDataEvent(String data) 196 { 197 for (final SerialDataListener listener : listeners) 198 listener.dataReceived(data); 199 } 200 201 /** 202 * Returns the regular expression being used to split incoming strings. 203 * 204 * @return the regular expression being used to split incoming strings. 205 */ 206 public String getRegex() 207 { 208 return regex; 209 } 210 211 /** 212 * Set the regular expression to use to split incoming strings. 213 * 214 * @param regex 215 * the regex to split incoming strings 216 */ 217 public void setRegex(String regex) 218 { 219 this.regex = regex; 220 this.regexParser.setRegEx(regex); 221 } 222 223 /** 224 * Returns the input stream for this device. 225 * 226 * @return the input stream 227 */ 228 public InputStream getInputStream() 229 { 230 return inputStream; 231 } 232 233 /** 234 * Returns the output stream for this device. 235 * 236 * @return the output stream 237 */ 238 public OutputStream getOutputStream() 239 { 240 return outputStream; 241 } 242 243 /** 244 * {@inheritDoc} 245 * 246 * @see org.openimaj.hardware.serial.SerialDataListener#dataReceived(java.lang.String) 247 */ 248 @Override 249 public void dataReceived(String data) 250 { 251 fireSerialDataEvent(data); 252 } 253 254 /** 255 * @return A HashSet containing the identifier for all serial ports 256 */ 257 public static HashSet<String> getSerialPorts() 258 { 259 final HashSet<String> ports = new HashSet<String>(); 260 for (final String s : SerialPortList.getPortNames()) { 261 ports.add(s); 262 } 263 return ports; 264 } 265}