/**
* Copyright (c) 2010-2016 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.smarthomatic.internal;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.TooManyListenersException;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;
/**
* directly invokes and controll's the serial communication
*
* @author mcjobo
* @since 1.9.0
*/
public class SerialDevice implements SerialPortEventListener {
private static final Logger logger = LoggerFactory.getLogger(SerialDevice.class);
private static int DEFAULT_PORT = 2000;
private String port;
private int baud;
private SerialEventWorker eventWorker;
private CommPortIdentifier portId;
private SerialPort serialPort;
private InputStream inputStream;
private OutputStream outputStream;
/**
* constructor to create a new serial device with the given port and baud
* rate
*
* @param port
* @param baud
*/
public SerialDevice(String port, int baud) {
this.port = port;
this.baud = baud;
}
/**
* setter for the used event worker
*
* @param eventWorker
*/
public void setEventWorker(SerialEventWorker eventWorker) {
this.eventWorker = eventWorker;
}
/**
* resets the event worker to null
*
*/
public void unsetEventWorker() {
this.eventWorker = null;
}
/**
* getter to return the used port
*
* @return
*/
public String getPort() {
return port;
}
/**
* Initialize this device and open the serial port
*
* @throws InitializationException
* if port can not be opened
*/
public void initialize() throws InitializationException {
// parse ports and if the default port is found, initialized the reader
Enumeration<?> portList = CommPortIdentifier.getPortIdentifiers();
while (portList.hasMoreElements()) {
CommPortIdentifier id = (CommPortIdentifier) portList.nextElement();
if (id.getPortType() == CommPortIdentifier.PORT_SERIAL) {
if (id.getName().equals(port)) {
logger.debug("Serial port '{}' has been found.", port);
portId = id;
}
}
}
if (portId != null) {
// initialize serial port
try {
serialPort = portId.open("openHAB-Smarthomatic", DEFAULT_PORT);
} catch (PortInUseException e) {
throw new InitializationException(e);
}
try {
inputStream = serialPort.getInputStream();
} catch (IOException e) {
throw new InitializationException(e);
}
try {
serialPort.addEventListener(this);
} catch (TooManyListenersException e) {
throw new InitializationException(e);
}
// activate the DATA_AVAILABLE notifier
serialPort.notifyOnDataAvailable(true);
try {
// set port parameters
serialPort.setSerialPortParams(baud, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
} catch (UnsupportedCommOperationException e) {
throw new InitializationException(e);
}
try {
// get the output stream
outputStream = serialPort.getOutputStream();
} catch (IOException e) {
throw new InitializationException(e);
}
} else {
StringBuilder sb = new StringBuilder();
portList = CommPortIdentifier.getPortIdentifiers();
while (portList.hasMoreElements()) {
CommPortIdentifier id = (CommPortIdentifier) portList.nextElement();
if (id.getPortType() == CommPortIdentifier.PORT_SERIAL) {
sb.append(id.getName() + "\n");
}
}
throw new InitializationException(
"Serial port '" + port + "' could not be found. Available ports are:\n" + sb.toString());
}
}
/**
* method called if new data is available from the serial device
*
*/
@Override
public 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
StringBuilder sb = new StringBuilder();
byte[] readBuffer = new byte[100];
try {
do {
// read data from serial device
while (inputStream.available() > 0) {
int bytes = inputStream.read(readBuffer);
sb.append(new String(readBuffer, 0, bytes));
}
try {
// add wait states around reading the stream, so that
// interrupted transmissions are merged
Thread.sleep(100);
} catch (InterruptedException e) {
// ignore interruption
}
} while (inputStream.available() > 0);
// sent data
String result = sb.toString();
String logResult = result.replaceAll("\n", "\\\\n").replaceAll("\r", "\\\\r").substring(0, 40);
// send data to the bus
logger.info("Received message '{}'... on serial port {}", new String[] { logResult, port });
if (eventWorker != null) {
eventWorker.eventOccured(result);
}
} catch (IOException e) {
logger.debug("Error receiving data on serial port {}: {}", new String[] { port, e.getMessage() });
}
break;
}
}
/**
* Sends a string to the serial port of this device
*
* @param msg
* the string to send
*/
public void writeString(String msg) {
logger.info("Writing '{}' to serial port {}", new String[] { msg, port });
try {
// write string to serial port
outputStream.write(msg.getBytes());
outputStream.flush();
} catch (IOException e) {
logger.error("Error writing '{}' to serial port {}: {}", new String[] { msg, port, e.getMessage() });
}
}
/**
* Close this serial device
*
*/
public void close() {
serialPort.removeEventListener();
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
serialPort.close();
}
}