/*
* (C) Copyright 2015 by fr3ts0n <erwin.scheuch-heilig@gmx.at>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundationpe; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package com.fr3ts0n.prot;
import org.apache.log4j.Logger;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Vector;
/**
* Protocol I/O stream handler
*
* @author erwin
*/
public class StreamHandler implements TelegramWriter, Runnable
{
private static final Logger log = Logger.getLogger("stream");
private InputStream in;
private BufferedWriter out;
private TelegramListener messageHandler;
// current receive message
private String message = "";
public StreamHandler()
{
}
/**
* Construct new StreamHandler using specified input and output stream
*
* @param inStream stream for incoming messages
* @param outStream stream for outgoing messages
*/
public StreamHandler(InputStream inStream, OutputStream outStream)
{
setStreams(inStream, outStream);
}
/**
* Set the input and output stream
*
* @param inStream stream for incoming messages
* @param outStream stream for outgoing messages
*/
public void setStreams(InputStream inStream, OutputStream outStream)
{
in = inStream;
/* Output uses BufferedWriter with buffer size 1 byte to trigger
flushing on every outgoing byte [$Fix #AndrOBD-27] */
out = new BufferedWriter(new OutputStreamWriter(outStream), 1);
}
/* (non-Javadoc)
* @see com.fr3ts0n.prot.TelegramWriter#writeTelegram(char[])
*/
@Override
public int writeTelegram(char[] buffer)
{
return (writeTelegram(buffer, 0, null));
}
/* (non-Javadoc)
* @see com.fr3ts0n.prot.TelegramWriter#writeTelegram(char[], int, java.lang.Object)
*/
@Override
public int writeTelegram(char[] buffer, int type, Object id)
{
int result = 0;
try
{
String msg = new String(buffer);
msg += "\r";
log.trace(this.toString() + " TX:" + ProtUtils.hexDumpBuffer(msg.toCharArray()));
out.write(msg.toCharArray());
out.flush();
result = buffer.length;
} catch (Exception ex)
{
log.warn("TX error:'" + ProtUtils.hexDumpBuffer(buffer) + "':" + ex);
}
return (result);
}
/**
* process incoming character
* @param chr the received char
*/
private void processRxChar(int chr)
{
// process incoming data
log.trace(this.toString() + " RX: '"
+ String.format("%02X : %1c", (byte) chr, chr < 32 ? '.' : chr)
+ "'");
switch (chr)
{
// ignore special characters
case 32:
break;
// trigger message handling for new request
case '>':
message += (char) chr;
// trigger message handling
case 10:
case 13:
if (messageHandler != null && !message.isEmpty())
messageHandler.handleTelegram(message.toCharArray());
message = "";
break;
default:
message += (char) chr;
}
}
/**
* start the thread
*/
@Override
@SuppressWarnings("fallthrough")
public void run()
{
int chr;
log.info("RX Thread started");
try
{
// loop until stream closed / invalid
while (true)
{
// if no data available, then wait for it
if (in.available() > 0)
{
// otherwise read- and process ...
// Is end of stream reached?
if ((chr = in.read()) > 0)
{
// process incoming data
processRxChar(chr);
}
else
{
log.warn(this.toString() + " RX: End of stream!");
// stream finished - break loop
break;
}
}
else
{
// wait 1 ms for incoming data
Thread.sleep(1);
}
}
}
catch ( Exception ex )
{
log.warn("RX error", ex);
}
log.info("RX Thread stopped");
}
/**
* Getter for property messageHandler.
*
* @return Value of property messageHandler.
*/
public TelegramListener getMessageHandler()
{
return messageHandler;
}
/**
* Setter for property messageHandler.
*
* @param messageHandler New value of property messageHandler.
*/
public void setMessageHandler(TelegramListener messageHandler)
{
this.messageHandler = messageHandler;
}
/** Utility field used by event firing mechanism. */
private Vector<PropertyChangeListener> listenerList = null;
/**
* Registers PropertyChangeListener to receive events.
*
* @param listener The listener to register.
*/
public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
{
if (listenerList == null)
{
listenerList = new Vector<PropertyChangeListener>();
}
listenerList.add(listener);
}
/**
* Removes PropertyChangeListener from the list of listeners.
*
* @param listener The listener to remove.
*/
public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
{
listenerList.remove(listener);
}
/**
* Notifies all registered listeners about the event.
*
* @param event The event to be fired
*/
protected void firePropertyChange(PropertyChangeEvent event)
{
if (listenerList == null) return;
PropertyChangeListener listener;
for (PropertyChangeListener aListenerList : listenerList)
{
listener = aListenerList;
listener.propertyChange(event);
}
}
}