]> git.basschouten.com Git - openhab-addons.git/blob
a2a172e6c11dfb8e248e197c6577ef588f807b02
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.powermax.internal.connector;
14
15 import java.io.IOException;
16 import java.io.InterruptedIOException;
17 import java.util.Arrays;
18
19 import org.openhab.binding.powermax.internal.message.PowermaxCommManager;
20 import org.openhab.binding.powermax.internal.message.PowermaxReceiveType;
21 import org.openhab.core.util.HexUtils;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 /**
26  * A class that reads messages from the Visonic alarm panel in a dedicated thread
27  *
28  * @author Laurent Garnier - Initial contribution
29  */
30 public class PowermaxReaderThread extends Thread {
31
32     private final Logger logger = LoggerFactory.getLogger(PowermaxReaderThread.class);
33
34     private static final int READ_BUFFER_SIZE = 20;
35     private static final int MAX_MSG_SIZE = 0xC0;
36
37     private PowermaxConnector connector;
38
39     /**
40      * Constructor
41      *
42      * @param in the input stream
43      * @param connector the object that should handle the received message
44      * @param threadName the name of the thread
45      */
46     public PowermaxReaderThread(PowermaxConnector connector, String threadName) {
47         super(threadName);
48         this.connector = connector;
49     }
50
51     @Override
52     public void run() {
53         logger.debug("Data listener started");
54
55         byte[] readDataBuffer = new byte[READ_BUFFER_SIZE];
56         byte[] dataBuffer = new byte[MAX_MSG_SIZE];
57         int index = 0;
58         int msgLen = 0;
59         boolean variableLen = false;
60
61         try {
62             while (!Thread.interrupted()) {
63                 int len = connector.read(readDataBuffer);
64                 if (len > 0) {
65                     for (int i = 0; i < len; i++) {
66                         if (index >= MAX_MSG_SIZE) {
67                             // too many bytes received, try to find new start
68                             if (logger.isDebugEnabled()) {
69                                 byte[] logData = Arrays.copyOf(dataBuffer, index);
70                                 logger.debug("Truncating message {}", HexUtils.bytesToHex(logData));
71                             }
72                             index = 0;
73                         }
74
75                         if (index == 0 && readDataBuffer[i] == 0x0D) {
76                             // Preamble
77
78                             dataBuffer[index++] = readDataBuffer[i];
79                         } else if (index > 0) {
80                             dataBuffer[index++] = readDataBuffer[i];
81
82                             if (index == 2) {
83                                 try {
84                                     PowermaxReceiveType msgType = PowermaxReceiveType.fromCode(readDataBuffer[i]);
85                                     msgLen = msgType.getLength();
86                                     variableLen = ((readDataBuffer[i] & 0x000000FF) > 0x10) && (msgLen == 0);
87                                 } catch (IllegalArgumentException arg0) {
88                                     msgLen = 0;
89                                     variableLen = false;
90                                 }
91                             } else if (index == 5 && variableLen) {
92                                 msgLen = (readDataBuffer[i] & 0x000000FF) + 7;
93                             } else if ((msgLen == 0 && readDataBuffer[i] == 0x0A) || (index == msgLen)) {
94                                 // Postamble
95
96                                 if (readDataBuffer[i] != 0x0A && dataBuffer[index - 1] == 0x43) {
97                                     // adjust message length for 0x43
98                                     msgLen++;
99                                 } else if (checkCRC(dataBuffer, index)) {
100                                     // whole message received with a right CRC
101
102                                     byte[] msg = new byte[index];
103                                     msg = Arrays.copyOf(dataBuffer, index);
104
105                                     connector.setWaitingForResponse(System.currentTimeMillis());
106                                     connector.handleIncomingMessage(msg);
107
108                                     // find new preamble
109                                     index = 0;
110                                 } else if (msgLen == 0) {
111                                     // CRC check failed for a message with an unknown length
112                                     logger.debug("Message length is now {} but message is apparently not complete",
113                                             index + 1);
114                                 } else {
115                                     // CRC check failed for a message with a known length
116
117                                     connector.setWaitingForResponse(System.currentTimeMillis());
118
119                                     // find new preamble
120                                     index = 0;
121                                 }
122                             }
123                         }
124                     }
125                 }
126             }
127         } catch (InterruptedIOException e) {
128             Thread.currentThread().interrupt();
129             logger.debug("Interrupted via InterruptedIOException");
130         } catch (IOException e) {
131             logger.debug("Reading failed: {}", e.getMessage(), e);
132         }
133
134         logger.debug("Data listener stopped");
135     }
136
137     /**
138      * Check if the CRC inside a received message is valid or not
139      *
140      * @param data the buffer containing the message
141      * @param len the size of the message in the buffer
142      *
143      * @return true if the CRC is valid or false if not
144      */
145     private boolean checkCRC(byte[] data, int len) {
146         byte checksum = PowermaxCommManager.computeCRC(data, len);
147         byte expected = data[len - 2];
148         if (checksum != expected) {
149             byte[] logData = Arrays.copyOf(data, len);
150             logger.warn("Powermax alarm binding: message CRC check failed (expected {}, got {}, message {})",
151                     String.format("%02X", expected), String.format("%02X", checksum), HexUtils.bytesToHex(logData));
152         }
153         return (checksum == expected);
154     }
155 }