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