nz.co.fortytwo.freeboard.server.SerialPortReader.java Source code

Java tutorial

Introduction

Here is the source code for nz.co.fortytwo.freeboard.server.SerialPortReader.java

Source

/*
 * Copyright 2012,2013 Robert Huitema robert@42.co.nz
 *
 * This file is part of FreeBoard. (http://www.42.co.nz/freeboard)
 *
 * FreeBoard 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 Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FreeBoard 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 FreeBoard. If not, see <http://www.gnu.org/licenses/>.
 */
package nz.co.fortytwo.freeboard.server;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import nz.co.fortytwo.freeboard.server.util.Constants;
import nz.co.fortytwo.freeboard.server.util.Util;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.ProducerTemplate;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.apache.log4j.Logger;

import purejavacomm.CommPortIdentifier;
import purejavacomm.SerialPort;
import purejavacomm.SerialPortEvent;
import purejavacomm.SerialPortEventListener;

/**
 * Wrapper to read serial port via rxtx, then fire messages into the camel route
 * via the seda queue.
 *
 * @author robert
 *
 */
public class SerialPortReader implements Processor {

    private static Logger logger = Logger.getLogger(SerialPortReader.class);
    private String portName;
    private File portFile;
    private ProducerTemplate producer;

    private boolean running = true;
    private boolean mapped = false;
    private String deviceType = null;
    private SerialPort serialPort = null;

    private LinkedBlockingQueue<String> queue;
    private SerialReader serialReader;

    public SerialPortReader() {
        super();
        queue = new LinkedBlockingQueue<String>(100);
    }

    /**
     * Opens a connection to the serial port, and starts two threads, one to read, one to write.
     * A background thread looks for new/lost USB devices and (re)attaches them
     *
     *
     * @param portName
     * @throws Exception
     */
    void connect(String portName, int baudRate) throws Exception {
        this.portName = portName;
        if (!SystemUtils.IS_OS_WINDOWS) {
            this.portFile = new File(portName);
        }
        CommPortIdentifier portid = CommPortIdentifier.getPortIdentifier(portName);
        serialPort = (SerialPort) portid.open("FreeboardSerialReader", 100);
        //TODO: change baud rate to config based setup
        serialPort.setSerialPortParams(baudRate, 8, 1, 0);
        serialReader = new SerialReader();
        serialPort.enableReceiveTimeout(1000);
        serialPort.notifyOnDataAvailable(true);
        serialPort.addEventListener(serialReader);
        //(new Thread(new SerialReader())).start();
        (new Thread(new SerialWriter())).start();

    }

    public class SerialWriter implements Runnable {

        BufferedOutputStream out;

        public SerialWriter() throws Exception {

            this.out = new BufferedOutputStream(serialPort.getOutputStream());

        }

        public void run() {

            try {
                while (running) {
                    String msg = queue.poll(5, TimeUnit.SECONDS);
                    if (StringUtils.isNotBlank(msg)) {
                        out.write((msg + "\r\n").getBytes());
                        out.flush();
                    }
                }
            } catch (IOException e) {
                running = false;
                logger.error(portName + ":" + e.getMessage());
                if (logger.isDebugEnabled())
                    logger.debug(e.getMessage(), e);
            } catch (InterruptedException e) {
                // do nothing
            }
        }

    }

    /** */
    public class SerialReader implements SerialPortEventListener {

        //BufferedReader in;

        private Pattern uid;
        List<String> lines = new ArrayList<String>();
        StringBuffer line = new StringBuffer(60);
        private boolean sendMessage = true;
        private boolean complete;
        private InputStream in;
        byte[] buff = new byte[256];
        int x = 0;

        public SerialReader() throws Exception {

            //this.in = new BufferedReader(new InputStreamReader(serialPort.getInputStream()));
            this.in = new BufferedInputStream(serialPort.getInputStream());
            uid = Pattern.compile(Constants.UID + ":");
            logger.info("Setup serialReader on :" + portName);
            sendMessage = new Boolean(Util.getConfig(null).getProperty(Constants.SEND_MESSAGE, "true"));
        }

