ch.wir_entwickeln.yavanna.serialport.SerialPortCCI.java Source code

Java tutorial

Introduction

Here is the source code for ch.wir_entwickeln.yavanna.serialport.SerialPortCCI.java

Source

/**
 * openHAB, the open Home Automation Bus.
 * Copyright (C) 2010-2012, openHAB.org <admin@openhab.org>
 *
 * See the contributors.txt file in the distribution for a
 * full listing of individual contributors.
 *
 * 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 Foundation; either version 3 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, see <http://www.gnu.org/licenses>.
 *
 * Additional permission under GNU GPL version 3 section 7
 *
 * If you modify this Program, or any covered work, by linking or
 * combining it with Eclipse (or a modified version of that library),
 * containing parts covered by the terms of the Eclipse Public License
 * (EPL), the licensors of this Program grant you additional permission
 * to convey the resulting work.
 */
package ch.wir_entwickeln.yavanna.serialport;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.TooManyListenersException;

import org.apache.commons.io.IOUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * This class represents a serial port.
 * 
 * @author Kai Kreuzer
 * @author Ronny Stauffer
 *
 */
public class SerialPortCCI implements SerialPortEventListener {
    public static final int DEFAULT_BAUD_RATE = 9600;
    public static final DataBits DEFAULT_DATA_BITS = DataBits.EIGHT;
    public static final StopBits DEFAULT_STOP_BITS = StopBits.ONE;
    public static final Parity DEFAULT_PARITY = Parity.NONE;

    public enum DataBits {
        SEVEN(SerialPort.DATABITS_7), EIGHT(SerialPort.DATABITS_8);

        private final int numberOfDataBits;

        private DataBits(int numberOfDataBits) {
            assert numberOfDataBits == SerialPort.DATABITS_7 || numberOfDataBits == SerialPort.DATABITS_8;

            this.numberOfDataBits = numberOfDataBits;
        }

        public int getNumberOfDataBits() {
            return numberOfDataBits;
        }
    }

    public enum StopBits {
        ONE(SerialPort.STOPBITS_1), TWO(SerialPort.STOPBITS_2);

        private final int numberOfStopBits;

        private StopBits(int numberOfStopBits) {
            assert numberOfStopBits == SerialPort.STOPBITS_1 || numberOfStopBits == SerialPort.STOPBITS_2;

            this.numberOfStopBits = numberOfStopBits;
        }

        public int getNumberOfStopBits() {
            return numberOfStopBits;
        }
    }

    public enum Parity {
        NONE(SerialPort.PARITY_NONE), EVEN(SerialPort.PARITY_EVEN), ODD(SerialPort.PARITY_ODD);

        private final int parity;

        private Parity(int parity) {
            assert parity == SerialPort.PARITY_NONE || parity == SerialPort.PARITY_EVEN
                    || parity == SerialPort.PARITY_ODD;

            this.parity = parity;
        }

        public int getParity() {
            return parity;
        }
    }

    private static final Logger logger = LoggerFactory.getLogger(SerialPortCCI.class);

    private final String portName;
    private final int baudRate;
    private final DataBits dataBits;
    private final StopBits stopBits;
    private final Parity parity;

    private SerialPort serialPort;

    private InputStream inputStream;
    private OutputStream outputStream;

    // To keep track of all data receivers
    protected Collection<SerialPortDataReceiver> dataReceivers = Collections
            .synchronizedSet(new HashSet<SerialPortDataReceiver>());

    public SerialPortCCI(String portName) {
        this(portName, DEFAULT_BAUD_RATE);
    }

    public SerialPortCCI(String portName, int baudRate) {
        this(portName, baudRate, DEFAULT_DATA_BITS, DEFAULT_STOP_BITS, DEFAULT_PARITY);
    }

    public SerialPortCCI(String portName, int baudRate, DataBits dataBits, StopBits stopBits, Parity parity) {
        if (portName == null || portName.isEmpty()) {
            throw new IllegalArgumentException("portName must not be undefined or empty!");
        }
        if (!(baudRate > 0)) {
            throw new IllegalArgumentException("baudRate must be > 0!");
        }
        if (dataBits == null) {
            throw new NullPointerException("dataBits must not be undefined!");
        }
        if (stopBits == null) {
            throw new NullPointerException("stopBits must not be undefined!");
        }
        if (parity == null) {
            throw new NullPointerException("parity must not be undefined!");
        }

        this.portName = portName;
        this.baudRate = baudRate;
        this.dataBits = dataBits;
        this.stopBits = stopBits;
        this.parity = parity;
    }

    public String getPort() {
        return portName;
    }

    public void addDataReceiver(SerialPortDataReceiver dataReceiver) {
        dataReceivers.add(dataReceiver);

        logger.debug("Data receiver have been added.");
    }

    public void removeDataReceiver(SerialPortDataReceiver dataReceiver) {
        dataReceivers.remove(dataReceiver);
    }

