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}