/*
* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.util.logging.Logger;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
public class UartEchoServer implements SerialPortEventListener
{
// Command line options
private static final String OPTION_HELP ="help";
private static final String OPTION_PORT ="port";
private static final String OPTION_BAUD ="baud_rate";
private static final String OPTION_DATA ="data_bits";
private static final String OPTION_STOP ="stop_bits";
private static final String OPTION_PARITY ="parity";
private static final String OPTION_FLOW ="flow_control";
// Default settings for serial port
private static final String DEFAULT_PORT_NAME = "COM1";
private static final int DEFAULT_BAUD_RATE = 9600;
private static final int DEFAULT_DATA_BITS = SerialPort.DATABITS_8;
private static final int DEFAULT_STOP_BITS = SerialPort.STOPBITS_1;
private static final int DEFAULT_PARITY = SerialPort.PARITY_NONE;
private static final int DEFAULT_FLOW_CONTROL= SerialPort.FLOWCONTROL_NONE;
// Command format limiters
private static final byte BYTE_IGNORE = '\0'; // Null
private static final byte BYTE_CLEARBI = '\1'; // Ctrl A
private static final byte BYTE_QUERYBI = '\2'; // Ctrl B
private static final byte BYTE_RESET = '\3'; // Ctrl C
private static final byte BYTE_BLOCK_START = '[';
private static final byte BYTE_BLOCK_END = ']';
private static final String BLOCK_ASSIGN = "=";
private static final String BLOCK_SEPERATOR = ",";
// Supported commands
private static final String CMD_BAUD_RATE = "baud_rate";
private static final String CMD_DATA_BITS = "data_bits";
private static final String CMD_DELAY = "delay";
private static final String CMD_DISCONNECT = "disconnect";
private static final String CMD_ECHO = "echo";
private static final String CMD_FLOW_CONTROL = "flow_control";
private static final String CMD_LOG = "log";
private static final String CMD_PARITY = "parity";
private static final String CMD_STOP_BITS = "stop_bits";
// Supported data bits
private static final String DATA_BITS_5 = "5";
private static final String DATA_BITS_6 = "6";
private static final String DATA_BITS_7 = "7";
private static final String DATA_BITS_8 = "8";
// Supported flow control
private static final String FLOWCONTROL_NONE = "none";
private static final String FLOWCONTROL_RTSCTS = "rtscts";
private static final String FLOWCONTROL_XONXOFF = "xonxoff";
// Supported parity
private static final String PARITY_NONE = "none";
private static final String PARITY_EVEN = "even";
private static final String PARITY_ODD = "odd";
private static final String PARITY_MARK = "mark";
private static final String PARITY_SPACE = "space";
// Supported stop bits
private static final String STOP_BITS_1 = "1";
private static final String STOP_BITS_1_5 = "1.5";
private static final String STOP_BITS_2 = "2";
// Constants
private static final int BUFFER_SIZE = 1024;
private static final int SLEEP_PERIOD = 200000;
private enum EStatus
{
EStatusEcho,
EStatusCommandStart,
};
private Thread iMainThread;
private int iRestartingDelay;
private boolean iRestarting;
private boolean iRunning;
private Logger iLogger;
private byte[] iBuffer;
private EStatus iStatus;
private String iCommand;
private String iPortName;
private byte iBI;
private int iBaudRate;
private int iDataBits;
private int iStopBits;
private int iParity;
private int iFlowControl;
private int iStartupBaudRate;
private int iStartupDataBits;
private int iStartupStopBits;
private int iStartupParity;
private int iStartupFlowControl;
private CommPortIdentifier iPortIdentifier;
private SerialPort iSerialPort;
/*
* Constructor
*/
protected UartEchoServer() throws Exception
{
iRestartingDelay=0;
iRestarting=true;
iRunning=true;
iLogger=Logger.getLogger("UartEchoServer");
iBuffer=new byte[BUFFER_SIZE];
iStatus = EStatus.EStatusEcho;
iCommand = "";
iBI=0;
iPortName=DEFAULT_PORT_NAME;
iBaudRate=DEFAULT_BAUD_RATE;
iDataBits=DEFAULT_DATA_BITS;
iStopBits=DEFAULT_STOP_BITS;
iParity=DEFAULT_PARITY;
iFlowControl=DEFAULT_FLOW_CONTROL;
}
/*
* Second pahse constructor
*
* @param aCommandLine Command line parameters
*/
protected void Construct(final CommandLine aCommandLine) throws Exception
{
/*
* Set port name if passed in command line
*/
if ( aCommandLine.hasOption(OPTION_PORT) )
{
iPortName=aCommandLine.getOptionValue(OPTION_PORT);
iLogger.info("PortName:" + iPortName);
}
/*
* Set baud rate if passed in command line
*/
if ( aCommandLine.hasOption(OPTION_BAUD) )
{
setBaudRate(aCommandLine.getOptionValue(OPTION_BAUD));
iLogger.info("Baud Rate:" + iBaudRate);
}
/*
* Set data bits if passed in command line
*/
if ( aCommandLine.hasOption(OPTION_DATA) )
{
setDataBits(aCommandLine.getOptionValue(OPTION_DATA));
iLogger.info("Data Bits:" + iDataBits);
}
/*
* Set stop bits if passed in command line
*/
if ( aCommandLine.hasOption(OPTION_STOP) )
{
setStopBits(aCommandLine.getOptionValue(OPTION_STOP));
iLogger.info("Stop Bits:" + iStopBits);
}
/*
* Set parity if passed in command line
*/
if ( aCommandLine.hasOption(OPTION_PARITY) )
{
setParity(aCommandLine.getOptionValue(OPTION_PARITY));
iLogger.info("Parity:" + iParity);
}
/*
* Set flow control if passed in command line
*/
if ( aCommandLine.hasOption(OPTION_FLOW) )
{
setFlowControl(aCommandLine.getOptionValue(OPTION_FLOW));
iLogger.info("Flow Control:" + iFlowControl);
}
/*
* Save startup values. Used by reset command
*/
iStartupBaudRate=iBaudRate;
iStartupDataBits=iDataBits;
iStartupStopBits=iStopBits;
iStartupParity=iParity;
iStartupFlowControl=iFlowControl;
/*
* Make sure port is not in use
*/
iPortIdentifier=CommPortIdentifier.getPortIdentifier(iPortName);
if ( iPortIdentifier.isCurrentlyOwned() )
{
throw new Exception("Error: Port is currently in use");
}
/*
* Port not in use so open it
*/
CommPort commPort=iPortIdentifier.open(this.getClass().getName(), 2000);
/*
* Save thread
*/
iMainThread=Thread.currentThread();
/*
* Make sure the port is of type serial
*/
if ( commPort instanceof SerialPort )
{
iSerialPort = (SerialPort) commPort;
iFlowControl=iSerialPort.getFlowControlMode();
while ( iRunning )
{
initPort();
iRestarting=false;
while ( iRunning && !iRestarting )
{
try
{
iMainThread.sleep(SLEEP_PERIOD);
}
catch ( InterruptedException e )
{
}
}
iSerialPort.close();
if ( iRestarting )
{
iLogger.finest("Restarting");
iMainThread.sleep(iRestartingDelay);
commPort=iPortIdentifier.open(this.getClass().getName(), 2000);
iSerialPort = (SerialPort) commPort;
}
}
}
else
{
throw new Exception("Error: Only serial ports are handled by this example.");
}
}
/*
* Initialise the port
*/
private void initPort() throws Exception
{
iSerialPort.setSerialPortParams(iBaudRate, iDataBits, iStopBits, iParity);
iSerialPort.setFlowControlMode(iFlowControl);
iSerialPort.addEventListener(this);
iSerialPort.notifyOnBreakInterrupt(true);
iSerialPort.notifyOnCarrierDetect(true);
iSerialPort.notifyOnCTS(true);
iSerialPort.notifyOnDataAvailable(true);
iSerialPort.notifyOnDSR(true);
iSerialPort.notifyOnFramingError(true);
iSerialPort.notifyOnOutputEmpty(true);
iSerialPort.notifyOnOverrunError(true);
iSerialPort.notifyOnParityError(true);
iSerialPort.notifyOnRingIndicator(true);
iSerialPort.setInputBufferSize(BUFFER_SIZE);
}
/*
* Set the baud rate
*
* @param aValue String representation of the baud rate
*/
private void setBaudRate(final String aValue)
{
try
{
iBaudRate=Integer.parseInt(aValue);
}
catch (Exception e)
{
iLogger.severe("convertToBaudRate(" + aValue + "):exception" + e);
}
}
/*
* Set the data bits
*
* @param aValue String representation of the data bits
*/
private void setDataBits(final String aValue)
{
try
{
if ( aValue.compareTo(DATA_BITS_5)==0 )
{
iDataBits=SerialPort.DATABITS_5;
}
else if ( aValue.compareTo(DATA_BITS_6)==0 )
{
iDataBits=SerialPort.DATABITS_6;
}
else if ( aValue.compareTo(DATA_BITS_7)==0 )
{
iDataBits=SerialPort.DATABITS_7;
}
else if ( aValue.compareTo(DATA_BITS_8)==0 )
{
iDataBits=SerialPort.DATABITS_8;
}
else
{
iLogger.severe("setDataBits(" + aValue + ")");
}
}
catch (Exception e)
{
iLogger.severe("setDataBits(" + aValue + "):excpetion" + e);
}
}
/*
* Set the flow control
*
* @param aValue String representation of the flow control
*/
private void setFlowControl(final String aValue)
{
try
{
if ( aValue.compareTo(FLOWCONTROL_NONE)==0 )
{
iFlowControl=SerialPort.FLOWCONTROL_NONE;
}
else if ( aValue.compareTo(FLOWCONTROL_RTSCTS)==0 )
{
iFlowControl=SerialPort.FLOWCONTROL_RTSCTS_IN | SerialPort.FLOWCONTROL_RTSCTS_OUT;
}
else if ( aValue.compareTo(FLOWCONTROL_XONXOFF)==0 )
{
iFlowControl=SerialPort.FLOWCONTROL_XONXOFF_IN | SerialPort.FLOWCONTROL_XONXOFF_OUT;
}
else
{
iLogger.severe("setFlowControl(" + aValue + ")");
}
}
catch (Exception e)
{
iLogger.severe("setFlowControl(" + aValue + "):exception" + e);
}
}
/*
* Set the parity
*
* @param aValue String representation of the parity
*/
private void setParity(final String aValue)
{
try
{
if ( aValue.compareTo(PARITY_NONE)==0 )
{
iParity=SerialPort.PARITY_NONE;
}
else if ( aValue.compareTo(PARITY_EVEN)==0 )
{
iParity=SerialPort.PARITY_EVEN;
}
else if ( aValue.compareTo(PARITY_ODD)==0 )
{
iParity=SerialPort.PARITY_ODD;
}
else if ( aValue.compareTo(PARITY_MARK)==0 )
{
iParity=SerialPort.PARITY_MARK;
}
else if ( aValue.compareTo(PARITY_SPACE)==0 )
{
iParity=SerialPort.PARITY_SPACE;
}
else
{
iLogger.severe("setParity(" + aValue + ")");
}
}
catch (Exception e)
{
iLogger.severe("setParity(" + aValue + "):exception" + e);
}
}
/*
* Set the stop bits
*
* @param aValue String representation of the stop bits
*/
private void setStopBits(final String aValue)
{
try
{
if ( aValue.compareTo(STOP_BITS_1)==0 )
{
iStopBits=SerialPort.STOPBITS_1;
}
else if ( aValue.compareTo(STOP_BITS_1_5)==0 )
{
iStopBits=SerialPort.STOPBITS_1_5;
}
else if ( aValue.compareTo(STOP_BITS_2)==0 )
{
iStopBits=SerialPort.STOPBITS_2;
}
else
{
iLogger.severe("setStopBits(" + aValue + ")");
}
}
catch (Exception e)
{
iLogger.severe("setStopBits(" + aValue + "):exception" + e);
}
}
/*
* Process a command from the input
*/
private void processCommand() throws Exception
{
final String values[] = iCommand.split(BLOCK_SEPERATOR);
iSerialPort.getOutputStream().flush();
for ( int i=0; i<values.length; i++ )
{
/*
* Commands should be of the type variable=value
*/
final String parts[]=values[i].split(BLOCK_ASSIGN);
if ( parts.length==2 )
{
/*
* Set baud rate command
*/
if ( parts[0].compareTo(CMD_BAUD_RATE)==0 )
{
setBaudRate(parts[1]);
iSerialPort.setSerialPortParams(iBaudRate, iDataBits, iStopBits, iParity);
}
/*
* Set data bits command
*/
else if ( parts[0].compareTo(CMD_DATA_BITS)==0 )
{
setDataBits(parts[1]);
iSerialPort.setSerialPortParams(iBaudRate, iDataBits, iStopBits, iParity);
}
/*
* Delay command
*/
else if ( parts[0].compareTo(CMD_DELAY)==0 )
{
final int delay=Integer.parseInt(parts[1]);
Thread.sleep(delay);
}
/*
* Disconnect command
*/
else if ( parts[0].compareTo(CMD_DISCONNECT)==0 )
{
iRestartingDelay=Integer.parseInt(parts[1]);
iRestarting=true;
}
/*
* Echo command
*/
else if ( parts[0].compareTo(CMD_ECHO)==0 )
{
final int length=parts[1].length();
for ( int index=0; index<length; ++index )
{
final byte out=(byte)(parts[1].charAt(index));
iLogger.finest("<< " + out);
iSerialPort.getOutputStream().write(out);
}
}
/*
* Set flow control command
*/
else if ( parts[0].compareTo(CMD_FLOW_CONTROL)==0 )
{
setFlowControl(parts[1]);
iSerialPort.setFlowControlMode(iFlowControl);
}
/*
* Log command
*/
else if ( parts[0].compareTo(CMD_LOG)==0 )
{
iLogger.info(parts[1]);
}
/*
* Set parity command
*/
else if ( parts[0].compareTo(CMD_PARITY)==0 )
{
setParity(parts[1]);
iSerialPort.setSerialPortParams(iBaudRate, iDataBits, iStopBits, iParity);
}
/*
* Set stop bits command
*/
else if ( parts[0].compareTo(CMD_STOP_BITS)==0 )
{
setStopBits(parts[1]);
iSerialPort.setSerialPortParams(iBaudRate, iDataBits, iStopBits, iParity);
}
/*
* Error command
*/
else
{
iLogger.severe("Bad command(" + parts[0] + ")");
}
}
else
{
iLogger.severe("Bad data");
}
}
}
/*
* Process data read from input stream
*
* @param aLength Length of the data in the buffer
*/
private void processInput(final int aLength) throws Exception
{
final String buffer=new String(iBuffer, 0, aLength);
iLogger.finest(">> " + buffer);
for ( int index=0; index<aLength; ++index )
{
switch ( iBuffer[index] )
{
/*
* Ignored data
*/
case BYTE_IGNORE:
break;
/*
* Clear the break interrupt count
*/
case BYTE_CLEARBI:
iBI=0;
break;
/*
* Query the break interrupt count
*/
case BYTE_QUERYBI:
iLogger.finest("BI Count=" + iBI);
iLogger.finest("<< " + iBI);
iSerialPort.getOutputStream().write(iBI);
break;
/*
* Reset port setting to startup values
*/
case BYTE_RESET:
iBaudRate=iStartupBaudRate;
iDataBits=iStartupDataBits;
iStopBits=iStartupStopBits;
iParity=iStartupParity;
iFlowControl=iStartupFlowControl;
iSerialPort.setFlowControlMode(iFlowControl);
iSerialPort.setSerialPortParams(iBaudRate, iDataBits, iStopBits, iParity);
iStatus=EStatus.EStatusEcho;
break;
default:
/*
* If in command mode add the byte to the command buffer
* unless we read the end of command block character
*/
if ( iStatus==EStatus.EStatusCommandStart )
{
if ( iBuffer[index]==BYTE_BLOCK_END )
{
processCommand();
iStatus=EStatus.EStatusEcho;
}
else
{
iCommand += buffer.charAt(index);
}
}
/*
* If in echo mode, echo the character unless we read the
* start of command block character
*/
else if ( iStatus==EStatus.EStatusEcho )
{
if ( iBuffer[index]==BYTE_BLOCK_START )
{
iCommand="";
iStatus=EStatus.EStatusCommandStart;
}
else
{
iLogger.finest("<< " + iBuffer[index]);
iSerialPort.getOutputStream().write(iBuffer[index]);
}
}
break;
}
}
}
/*
* Serial port event received
*
* @param aEvent Event received
*/
public void serialEvent(SerialPortEvent aEvent)
{
switch ( aEvent.getEventType() )
{
case SerialPortEvent.DATA_AVAILABLE:
/*
* Process data in input buffer
*/
try
{
final int length = iSerialPort.getInputStream().read(iBuffer);
if ( length>0 )
{
processInput(length);
}
}
catch (Exception e)
{
e.printStackTrace();
}
if ( iRestarting )
{
iMainThread.interrupt();
}
break;
case SerialPortEvent.BI:
/*
* Increment break interrupt count
*/
++iBI;
iLogger.finest("Break Interrupt");
break;
case SerialPortEvent.CD:
/*
* Ignore
*/
iLogger.finest("Carrier Detect");
break;
case SerialPortEvent.CTS:
/*
* Ignore
*/
iLogger.finest("Clear To Send");
break;
case SerialPortEvent.DSR:
/*
* Ignore
*/
iLogger.finest("Data Set Ready");
break;
case SerialPortEvent.FE:
/*
* Ignore
*/
iLogger.finest("Framing Error");
break;
case SerialPortEvent.OE:
/*
* Ignore
*/
iLogger.finest("Overflow Error");
break;
case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
/*
* Ignore
*/
iLogger.finest("Output Buffer Empty");
break;
case SerialPortEvent.PE:
/*
* Ignore
*/
iLogger.finest("Parity Error");
break;
case SerialPortEvent.RI:
/*
* Ignore
*/
iLogger.finest("Ring Interrupt");
break;
default:
iLogger.finest("Unknown event");
break;
}
}
/*
* Application entry point
*
* @param aArgs COmmand line arguments
*/
public static void main(String[] aArgs)
{
final Options options = new Options();
options.addOption(new Option(OPTION_HELP, "print this message"));
OptionBuilder.withLongOpt(OPTION_PORT);
OptionBuilder.withDescription("set port COMx");
OptionBuilder.withValueSeparator( '=' );
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create());
OptionBuilder.withLongOpt(OPTION_BAUD);
OptionBuilder.withDescription("set the baud rate");
OptionBuilder.withValueSeparator( '=' );
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create());
OptionBuilder.withLongOpt(OPTION_DATA);
OptionBuilder.withDescription("set the data bits [" + DATA_BITS_5 +"|" + DATA_BITS_6 + "|" + DATA_BITS_7 + "|" + DATA_BITS_8 + "]");
OptionBuilder.withValueSeparator( '=' );
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create());
OptionBuilder.withLongOpt(OPTION_STOP);
OptionBuilder.withDescription("set the stop bits [" + STOP_BITS_1 + "|" + STOP_BITS_1_5 + "|" + STOP_BITS_2 + "]");
OptionBuilder.withValueSeparator( '=' );
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create());
OptionBuilder.withLongOpt(OPTION_PARITY);
OptionBuilder.withDescription("set the parity [" + PARITY_NONE + "|" + PARITY_EVEN + "|" + PARITY_ODD + "|" + PARITY_MARK + "|" + PARITY_SPACE + "]");
OptionBuilder.withValueSeparator( '=' );
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create());
OptionBuilder.withLongOpt(OPTION_FLOW);
OptionBuilder.withDescription("set the flow control [" + FLOWCONTROL_NONE + "|" + FLOWCONTROL_RTSCTS + "|" + FLOWCONTROL_XONXOFF + "]");
OptionBuilder.withValueSeparator( '=' );
OptionBuilder.hasArg();
options.addOption(OptionBuilder.create());
final CommandLineParser parser = new PosixParser();
try
{
// parse the command line arguments
final CommandLine commandLine = parser.parse(options, aArgs);
if ( commandLine.hasOption(OPTION_HELP) )
{
final HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("UartEchoServer", options);
}
else
{
final UartEchoServer echoServer=new UartEchoServer();
echoServer.Construct(commandLine);
}
}
catch ( Exception e )
{
e.printStackTrace();
}
}
}