/** * 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.powermax.internal.connector; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.util.Arrays; import javax.xml.bind.DatatypeConverter; import org.openhab.binding.powermax.internal.message.PowerMaxCommDriver; import org.openhab.binding.powermax.internal.message.PowerMaxReceiveType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A class that reads messages from the Visonic alarm panel in a dedicated thread * * @author Laurent Garnier * @since 1.9.0 */ public class PowerMaxReaderThread extends Thread { private static final Logger logger = LoggerFactory.getLogger(PowerMaxReaderThread.class); private InputStream in; private PowerMaxConnector connector; private boolean interrupted; /** * Constructor * * @param in * the input stream * @param connector * the object that should handle the received message */ public PowerMaxReaderThread(InputStream in, PowerMaxConnector connector) { this.in = in; this.connector = connector; interrupted = false; } @Override public void interrupt() { interrupted = true; super.interrupt(); try { in.close(); } catch (IOException e) { } // quietly close } @Override public void run() { logger.debug("Data listener started"); final int dataBufferMaxLen = 0xC0; byte[] dataBuffer = new byte[dataBufferMaxLen]; int index = 0; int msgLen = 0; boolean variableLen = false; try { byte[] tmpData = new byte[20]; int len = -1; while (((len = in.read(tmpData)) > 0) && !interrupted) { for (int i = 0; i < len; i++) { if (index >= dataBufferMaxLen) { // too many bytes received, try to find new start if (logger.isDebugEnabled()) { byte[] logData = Arrays.copyOf(dataBuffer, index); logger.debug("Truncating message {}", DatatypeConverter.printHexBinary(logData)); } index = 0; } if (index == 0 && tmpData[i] == 0x0D) { // Preamble dataBuffer[index++] = tmpData[i]; } else if (index > 0) { dataBuffer[index++] = tmpData[i]; if (index == 2) { try { PowerMaxReceiveType msgType = PowerMaxReceiveType.fromCode(tmpData[i]); msgLen = msgType.getLength(); variableLen = ((tmpData[i] & 0x000000FF) > 0x10) && (msgLen == 0); } catch (IllegalArgumentException arg0) { msgLen = 0; variableLen = false; } } else if (index == 5 && variableLen) { msgLen = (tmpData[i] & 0x000000FF) + 7; } else if ((msgLen == 0 && tmpData[i] == 0x0A) || (index == msgLen)) { // Postamble if (tmpData[i] != 0x0A && dataBuffer[index - 1] == 0x43) { // adjust message length for 0x43 msgLen++; } else if (checkCRC(dataBuffer, index)) { // whole message received with a right CRC byte[] msg = new byte[index]; msg = Arrays.copyOf(dataBuffer, index); connector.setWaitingForResponse(System.currentTimeMillis()); connector.handleIncomingMessage(msg); // find new preamble index = 0; } else if (msgLen == 0) { // CRC check failed for a message with an unknown length logger.debug("Message length is now {} but message is apparently not complete", index + 1); } else { // CRC check failed for a message with a known length connector.setWaitingForResponse(System.currentTimeMillis()); // find new preamble index = 0; } } } } } } catch (InterruptedIOException e) { Thread.currentThread().interrupt(); logger.debug("Interrupted via InterruptedIOException"); } catch (IOException e) { logger.debug("Reading failed: {}", e.getMessage()); } logger.debug("Data listener stopped"); } /** * Check if the CRC inside a received message is valid or not * * @param data * the buffer containing the message * @param len * the size of the message in the buffer * * @return true if the CRC is valid or false if not */ private boolean checkCRC(byte[] data, int len) { byte checksum = PowerMaxCommDriver.computeCRC(data, len); byte expected = data[len - 2]; if (checksum != expected) { byte[] logData = Arrays.copyOf(data, len); logger.warn("PowerMax alarm binding: message CRC check failed (expected {}, got {}, message {})", String.format("%02X", expected), String.format("%02X", checksum), DatatypeConverter.printHexBinary(logData)); } return (checksum == expected); } }