/** * Copyright (c) 2011-2014, OpenIoT * * This file is part of OpenIoT. * * OpenIoT is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, version 3 of the License. * * OpenIoT 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with OpenIoT. If not, see <http://www.gnu.org/licenses/>. * * Contact: OpenIoT mailto: info@openiot.eu * @author Ali Salehi * @author Mehdi Riahi */ package org.openiot.gsn.wrappers.general; import gnu.io.CommPortIdentifier; import gnu.io.NoSuchPortException; import gnu.io.PortInUseException; import gnu.io.SerialPort; import gnu.io.SerialPortEvent; import gnu.io.SerialPortEventListener; import gnu.io.UnsupportedCommOperationException; import org.openiot.gsn.beans.AddressBean; import org.openiot.gsn.beans.DataField; import org.openiot.gsn.utils.KeyValueImp; import org.openiot.gsn.wrappers.AbstractWrapper; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Properties; import java.util.TooManyListenersException; import javax.naming.OperationNotSupportedException; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; /** * Modified to used RXTX (http://users.frii.com/jarvi/rxtx/) which a LGPL * replacement for javacomm. The Easiest way to install RXTX is from the binary * distribution which is available at * http://users.frii.com/jarvi/rxtx/download.html Links GSN to a sensor network * through serial port. <p/> The only needed parameter is the serial port * address, provided through xml. Default connection settings are 9600 8 N 1 * Optional parameters for the XML file: - inputseparator: if set, use this as * divider between data 'packets' - baudrate: set serialport baudrate (default: * 9600) - flowcontrolmode: set serialport flowcontrol mode possible values are: - * FLOWCONTROL_NONE: Flow control off. - FLOWCONTROL_RTSCTS_IN: RTS/CTS flow * control on input. - FLOWCONTROL_RTSCTS_OUT: RTS/CTS flow control on output. - * FLOWCONTROL_XONXOFF_IN: XON/XOFF flow control on input. - * FLOWCONTROL_XONXOFF_OUT: XON/XOFF flow control on output. - databits: set * serialport databits (5, 6, 7 or 8) default is 8 - stopbits: set serialport * stopbits (1, 2 or 1.5) default is 1 - parity: set serialport parity possiblie * values are: - PARITY_EVEN: EVEN parity scheme. - PARITY_MARK: MARK parity * scheme. - PARITY_NONE: No parity bit. (default) - PARITY_ODD: ODD parity * scheme. - PARITY_SPACE: SPACE parity scheme. */ public class SerialWrapper extends AbstractWrapper implements SerialPortEventListener { public static final String RAW_PACKET = "RAW_PACKET"; private final transient Logger logger = Logger.getLogger( SerialWrapper.class ); private SerialConnection wnetPort; private int threadCounter = 0; public InputStream is; private AddressBean addressBean; private String serialPort; private String inputSeparator; private boolean useInputSeparator; private int flowControlMode; private int baudRate = 9600; private int dataBits = SerialPort.DATABITS_8; private int stopBits = SerialPort.STOPBITS_1; private int parity = SerialPort.PARITY_NONE; private DataField [] dataField ; private int output_format; private int packet_length = 100; /* * Needs the following information from XML file : serialport : the name of * the serial port (/dev/ttyS0...) Optional parameters for the XML file: - * inputseparator: if set, use this as divider between data 'packets' - * baudrate: set serialport baudrate (default: 9600) - flowcontrolmode: set * serialport flowcontrol mode possible values are: - FLOWCONTROL_NONE: Flow * control off. - FLOWCONTROL_RTSCTS_IN: RTS/CTS flow control on input. - * FLOWCONTROL_RTSCTS_OUT: RTS/CTS flow control on output. - * FLOWCONTROL_XONXOFF_IN: XON/XOFF flow control on input. - * FLOWCONTROL_XONXOFF_OUT: XON/XOFF flow control on output. - databits: set * serialport databits (5, 6, 7 or 8) default is 8 - stopbits: set serialport * stopbits (1, 2 or 1.5) default is 1 - parity: set serialport parity * possiblie values are: - PARITY_EVEN: EVEN parity scheme. - PARITY_MARK: * MARK parity scheme. - PARITY_NONE: No parity bit. (default) - PARITY_ODD: * ODD parity scheme. - PARITY_SPACE: SPACE parity scheme. */ public boolean initialize ( ) { setName( "SerialWrapper-Thread" + ( ++threadCounter ) ); addressBean = getActiveAddressBean( ); serialPort = addressBean.getPredicateValue( "serialport" ); if ( serialPort == null || serialPort.trim( ).length( ) == 0 ) { logger.warn( "The >serialport< parameter is missing from the SerialWrapper, wrapper initialization failed." ); return false; } inputSeparator = addressBean.getPredicateValue( "inputseparator" ); if ( inputSeparator == null ) useInputSeparator = false; else useInputSeparator = true; String representation = addressBean.getPredicateValue( "representation" ); if ( representation == null || representation.equalsIgnoreCase("binary") ){ output_format=0; }else if (representation.startsWith("string")) output_format=1; else { logger.error("The provided representation >"+representation+"< is not valid, possible values are binary , string"); return false; } packet_length= addressBean.getPredicateValueAsInt("packet-length",100); String newBaudRate = addressBean.getPredicateValue( "baudrate" ); if ( newBaudRate != null && newBaudRate.trim( ).length( ) > 0 ) { baudRate = Integer.parseInt( newBaudRate ); // TODO: check validity of // baudrate? } String newDataBits = addressBean.getPredicateValue( "databits" ); if ( newDataBits != null && newDataBits.trim( ).length( ) > 0 ) { switch ( Integer.parseInt( newDataBits ) ) { case 5 : dataBits = SerialPort.DATABITS_5; break; case 6 : dataBits = SerialPort.DATABITS_6; break; case 7 : dataBits = SerialPort.DATABITS_7; break; case 8 : dataBits = SerialPort.DATABITS_8; break; } } String newStopBits = addressBean.getPredicateValue( "stopbits" ); if ( newStopBits != null && newStopBits.trim( ).length( ) > 0 ) { float newstopbits = Float.parseFloat( newStopBits ); if ( newstopbits == 1.0 ) stopBits = SerialPort.STOPBITS_1; if ( newstopbits == 2.0 ) stopBits = SerialPort.STOPBITS_2; if ( newstopbits == 1.5 ) stopBits = SerialPort.STOPBITS_1_5; } String newParity = addressBean.getPredicateValue( "parity" ); if ( newParity != null && newParity.trim( ).length( ) > 0 ) { if ( newParity.equals( "PARITY_EVEN" ) ) parity = SerialPort.PARITY_EVEN; if ( newParity.equals( "PARITY_MARK" ) ) parity = SerialPort.PARITY_MARK; if ( newParity.equals( "PARITY_NONE" ) ) parity = SerialPort.PARITY_NONE; if ( newParity.equals( "PARITY_ODD" ) ) parity = SerialPort.PARITY_ODD; if ( newParity.equals( "PARITY_SPACE" ) ) parity = SerialPort.PARITY_SPACE; } String newflowControlMode = addressBean.getPredicateValue( "flowcontrolmode" ); if ( newflowControlMode != null && newflowControlMode.trim( ).length( ) > 0 ) { flowControlMode = 0; String modes[] = newflowControlMode.split( "\\|" ); for ( int i = 0 ; i < modes.length ; i++ ) { if ( modes[ i ].equals( "FLOWCONTROL_NONE" ) ) flowControlMode |= SerialPort.FLOWCONTROL_NONE; if ( modes[ i ].equals( "FLOWCONTROL_RTSCTS_IN" ) ) flowControlMode |= SerialPort.FLOWCONTROL_RTSCTS_IN; if ( modes[ i ].equals( "FLOWCONTROL_RTSCTS_OUT" ) ) flowControlMode |= SerialPort.FLOWCONTROL_RTSCTS_OUT; if ( modes[ i ].equals( "FLOWCONTROL_XONXOFF_IN" ) ) flowControlMode |= SerialPort.FLOWCONTROL_XONXOFF_IN; if ( modes[ i ].equals( "FLOWCONTROL_XONXOFF_OUT" ) ) flowControlMode |= SerialPort.FLOWCONTROL_XONXOFF_OUT; } if ( flowControlMode == 0 ) { flowControlMode = -1; // don't set flow control mode if it is // empty or is only composed of invalid // arguments } } else { flowControlMode = -1; } // TASK : TRYING TO CONNECT USING THE ADDRESS wnetPort = new SerialConnection( serialPort ); if ( wnetPort.openConnection( ) == false ) { return false; } wnetPort.addEventListener( this ); is = wnetPort.getInputStream( ); if ( logger.isDebugEnabled( ) ) { logger.debug( "Serial port wrapper successfully opened port and registered itself as listener." ); } inputBuffer = new byte [ MAXBUFFERSIZE ]; dataField = new DataField[] { new DataField( RAW_PACKET , (output_format==0?"binary":"varchar")+"("+packet_length+")" , "The packet contains raw data from a sensor network." ) }; return true; } /** * A class that handles the details of the serial connection. */ public class SerialConnection { protected OutputStream os; protected InputStream is; private CommPortIdentifier portId; public SerialPort sPort; private String serialPort; private boolean open; /** * Creates a SerialConnection object and initialiazes variables passed in * as params. * * @param serialPort A SerialParameters object. */ public SerialConnection ( String serialPort ) { open = false; this.serialPort = serialPort; } /** * Attempts to open a serial connection (9600 8N1). If it is unsuccesfull * at any step it returns the port to a closed state, throws a * <code>SerialConnectionException</code>, and returns. <p/> Gives a * timeout of 30 seconds on the portOpen to allow other applications to * reliquish the port if have it open and no longer need it. */ public boolean openConnection ( ) { // Obtain a CommPortIdentifier object for the port you want to open. try { portId = CommPortIdentifier.getPortIdentifier( serialPort ); } catch ( NoSuchPortException e ) { logger.error( "Port doesn't exist : " + serialPort , e ); return false; } // Open the port represented by the CommPortIdentifier object. // Give the open call a relatively long timeout of 30 seconds to // allow a different application to reliquish the port if the user // wants to. if ( portId.isCurrentlyOwned( ) ) { logger.error( "port owned by someone else" ); return false; } try { sPort = ( SerialPort ) portId.open( "GSNSerialConnection" , 30 * 1000 ); sPort.setSerialPortParams( baudRate , dataBits , stopBits , parity ); if ( flowControlMode != -1 ) { sPort.setFlowControlMode( flowControlMode ); } } catch ( PortInUseException e ) { logger.error( e.getMessage( ) , e ); return false; } catch ( UnsupportedCommOperationException e ) { logger.error( e.getMessage( ) , e ); return false; } // Open the input and output streams for the connection. If they // won't // open, close the port before throwing an exception. try { os = sPort.getOutputStream( ); is = sPort.getInputStream( ); } catch ( IOException e ) { sPort.close( ); logger.error( e.getMessage( ) , e ); return false; } sPort.notifyOnDataAvailable( true ); sPort.notifyOnBreakInterrupt( false ); // Set receive timeout to allow breaking out of polling loop // during // input handling. try { sPort.enableReceiveTimeout( 30 ); } catch ( UnsupportedCommOperationException e ) { } open = true; return true; } /** * Close the port and clean up associated elements. */ public void closeConnection ( ) { // If port is alread closed just return. if ( !open ) { return; } // Check to make sure sPort has reference to avoid a NPE. if ( sPort != null ) { try { os.close( ); is.close( ); } catch ( IOException e ) { System.err.println( e ); } sPort.close( ); } open = false; } /** * Send a one second break signal. */ public void sendBreak ( ) { sPort.sendBreak( 1000 ); } /** * Reports the open status of the port. * * @return true if port is open, false if port is closed. */ public boolean isOpen ( ) { return open; } public void addEventListener ( SerialPortEventListener listener ) { try { sPort.addEventListener( listener ); } catch ( TooManyListenersException e ) { sPort.close( ); logger.warn( e.getMessage( ) , e ); } } /** * Send a byte. */ public void sendByte ( int i ) { try { os.write( i ); } catch ( IOException e ) { System.err.println( "OutputStream write error: " + e ); } } public InputStream getInputStream ( ) { return is; } public OutputStream getOutputStream ( ) { return os; } } public void run ( ) { } public boolean sendToWrapper ( Object dataItem ) throws OperationNotSupportedException { if ( logger.isDebugEnabled( ) ) logger.debug( "Serial wrapper received a serial port sending..." ); if ( !wnetPort.isOpen( ) ) throw new OperationNotSupportedException( "The connection is closed." ); try { if ( logger.isDebugEnabled( ) ) logger.debug( "Serial wrapper performing a serial port sending." ); if ( dataItem instanceof byte [ ] ) wnetPort.getOutputStream( ).write( ( byte [ ] ) dataItem ); else { // general case, writes using the printwriter. PrintWriter pw = new PrintWriter( wnetPort.getOutputStream( ) ); pw.write( dataItem.toString( ) ); pw.flush( ); pw.close( ); } return true; } catch ( IOException e ) { logger.warn( "OutputStream write error. " , e ); return false; } } public DataField [] getOutputFormat ( ) { return dataField; } public void dispose ( ) { wnetPort.closeConnection( ); threadCounter--; } private static final int MAXBUFFERSIZE = 1024; private byte [ ] inputBuffer; public void serialEvent ( SerialPortEvent e ) { // if ( logger.isDebugEnabled( ) ) logger.debug( "Serial wrapper received a serial port event, reading..." ); // if ( !isActive( ) || listeners.isEmpty( ) ) { // if ( logger.isDebugEnabled( ) ) logger.debug( "Serial wrapper dropped the input b/c there is no listener there or the wrapper is inactive." ); // return; // } // Determine type of event. switch ( e.getEventType( ) ) { // Read data until -1 is returned. case SerialPortEvent.DATA_AVAILABLE : /* * int index = 0; while (newData != -1) { try { if (is == null) { if * (logger.isDebugEnabled ()) logger.debug("SerialWrapper: Warning, * is == null !"); is = wnetPort.getInputStream(); } else newData = * is.read(); if (newData > -1 && newData < 256) { * inputBuffer[index++] = (byte) newData; } } catch (IOException ex) { * System.err.println(ex); return; } } */ try { is.read( inputBuffer ); } catch ( IOException ex ) { logger.warn( "Serial port wrapper couldn't read data : " + ex ); return; } break; // If break event append BREAK RECEIVED message. case SerialPortEvent.BI : // messageAreaIn.append("\n--- BREAK RECEIVED ---\n"); } if ( logger.isDebugEnabled( ) ) logger.debug( new StringBuilder( "Serial port wrapper processed a serial port event, stringbuffer is now : " ).append( new String(inputBuffer) ).toString( ) ); if ( useInputSeparator ) { for ( String chunk : new String(inputBuffer).split( inputSeparator ) ) if ( chunk.length( ) > 0 ) post_item(chunk); } else { //without separator character. post_item(new String(inputBuffer) ); } } private void post_item (String val){ switch (output_format){ case 0: // for binary data postStreamElement( val.length()>packet_length?val.substring(0,packet_length).getBytes():val.getBytes() ); break; case 1: // for strings postStreamElement( val.length()>packet_length ? val.substring(0,packet_length):val ); break; } } public static void main ( String [ ] args ) { Properties properties = new Properties( ); properties.put( "log4j.rootLogger" , "DEBUG,console" ); properties.put( "log4j.appender.console" , "org.apache.log4j.ConsoleAppender" ); properties.put( "log4j.appender.console.Threshold" , "DEBUG" ); properties.put( "log4j.appender.console.layout" , "org.apache.log4j.PatternLayout" ); properties.put( "log4j.appender.console.layout.ConversionPattern" , "%-6p[%d] [%t] (%13F:%L) %3x - %m%n" ); PropertyConfigurator.configure( properties ); Logger logger = Logger.getLogger( SerialWrapper.class ); logger.info( "SerialWrapper Test Started" ); SerialWrapper serialWrapper = new SerialWrapper( ); ArrayList < KeyValueImp > predicates = new ArrayList < KeyValueImp >( ); predicates.add( new KeyValueImp( "serialport" , "/dev/ttyUSB0" ) ); predicates.add( new KeyValueImp( "inputseparator" , "(\n|\r|\f)" ) ); predicates.add( new KeyValueImp( "baudrate" , "57600" ) ); predicates.add( new KeyValueImp( "flowcontrolmode" , "FLOWCONTROL_NONE" ) ); predicates.add( new KeyValueImp( "databits" , "8" ) ); predicates.add( new KeyValueImp( "stopbits" , "1" ) ); predicates.add( new KeyValueImp( "parity" , "PARITY_NONE" ) ); predicates.add( new KeyValueImp( "host" , "localhost" ) ); predicates.add( new KeyValueImp( "port" , "22001" ) ); serialWrapper.setActiveAddressBean( new AddressBean( "SerialWrapper" , predicates.toArray(new KeyValueImp[] {}) ) ); if ( !serialWrapper.initialize( ) ) { System.out.println( "initialization failed\n" ); } else { System.out.println( "initialization successful\n" ); } } public String getWrapperName() { return "serial port wrapper rs-232"; } }