001/** 002 * 003 */ 004package org.openimaj.hardware.serial; 005 006import gnu.trove.list.TByteList; 007import gnu.trove.list.array.TByteArrayList; 008 009import java.io.Closeable; 010import java.io.IOException; 011import java.io.InputStream; 012import java.util.ArrayList; 013import java.util.List; 014 015import jssc.SerialPortEvent; 016import jssc.SerialPortEventListener; 017 018/** 019 * An event listener that receives data from the serial port, buffers the data, 020 * parses the data then calls the listeners for every sentence parsed. 021 * 022 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 023 * 024 * @created 12 Jul 2011 025 */ 026public class SerialReader implements SerialPortEventListener, Closeable 027{ 028 /** The input stream from the serial device */ 029 private InputStream inputStream = null; 030 031 /** The parser being used for incoming data */ 032 private SerialDataParser parser = null; 033 034 /** We use trove to buffer the incoming data */ 035 private TByteList buffer = new TByteArrayList(); 036 037 /** The maximum size of a buffer before parsing data */ 038 private int maxSize = 256; 039 040 /** Listeners */ 041 private List<SerialDataListener> listeners = new ArrayList<SerialDataListener>(); 042 043 private boolean closed = false; 044 045 /** 046 * Default constructor 047 * 048 * @param in 049 * @param parser 050 */ 051 public SerialReader(InputStream in, SerialDataParser parser) 052 { 053 this.inputStream = in; 054 this.parser = parser; 055 } 056 057 /** 058 * {@inheritDoc} 059 */ 060 @Override 061 public void serialEvent(SerialPortEvent event) 062 { 063 if (closed) 064 return; 065 066 if (event != null && !event.isRXCHAR()) 067 return; 068 069 try 070 { 071 // Reads all the data from the serial port event (upto a maximum 072 // size) 073 int data = 0; 074 while (buffer.size() < maxSize && (data = inputStream.read()) > -1) 075 buffer.add((byte) data); 076 077 // Parse the data 078 final String dataString = new String(buffer.toArray(), 0, buffer.size()); 079 final String[] strings = parser.parse(dataString); 080 final String leftOvers = parser.getLeftOverString(); 081 082 // If we've got to the end of the stream, we'll simply fire the 083 // events 084 // for the strings that are parsed and the left overs. 085 if (data == -1) 086 { 087 if (strings.length > 0) 088 fireDataReceived(strings); 089 if (leftOvers.length() > 0) 090 fireDataReceived(new String[] { leftOvers }); 091 buffer.clear(); 092 } 093 else 094 { 095 // Keep the left-over parts of the string in the buffer 096 if (leftOvers != null) 097 buffer = buffer.subList( 098 buffer.size() - leftOvers.length(), 099 buffer.size()); 100 else 101 buffer.clear(); 102 103 // Let everyone know we have data! 104 fireDataReceived(strings); 105 } 106 } catch (final IOException e) 107 { 108 // FIXME: RuntimeException? Seems a bit harsh. 109 throw new RuntimeException(e); 110 } 111 } 112 113 /** 114 * Add a serial data listener that will be informed of individual tokens 115 * that are parsed from the parser. 116 * 117 * @param listener 118 * The listener 119 */ 120 public void addSerialDataListener(SerialDataListener listener) 121 { 122 listeners.add(listener); 123 } 124 125 /** 126 * Remove the given listener from this reader. 127 * 128 * @param listener 129 * The listener 130 */ 131 public void removeSerialDataListener(SerialDataListener listener) 132 { 133 listeners.remove(listener); 134 } 135 136 /** 137 * Fire multiple events: one for each parsed string. 138 * 139 * @param strings 140 * The strings parsed from the parser. 141 */ 142 protected void fireDataReceived(String[] strings) 143 { 144 for (final String s : strings) 145 for (final SerialDataListener listener : listeners) 146 listener.dataReceived(s); 147 } 148 149 /** 150 * Set the size of the buffer to use. The buffer size must be larger than 151 * any expected data item that you are wanting to parse. If your sentences 152 * can be up to 128 bytes, then the buffer should be at least 128 bytes. It 153 * can be larger. 154 * 155 * @param maxSize 156 * The size of the buffer to use. 157 */ 158 public void setMaxBufferSize(int maxSize) 159 { 160 this.maxSize = maxSize; 161 } 162 163 @Override 164 public void close() throws IOException { 165 this.closed = true; 166 } 167}