/**
* 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.zwave.internal.protocol.commandclass;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.openhab.binding.zwave.internal.protocol.SerialMessage;
import org.openhab.binding.zwave.internal.protocol.ZWaveController;
import org.openhab.binding.zwave.internal.protocol.ZWaveEndpoint;
import org.openhab.binding.zwave.internal.protocol.ZWaveNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
/**
* Handles the CRC 16 Encapsulation Command command class.
*
* @author Hans Vanbellingen
* @since 1.7.0
*/
@XStreamAlias("crc16EncapsulationCommandClass")
public class ZWaveCRC16EncapsulationCommandClass extends ZWaveCommandClass {
@XStreamOmitField
private static final Logger logger = LoggerFactory.getLogger(ZWaveCRC16EncapsulationCommandClass.class);
/**
* Creates a new instance of the ZWaveMultiCommandCommandClass class.
*
* @param node
* the node this command class belongs to
* @param controller
* the controller to use
* @param endpoint
* the endpoint this Command class belongs to
*/
public ZWaveCRC16EncapsulationCommandClass(ZWaveNode node, ZWaveController controller, ZWaveEndpoint endpoint) {
super(node, controller, endpoint);
}
/**
* {@inheritDoc}
*/
@Override
public CommandClass getCommandClass() {
return CommandClass.CRC_16_ENCAP;
}
/**
* {@inheritDoc}
*/
@Override
public void handleApplicationCommandRequest(SerialMessage serialMessage, int offset, int endpointId) {
logger.debug("NODE {}: Received CRC 16 Encapsulation Request", this.getNode().getNodeId());
int command = serialMessage.getMessagePayloadByte(offset);
switch (command) {
case 0x01: // crc 16 Encapsulation
handleCRC16EncapResponse(serialMessage, offset + 1);
break;
}
}
/**
* Handle the crc16 encapsulated message. This processes the received frame,
* checks the crc and forwards to the real command class.
*
* @param serialMessage
* The received message
* @param offset
* The starting offset into the payload
*/
private void handleCRC16EncapResponse(SerialMessage serialMessage, int offset) {
logger.trace("Process CRC16 Encapsulation");
// calculate CRC
byte[] payload = serialMessage.getMessagePayload();
byte[] messageCrc = Arrays.copyOfRange(payload, payload.length - 2, payload.length);
byte[] tocheck = Arrays.copyOfRange(payload, offset - 2, payload.length - 2);
short calculatedCrc = crc_ccit(tocheck);
// check if messageCrc = calculatedCrc
ByteBuffer byteBuffer = ByteBuffer.allocate(2);
byteBuffer.putShort(calculatedCrc);
if (!Arrays.equals(messageCrc, byteBuffer.array())) {
logger.error("NODE {}: CRC check failed message contains {} but should be {}", this.getNode().getNodeId(),
SerialMessage.bb2hex(messageCrc), SerialMessage.bb2hex(byteBuffer.array()));
return;
}
// Execute underlying command
CommandClass commandClass;
ZWaveCommandClass zwaveCommandClass;
int commandClassCode = serialMessage.getMessagePayloadByte(offset);
commandClass = CommandClass.getCommandClass(commandClassCode);
if (commandClass == null) {
logger.error(String.format("NODE %d: Unsupported command class 0x%02x", this.getNode().getNodeId(),
commandClassCode));
} else {
zwaveCommandClass = this.getNode().getCommandClass(commandClass);
// Apparently, this node supports a command class that we did not
// get (yet) during initialization.
// Let's add it now then to support handling this message.
if (zwaveCommandClass == null) {
logger.debug(String.format("NODE %d: Command class %s (0x%02x) not found, trying to add it.",
getNode().getNodeId(), commandClass.getLabel(), commandClass.getKey()));
zwaveCommandClass = ZWaveCommandClass.getInstance(commandClass.getKey(), getNode(), getController());
if (zwaveCommandClass != null) {
logger.debug(String.format("NODE %d: Adding command class %s (0x%02x)", getNode().getNodeId(),
commandClass.getLabel(), commandClass.getKey()));
getNode().addCommandClass(zwaveCommandClass);
}
}
if (zwaveCommandClass == null) {
logger.error(String.format("NODE %d: CommandClass %s (0x%02x) not implemented.",
this.getNode().getNodeId(), commandClass.getLabel(), commandClassCode));
} else {
logger.debug(
String.format("NODE %d: Calling handleApplicationCommandRequest.", this.getNode().getNodeId()));
zwaveCommandClass.handleApplicationCommandRequest(serialMessage, offset + 1, 0);
}
}
}
private short crc_ccit(final byte[] args) {
int crc = 0x1D0F;
int polynomial = 0x1021;
for (byte b : args) {
for (int i = 0; i < 8; i++) {
boolean bit = ((b >> (7 - i) & 1) == 1);
boolean c15 = ((crc >> 15 & 1) == 1);
crc <<= 1;
// If coefficient of bit and remainder polynomial = 1 xor crc with polynomial
if (c15 ^ bit) {
crc ^= polynomial;
}
}
}
crc &= 0xffff;
return (short) crc;
}
}