]> git.basschouten.com Git - openhab-addons.git/blob
465d92862ab6215607f800e102c71ea3f34f2d85
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.elerotransmitterstick.internal.stick;
14
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.TooManyListenersException;
19
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;
30
31 /**
32  * @author Volker Bier - Initial contribution
33  */
34 public class SerialConnection {
35     private final Logger logger = LoggerFactory.getLogger(SerialConnection.class);
36
37     private SerialPort serialPort;
38     private boolean open;
39     private String portName;
40     private final List<Byte> bytes = new ArrayList<>();
41     private Response response = null;
42     private final SerialPortManager serialPortManager;
43
44     public SerialConnection(String portName, SerialPortManager serialPortManager) {
45         this.portName = portName;
46         this.serialPortManager = serialPortManager;
47     }
48
49     public synchronized void open() throws ConnectException {
50         try {
51             if (!open) {
52                 logger.debug("Trying to open serial connection to port {}...", portName);
53
54                 SerialPortIdentifier portIdentifier = serialPortManager.getIdentifier(portName);
55                 if (portIdentifier == null) {
56                     throw new ConnectException("No such port: " + portName);
57                 }
58
59                 try {
60                     serialPort = portIdentifier.open("openhab", 3000);
61                     open = true;
62                     logger.debug("Serial connection to port {} opened.", portName);
63
64                     serialPort.setSerialPortParams(38400, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
65                             SerialPort.PARITY_NONE);
66
67                     serialPort.addEventListener(new SerialPortEventListener() {
68                         @Override
69                         public void serialEvent(SerialPortEvent event) {
70                             try {
71                                 if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
72                                     parseInput();
73                                 }
74                             } catch (IOException ex) {
75                                 logger.warn("IOException reading from port {}!", portName);
76                             }
77                         }
78                     });
79
80                     serialPort.notifyOnDataAvailable(true);
81                 } catch (UnsupportedCommOperationException | TooManyListenersException ex) {
82                     close();
83                     throw new ConnectException(ex);
84                 }
85             }
86         } catch (PortInUseException ex) {
87             throw new ConnectException(ex);
88         }
89     }
90
91     public synchronized boolean isOpen() {
92         return open;
93     }
94
95     public synchronized void close() {
96         if (open) {
97             logger.debug("Closing serial connection to port {}...", portName);
98
99             serialPort.notifyOnDataAvailable(false);
100             serialPort.removeEventListener();
101             serialPort.close();
102             open = false;
103         }
104     }
105
106     // send a packet to the stick and wait for the response
107     public synchronized Response sendPacket(CommandPacket p) throws IOException {
108         if (open) {
109             Response r = response;
110
111             synchronized (bytes) {
112                 response = null;
113                 logger.debug("Writing packet to stick: {}", p);
114                 serialPort.getOutputStream().write(p.getBytes());
115
116                 if (r != null) {
117                     return r;
118                 }
119
120                 final long responseTimeout = p.getResponseTimeout();
121                 try {
122                     logger.trace("Waiting {} ms for answer from stick...", responseTimeout);
123                     bytes.wait(responseTimeout);
124                 } catch (InterruptedException e) {
125                     Thread.currentThread().interrupt();
126                 }
127
128                 r = response;
129                 response = null;
130             }
131
132             logger.debug("Stick answered {} for packet {}.", r, p);
133             return r;
134         }
135
136         logger.warn("Stick skipped packet {}. Connection is not open.", p);
137         return null;
138     }
139
140     private void parseInput() throws IOException {
141         logger.trace("parsing input...");
142         while (serialPort.getInputStream().available() > 0) {
143             byte b = (byte) serialPort.getInputStream().read();
144             bytes.add(b);
145         }
146         logger.trace("input parsed. buffer contains {} bytes.", bytes.size());
147         analyzeBuffer();
148     }
149
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));
154             bytes.remove(0);
155         }
156
157         if (logger.isTraceEnabled()) {
158             logger.trace("buffer contains {} bytes: {}", bytes.size(),
159                     ArrayUtils.toPrimitive(bytes.toArray(new Byte[bytes.size()])));
160         }
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);
165
166             if (len != 4 && len != 5) {
167                 // invalid length, drop packet
168                 bytes.remove(0);
169                 analyzeBuffer();
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);
174
175                 synchronized (bytes) {
176                     if (respType == ResponseStatus.EASY_CONFIRM) {
177                         logger.trace("response type is EASY_CONFIRM.");
178
179                         long val = bytes.get(0) + bytes.get(1) + bytes.get(2) + bytes.get(3) + bytes.get(4)
180                                 + bytes.get(5);
181                         if (val % 256 == 0) {
182                             response = ResponseUtil.createResponse(bytes.get(3), bytes.get(4));
183                         } else {
184                             logger.warn("invalid response checksum. Skipping response.");
185                         }
186
187                         bytes.notify();
188                     } else if (respType == ResponseStatus.EASY_ACK) {
189                         logger.trace("response type is EASY_ACK.");
190
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));
195                         } else {
196                             logger.warn("invalid response checksum. Skipping response.");
197                         }
198
199                         bytes.notify();
200                     } else {
201                         logger.warn("invalid response type {}. Skipping response.", respType);
202                     }
203                 }
204
205                 bytes.remove(0);
206                 analyzeBuffer();
207             }
208         }
209     }
210 }