        //@Override
        public void serialEvent(SerialPortEvent event) {
            if (logger.isTraceEnabled())
                logger.trace("SerialEvent:" + event.getEventType());
            try {
                if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {

                    int r = 0;

                    while ((r > -1) && in.available() > 0) {
                        try {
                            r = in.read();
                            buff[x] = (byte) r;
                            x++;

                            //10=LF, 13=CR, lines should end in CR/LF
                            if (r == 10 || x == 256) {
                                if (r == 10)
                                    complete = true;
                                line.append(new String(buff));
                                buff = new byte[256];
                                x = 0;
                            }

                        } catch (IOException e) {
                            logger.error(portName + ":" + e.getMessage());
                            if (logger.isDebugEnabled())
                                logger.debug(e.getMessage(), e);
                            return;
                        }
                        //we have a line ending in CR/LF
                        if (complete) {
                            String lineStr = line.toString().trim();
                            if (logger.isDebugEnabled())
                                logger.debug(portName + ":Serial Received:" + lineStr);

                            // enable the lines below to substitute SDDBT sentence for GGA sentence for testing Depth ionstrument
                            //                        if (lineStr.contains("GGA")){
                            //                           lineStr = "$SDDBT,8.1,f,2.4,M,1.3,F*0B";
                            //                        }

                            //its not empty!
                            if (lineStr.length() > 0) {
                                //map it if we havent already
                                if (!mapped && uid.matcher(lineStr).matches()) {
                                    // add to map
                                    if (logger.isDebugEnabled())
                                        logger.debug(portName + ":Serial Received:" + lineStr);
                                    String type = StringUtils.substringBetween(lineStr, Constants.UID + ":", ",");
                                    if (type != null) {
                                        if (logger.isDebugEnabled())
                                            logger.debug(portName + ":  device name:" + type);
                                        deviceType = type.trim();
                                        mapped = true;
                                    }
                                }
                                if (sendMessage) {
                                    producer.sendBody(lineStr);
                                } else {
                                    if (logger.isDebugEnabled())
                                        logger.debug("sendMessage false:" + lineStr);
                                }
                            }
                            complete = false;
                            line = new StringBuffer(60);
                        }
                    }
                }
            } catch (Exception e) {
                running = false;
                logger.error(portName, e);
            }

        }

    }

    /** */

    /**
     * Set the camel producer, which fire the messages into camel
     *
     * @param producer
     */
    public void setProducer(ProducerTemplate producer) {
        this.producer = producer;

    }

    /**
     * True if the serial port read/write threads are running
     *
     * @return
     */
    public boolean isRunning() {
        //no good on windoze
        if (!SystemUtils.IS_OS_WINDOWS && !portFile.exists()) {

            try {
                serialPort.close();
                serialPort.removeEventListener();
            } catch (Exception e) {
                logger.error("Problem disconnecting port " + portName + ", " + e.getMessage());
                if (logger.isDebugEnabled())
                    logger.debug(e.getMessage(), e);
            }
            running = false;
        }
        return running;
    }

    /**
     * Set to false to stop the serial port read/write threads.
     * You must connect() to restart.
     *
     * @param running
     */
    public void setRunning(boolean running) {
        this.running = running;
    }

    public String getPortName() {
        return portName;
    }

    public void setPortName(String portName) {
        this.portName = portName;
    }

    /*
     * Handles the messages to be delivered to the device attached to this port.
     *
     * @see org.apache.camel.Processor#process(org.apache.camel.Exchange)
     */
    public void process(Exchange exchange) throws Exception {
        // send to device
        String message = exchange.getIn().getBody(String.class);
        if (logger.isDebugEnabled())
            logger.debug(portName + ":msg received for device:" + message);
        if (StringUtils.isNotBlank(message)) {
            // check its valid for this device
            if (running && deviceType == null || message.contains(Constants.UID + ":" + deviceType)) {
                if (logger.isDebugEnabled())
                    logger.debug(portName + ":wrote out to device:" + message);
                // queue them and write in background
                if (!queue.offer(message)) {
                    if (logger.isDebugEnabled())
                        logger.debug("Output queue id full for " + portName);
                }
            }
        }
    }

}