    /**
     * Initialize (= opens) the serial port.
     * 
     * @throws SerialPortInitializationException If the serial port cannot be opened.
     */
    @SuppressWarnings("rawtypes")
    public synchronized void initialize() throws SerialPortInitializationException {
        // Iterate over all available ports and if the requested port is found, initialize it
        CommPortIdentifier requestedPortID = null;
        Enumeration portIDList = CommPortIdentifier.getPortIdentifiers();
        while (portIDList.hasMoreElements()) {
            CommPortIdentifier portID = (CommPortIdentifier) portIDList.nextElement();
            if (portID.getPortType() == CommPortIdentifier.PORT_SERIAL) {
                if (portID.getName().equals(portName)) {
                    logger.debug("Serial port {} has been found.", portName);

                    requestedPortID = portID;
                }
            }
        }
        if (requestedPortID != null) {
            // Open the serial port
            try {
                serialPort = (SerialPort) requestedPortID.open("openHAB", 2000);
            } catch (PortInUseException e) {
                throw new SerialPortInitializationException(e);
            }

            try {
                try {
                    // Set port parameters
                    serialPort.setSerialPortParams(baudRate, dataBits.getNumberOfDataBits(),
                            stopBits.getNumberOfStopBits(), parity.getParity());
                } catch (UnsupportedCommOperationException e) {
                    throw new SerialPortInitializationException(e);
                }

                // Get the input stream
                try {
                    inputStream = serialPort.getInputStream();
                } catch (IOException e) {
                    throw new SerialPortInitializationException(e);
                }

                // Get the output stream
                try {
                    outputStream = serialPort.getOutputStream();
                } catch (IOException e) {
                    throw new SerialPortInitializationException(e);
                }

                try {
                    serialPort.addEventListener(this);
                } catch (TooManyListenersException e) {
                    throw new SerialPortInitializationException(e);
                }

                // Activate the DATA_AVAILABLE notifier
                serialPort.notifyOnDataAvailable(true);
            } catch (SerialPortInitializationException e) {
                serialPort.close();
                serialPort = null;
            }
        } else {
            StringBuilder availablePorts = new StringBuilder();
            Enumeration portIDList2 = CommPortIdentifier.getPortIdentifiers();
            while (portIDList2.hasMoreElements()) {
                CommPortIdentifier portID2 = (CommPortIdentifier) portIDList2.nextElement();
                if (portID2.getPortType() == CommPortIdentifier.PORT_SERIAL) {
                    availablePorts.append(String.format("%s\n", portID2.getName()));
                }
            }
            throw new SerialPortInitializationException(
                    String.format("Serial port %s could not be found. Available ports are:\n%s", portName,
                            availablePorts.toString()));
        }
    }

    private static final int INPUT_BUFFER_SIZE = 1024;

    public synchronized void serialEvent(SerialPortEvent event) {
        switch (event.getEventType()) {
        case SerialPortEvent.BI:
        case SerialPortEvent.OE:
        case SerialPortEvent.FE:
        case SerialPortEvent.PE:
        case SerialPortEvent.CD:
        case SerialPortEvent.CTS:
        case SerialPortEvent.DSR:
        case SerialPortEvent.RI:
        case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
            break;
        case SerialPortEvent.DATA_AVAILABLE:
            // We get here if data has been received

            // Read data from serial port buffer
            int numberOfBytesRead = 0;
            byte[] inputBuffer = new byte[INPUT_BUFFER_SIZE];
            try {
                while (inputStream.available() > 0) {
                    numberOfBytesRead += inputStream.read(inputBuffer, numberOfBytesRead,
                            INPUT_BUFFER_SIZE - numberOfBytesRead);
                }
                IOUtils.closeQuietly(inputStream);
            } catch (IOException e) {
                logger.debug("Error receiving data on serial port {}: {}", portName, e.getMessage());
            }

            byte[] data = new byte[INPUT_BUFFER_SIZE];
            int i = 0;
            for (byte inputBufferByte : inputBuffer) {
                if ((inputBufferByte & 0x80) > 0) {
                    data[i++] = inputBufferByte;
                }
            }

            byte[] processBuffer = Arrays.copyOf(data, i);
            for (SerialPortDataReceiver dataReceiver : dataReceivers) {
                dataReceiver.processReceived(processBuffer);
            }

            break;
        }
    }

    /**
     * Sends data on the serial port.
     * 
     * @param data The data to send.
     */
    public synchronized void send(byte[] data) {
        String formattedData = new String(data).replaceAll(Character.toString((char) 13), "<CR>");
        logger.debug("Sending '{}' on serial port {}...", new String[] { formattedData, portName });
        try {
            // Write data to serial port buffer
            for (byte dataByte : data) {
                outputStream.write(dataByte);
                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    // Ignore exception
                }
            }
            outputStream.flush();
        } catch (IOException e) {
            logger.error("Error sending data on serial port {}: {}", portName, e.getMessage());
        }
    }

    /**
     * Closes the serial port.
     */
    public synchronized void close() {
        serialPort.removeEventListener();

        try {
            inputStream.close();
        } catch (IOException e) {
            logger.warn("Error closing serial port {}!", portName);
        }
        try {
            outputStream.close();
        } catch (IOException e) {
            logger.warn("Error closing serial port {}!", portName);
        }

        serialPort.close();
        serialPort = null;
    }
}