2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.elerotransmitterstick.internal.stick;
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.TooManyListenersException;
20 import org.apache.commons.lang.ArrayUtils;
21 import org.openhab.core.io.transport.serial.PortInUseException;
22 import org.openhab.core.io.transport.serial.SerialPort;
23 import org.openhab.core.io.transport.serial.SerialPortEvent;
24 import org.openhab.core.io.transport.serial.SerialPortEventListener;
25 import org.openhab.core.io.transport.serial.SerialPortIdentifier;
26 import org.openhab.core.io.transport.serial.SerialPortManager;
27 import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
32 * @author Volker Bier - Initial contribution
34 public class SerialConnection {
35 private final Logger logger = LoggerFactory.getLogger(SerialConnection.class);
37 private SerialPort serialPort;
39 private String portName;
40 private final List<Byte> bytes = new ArrayList<>();
41 private Response response = null;
42 private final SerialPortManager serialPortManager;
44 public SerialConnection(String portName, SerialPortManager serialPortManager) {
45 this.portName = portName;
46 this.serialPortManager = serialPortManager;
49 public synchronized void open() throws ConnectException {
52 logger.debug("Trying to open serial connection to port {}...", portName);
54 SerialPortIdentifier portIdentifier = serialPortManager.getIdentifier(portName);
55 if (portIdentifier == null) {
56 throw new ConnectException("No such port: " + portName);
60 serialPort = portIdentifier.open("openhab", 3000);
62 logger.debug("Serial connection to port {} opened.", portName);
64 serialPort.setSerialPortParams(38400, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
65 SerialPort.PARITY_NONE);
67 serialPort.addEventListener(new SerialPortEventListener() {
69 public void serialEvent(SerialPortEvent event) {
71 if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
74 } catch (IOException ex) {
75 logger.warn("IOException reading from port {}!", portName);
80 serialPort.notifyOnDataAvailable(true);
81 } catch (UnsupportedCommOperationException | TooManyListenersException ex) {
83 throw new ConnectException(ex);
86 } catch (PortInUseException ex) {
87 throw new ConnectException(ex);
91 public synchronized boolean isOpen() {
95 public synchronized void close() {
97 logger.debug("Closing serial connection to port {}...", portName);
99 serialPort.notifyOnDataAvailable(false);
100 serialPort.removeEventListener();
106 // send a packet to the stick and wait for the response
107 public synchronized Response sendPacket(CommandPacket p) throws IOException {
109 Response r = response;
111 synchronized (bytes) {
113 logger.debug("Writing packet to stick: {}", p);
114 serialPort.getOutputStream().write(p.getBytes());
120 final long responseTimeout = p.getResponseTimeout();
122 logger.trace("Waiting {} ms for answer from stick...", responseTimeout);
123 bytes.wait(responseTimeout);
124 } catch (InterruptedException e) {
125 Thread.currentThread().interrupt();
132 logger.debug("Stick answered {} for packet {}.", r, p);
136 logger.warn("Stick skipped packet {}. Connection is not open.", p);
140 private void parseInput() throws IOException {
141 logger.trace("parsing input...");
142 while (serialPort.getInputStream().available() > 0) {
143 byte b = (byte) serialPort.getInputStream().read();
146 logger.trace("input parsed. buffer contains {} bytes.", bytes.size());
150 private void analyzeBuffer() {
151 // drop everything before the beginning of the packet header 0xAA
152 while (!bytes.isEmpty() && bytes.get(0) != (byte) 0xAA) {
153 logger.trace("dropping byte {} from buffer", bytes.get(0));
157 if (logger.isTraceEnabled()) {
158 logger.trace("buffer contains {} bytes: {}", bytes.size(),
159 ArrayUtils.toPrimitive(bytes.toArray(new Byte[bytes.size()])));
161 if (bytes.size() > 1) {
162 // second byte should be length byte (has to be either 0x04 or 0x05)
163 int len = bytes.get(1);
164 logger.trace("packet length is {} bytes.", len);
166 if (len != 4 && len != 5) {
167 // invalid length, drop packet
170 } else if (bytes.size() > len + 1) {
171 // we have a complete packet in the buffer, analyze it
172 // third byte should be response type byte (has to be either EASY_CONFIRM or EASY_ACK)
173 byte respType = bytes.get(2);
175 synchronized (bytes) {
176 if (respType == ResponseStatus.EASY_CONFIRM) {
177 logger.trace("response type is EASY_CONFIRM.");
179 long val = bytes.get(0) + bytes.get(1) + bytes.get(2) + bytes.get(3) + bytes.get(4)
181 if (val % 256 == 0) {
182 response = ResponseUtil.createResponse(bytes.get(3), bytes.get(4));
184 logger.warn("invalid response checksum. Skipping response.");
188 } else if (respType == ResponseStatus.EASY_ACK) {
189 logger.trace("response type is EASY_ACK.");
191 long val = bytes.get(0) + bytes.get(1) + bytes.get(2) + bytes.get(3) + bytes.get(4)
192 + bytes.get(5) + bytes.get(6);
193 if (val % 256 == 0) {
194 response = ResponseUtil.createResponse(bytes.get(3), bytes.get(4), bytes.get(5));
196 logger.warn("invalid response checksum. Skipping response.");
201 logger.warn("invalid response type {}. Skipping response.", respType);