[nibeheatpump] Data parsing fix (#9958)

* Fixed escaped message parsing
* Removed also Apache commons dependency
* Generic improvements

Signed-off-by: Pauli Anttila <pauli.anttila@gmail.com>
This commit is contained in:
pali 2021-04-24 13:43:01 +03:00 committed by GitHub
parent 0c390ab9c9
commit 9b3adfc372
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 208 additions and 162 deletions

View File

@ -191,7 +191,7 @@ public class SerialConnector extends NibeHeatPumpBaseConnector {
@Override
public void sendAck() {
try {
byte addr = msg().get(NibeHeatPumpProtocol.OFFSET_ADR);
byte addr = msg().get(NibeHeatPumpProtocol.RES_OFFS_ADR);
sendAckToNibe(addr);
} catch (IOException e) {
sendErrorToListeners(e.getMessage());
@ -215,7 +215,7 @@ public class SerialConnector extends NibeHeatPumpBaseConnector {
sendDataToNibe(writeQueue.remove(0));
} else {
// no messages to send, send ack to pump
byte addr = msg().get(NibeHeatPumpProtocol.OFFSET_ADR);
byte addr = msg().get(NibeHeatPumpProtocol.RES_OFFS_ADR);
sendAckToNibe(addr);
}
} catch (IOException e) {
@ -230,7 +230,7 @@ public class SerialConnector extends NibeHeatPumpBaseConnector {
sendDataToNibe(readQueue.remove(0));
} else {
// no messages to send, send ack to pump
byte addr = msg().get(NibeHeatPumpProtocol.OFFSET_ADR);
byte addr = msg().get(NibeHeatPumpProtocol.RES_OFFS_ADR);
sendAckToNibe(addr);
}
} catch (IOException e) {

View File

@ -25,7 +25,7 @@ import org.openhab.binding.nibeheatpump.internal.protocol.NibeHeatPumpProtocol;
*/
public class ModbusDataReadOutMessage extends NibeHeatPumpBaseMessage {
private List<ModbusValue> values;
private List<ModbusValue> values = new ArrayList<>();
private ModbusDataReadOutMessage(MessageBuilder builder) {
super.msgType = MessageType.MODBUS_DATA_READ_OUT_MSG;
@ -42,27 +42,44 @@ public class ModbusDataReadOutMessage extends NibeHeatPumpBaseMessage {
@Override
public void encodeMessage(byte[] data) throws NibeHeatPumpException {
values = parseMessage(data);
if (NibeHeatPumpProtocol.isModbus40DataReadOut(data)) {
super.encodeMessage(data);
final int msglen = NibeHeatPumpProtocol.RES_HEADER_LEN + rawMessage[NibeHeatPumpProtocol.RES_OFFS_LEN];
values.clear();
try {
for (int i = NibeHeatPumpProtocol.RES_OFFS_DATA; i < (msglen - 1); i += 4) {
int id = ((rawMessage[i + 1] & 0xFF) << 8 | (rawMessage[i + 0] & 0xFF));
int value = (rawMessage[i + 3] & 0xFF) << 8 | (rawMessage[i + 2] & 0xFF);
if (id != 0xFFFF) {
values.add(new ModbusValue(id, value));
}
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new NibeHeatPumpException("Error occurred during data parsing", e);
}
} else {
throw new NibeHeatPumpException("Not Modbus data readout message");
}
}
@Override
public byte[] decodeMessage() {
return createDataReadOutPdu(values);
}
private byte[] createDataReadOutPdu(List<ModbusValue> values) {
byte datalen = (byte) (values.size() * 4);
byte msglen = (byte) (6 + datalen);
byte msglen = (byte) (NibeHeatPumpProtocol.RES_HEADER_LEN + datalen + NibeHeatPumpProtocol.PDU_CHECKSUM_LEN);
byte[] data = new byte[msglen];
data[0] = NibeHeatPumpProtocol.FRAME_START_CHAR_FROM_NIBE;
data[0] = NibeHeatPumpProtocol.FRAME_START_CHAR_RES;
data[1] = 0x00;
data[2] = NibeHeatPumpProtocol.ADR_MODBUS40;
data[3] = NibeHeatPumpProtocol.CMD_MODBUS_DATA_MSG;
data[4] = datalen;
int i = NibeHeatPumpProtocol.OFFSET_DATA;
int i = NibeHeatPumpProtocol.RES_OFFS_DATA;
for (ModbusValue value : values) {
@ -77,7 +94,6 @@ public class ModbusDataReadOutMessage extends NibeHeatPumpBaseMessage {
}
data[msglen - 1] = NibeHeatPumpProtocol.calculateChecksum(data, 2, msglen);
return data;
}
@ -89,34 +105,6 @@ public class ModbusDataReadOutMessage extends NibeHeatPumpBaseMessage {
return str;
}
private List<ModbusValue> parseMessage(byte[] data) throws NibeHeatPumpException {
if (NibeHeatPumpProtocol.isModbus40DataReadOut(data)) {
super.encodeMessage(data);
final int msglen = 5 + rawMessage[NibeHeatPumpProtocol.OFFSET_LEN];
List<ModbusValue> vals = new ArrayList<>();
try {
for (int i = NibeHeatPumpProtocol.OFFSET_DATA; i < (msglen - 1); i += 4) {
int id = ((rawMessage[i + 1] & 0xFF) << 8 | (rawMessage[i + 0] & 0xFF));
int value = (rawMessage[i + 3] & 0xFF) << 8 | (rawMessage[i + 2] & 0xFF);
if (id != 0xFFFF) {
vals.add(new ModbusValue(id, value));
}
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new NibeHeatPumpException("Error occurred during data parsing", e);
}
return vals;
} else {
throw new NibeHeatPumpException("Not Modbus data readout message");
}
}
public static class MessageBuilder {
private List<ModbusValue> values = new ArrayList<>();

View File

@ -41,7 +41,7 @@ public class ModbusReadRequestMessage extends NibeHeatPumpBaseMessage {
public void encodeMessage(byte[] data) throws NibeHeatPumpException {
if (NibeHeatPumpProtocol.isModbus40ReadRequestPdu(data)) {
super.encodeMessage(data);
coilAddress = (data[4] & 0xFF) << 8 | (data[3] & 0xFF);
coilAddress = (rawMessage[4] & 0xFF) << 8 | (rawMessage[3] & 0xFF);
} else {
throw new NibeHeatPumpException("Not Read Request message");
}
@ -49,12 +49,8 @@ public class ModbusReadRequestMessage extends NibeHeatPumpBaseMessage {
@Override
public byte[] decodeMessage() {
return createModbus40ReadPdu(coilAddress);
}
private byte[] createModbus40ReadPdu(int coilAddress) {
byte[] data = new byte[6];
data[0] = NibeHeatPumpProtocol.FRAME_START_CHAR_TO_NIBE;
data[0] = NibeHeatPumpProtocol.FRAME_START_CHAR_REQ;
data[1] = NibeHeatPumpProtocol.CMD_MODBUS_READ_REQ;
data[2] = (byte) 0x02; // data len
data[3] = (byte) (coilAddress & 0xFF);

View File

@ -58,21 +58,25 @@ public class ModbusReadResponseMessage extends NibeHeatPumpBaseMessage {
@Override
public void encodeMessage(byte[] data) throws NibeHeatPumpException {
super.encodeMessage(data);
if (NibeHeatPumpProtocol.isModbus40ReadResponse(data)) {
super.encodeMessage(data);
coilAddress = ((rawMessage[NibeHeatPumpProtocol.RES_OFFS_DATA + 1] & 0xFF) << 8
| (rawMessage[NibeHeatPumpProtocol.RES_OFFS_DATA + 0] & 0xFF));
value = (rawMessage[NibeHeatPumpProtocol.RES_OFFS_DATA + 5] & 0xFF) << 24
| (rawMessage[NibeHeatPumpProtocol.RES_OFFS_DATA + 4] & 0xFF) << 16
| (rawMessage[NibeHeatPumpProtocol.RES_OFFS_DATA + 3] & 0xFF) << 8
| (rawMessage[NibeHeatPumpProtocol.RES_OFFS_DATA + 2] & 0xFF);
coilAddress = (data[3] & 0xFF) << 8 | (data[4] & 0xFF);
parseMessage(data);
} else {
throw new NibeHeatPumpException("Not Read Response message");
}
}
@Override
public byte[] decodeMessage() {
return createModbusReadResponsePdu(coilAddress, value);
}
private byte[] createModbusReadResponsePdu(int coilAddress, int value) {
byte[] data = new byte[12];
data[0] = NibeHeatPumpProtocol.FRAME_START_CHAR_FROM_NIBE;
data[0] = NibeHeatPumpProtocol.FRAME_START_CHAR_RES;
data[1] = 0x00;
data[2] = NibeHeatPumpProtocol.ADR_MODBUS40;
data[3] = NibeHeatPumpProtocol.CMD_MODBUS_READ_RESP;
@ -93,30 +97,13 @@ public class ModbusReadResponseMessage extends NibeHeatPumpBaseMessage {
@Override
public String toString() {
String str = "";
str += super.toString();
String str = super.toString();
str += ", Coil address = " + coilAddress;
str += ", Value = " + value;
return str;
}
private void parseMessage(byte[] data) throws NibeHeatPumpException {
if (NibeHeatPumpProtocol.isModbus40ReadResponse(data)) {
super.encodeMessage(data);
coilAddress = ((data[NibeHeatPumpProtocol.OFFSET_DATA + 1] & 0xFF) << 8
| (data[NibeHeatPumpProtocol.OFFSET_DATA + 0] & 0xFF));
value = (data[NibeHeatPumpProtocol.OFFSET_DATA + 5] & 0xFF) << 24
| (data[NibeHeatPumpProtocol.OFFSET_DATA + 4] & 0xFF) << 16
| (data[NibeHeatPumpProtocol.OFFSET_DATA + 3] & 0xFF) << 8
| (data[NibeHeatPumpProtocol.OFFSET_DATA + 2] & 0xFF);
} else {
throw new NibeHeatPumpException("Not Read Response message");
}
}
public static class MessageBuilder {
private int coilAddress;
private int value;

View File

@ -55,8 +55,9 @@ public class ModbusWriteRequestMessage extends NibeHeatPumpBaseMessage {
public void encodeMessage(byte[] data) throws NibeHeatPumpException {
if (NibeHeatPumpProtocol.isModbus40WriteRequestPdu(data)) {
super.encodeMessage(data);
coilAddress = (data[4] & 0xFF) << 8 | (data[3] & 0xFF);
value = (data[8] & 0xFF) << 24 | (data[7] & 0xFF) << 16 | (data[6] & 0xFF) << 8 | (data[5] & 0xFF);
coilAddress = (rawMessage[4] & 0xFF) << 8 | (rawMessage[3] & 0xFF);
value = (rawMessage[8] & 0xFF) << 24 | (rawMessage[7] & 0xFF) << 16 | (rawMessage[6] & 0xFF) << 8
| (rawMessage[5] & 0xFF);
} else {
throw new NibeHeatPumpException("Not Write Request message");
}
@ -64,17 +65,13 @@ public class ModbusWriteRequestMessage extends NibeHeatPumpBaseMessage {
@Override
public byte[] decodeMessage() {
return createModbus40WritePdu(coilAddress, value);
}
private byte[] createModbus40WritePdu(int coildAddress, int value) {
byte[] data = new byte[10];
data[0] = NibeHeatPumpProtocol.FRAME_START_CHAR_TO_NIBE;
data[0] = NibeHeatPumpProtocol.FRAME_START_CHAR_REQ;
data[1] = NibeHeatPumpProtocol.CMD_MODBUS_WRITE_REQ;
data[2] = (byte) 0x06; // data len
data[3] = (byte) (coildAddress & 0xFF);
data[4] = (byte) ((coildAddress >> 8) & 0xFF);
data[3] = (byte) (coilAddress & 0xFF);
data[4] = (byte) ((coilAddress >> 8) & 0xFF);
data[5] = (byte) (value & 0xFF);
data[6] = (byte) ((value >> 8) & 0xFF);
data[7] = (byte) ((value >> 16) & 0xFF);
@ -86,9 +83,7 @@ public class ModbusWriteRequestMessage extends NibeHeatPumpBaseMessage {
@Override
public String toString() {
String str = "";
str += super.toString();
String str = super.toString();
str += ", Coil address = " + coilAddress;
str += ", Value = " + value;

View File

@ -36,7 +36,12 @@ public class ModbusWriteResponseMessage extends NibeHeatPumpBaseMessage {
@Override
public void encodeMessage(byte[] data) throws NibeHeatPumpException {
result = modbus40WriteSuccess(data);
if (NibeHeatPumpProtocol.isModbus40WriteResponsePdu(data)) {
super.encodeMessage(data);
result = rawMessage[NibeHeatPumpProtocol.RES_OFFS_DATA] == 1;
} else {
throw new NibeHeatPumpException("Not Write Response message");
}
}
public boolean isSuccessfull() {
@ -45,13 +50,9 @@ public class ModbusWriteResponseMessage extends NibeHeatPumpBaseMessage {
@Override
public byte[] decodeMessage() {
return createModbusWriteResponsePdu(result);
}
private byte[] createModbusWriteResponsePdu(boolean result) {
byte[] data = new byte[7];
data[0] = NibeHeatPumpProtocol.FRAME_START_CHAR_FROM_NIBE;
data[0] = NibeHeatPumpProtocol.FRAME_START_CHAR_RES;
data[1] = 0x00;
data[2] = NibeHeatPumpProtocol.ADR_MODBUS40;
data[3] = NibeHeatPumpProtocol.CMD_MODBUS_WRITE_RESP;
@ -70,14 +71,6 @@ public class ModbusWriteResponseMessage extends NibeHeatPumpBaseMessage {
return str;
}
private boolean modbus40WriteSuccess(byte[] data) throws NibeHeatPumpException {
if (NibeHeatPumpProtocol.isModbus40WriteResponsePdu(data)) {
super.encodeMessage(data);
return data[NibeHeatPumpProtocol.OFFSET_DATA] == 1;
}
throw new NibeHeatPumpException("Not Write Response message");
}
public static class MessageBuilder {
private boolean result;

View File

@ -56,7 +56,6 @@ public abstract class NibeHeatPumpBaseMessage implements NibeHeatPumpMessage {
public byte[] rawMessage;
public MessageType msgType = MessageType.UNKNOWN;
public byte msgId;
public NibeHeatPumpBaseMessage() {
}
@ -67,12 +66,15 @@ public abstract class NibeHeatPumpBaseMessage implements NibeHeatPumpMessage {
@Override
public void encodeMessage(byte[] data) throws NibeHeatPumpException {
data = NibeHeatPumpProtocol.checkMessageChecksumAndRemoveDoubles(data);
rawMessage = data;
msgId = data[1];
if (data.length >= NibeHeatPumpProtocol.PDU_MIN_LEN) {
byte[] d = NibeHeatPumpProtocol.checkMessageChecksumAndRemoveDoubles(data);
rawMessage = d.clone();
byte messageTypeByte = NibeHeatPumpProtocol.getMessageType(data);
msgType = NibeHeatPumpBaseMessage.getMessageType(messageTypeByte);
byte messageTypeByte = NibeHeatPumpProtocol.getMessageType(d);
msgType = NibeHeatPumpBaseMessage.getMessageType(messageTypeByte);
} else {
throw new NibeHeatPumpException("Too short message");
}
}
@Override

View File

@ -12,7 +12,8 @@
*/
package org.openhab.binding.nibeheatpump.internal.protocol;
import org.apache.commons.lang3.ArrayUtils;
import java.io.ByteArrayOutputStream;
import org.openhab.binding.nibeheatpump.internal.NibeHeatPumpException;
/**
@ -22,14 +23,24 @@ import org.openhab.binding.nibeheatpump.internal.NibeHeatPumpException;
*/
public class NibeHeatPumpProtocol {
public static final byte FRAME_START_CHAR_FROM_NIBE = (byte) 0x5C;
public static final byte FRAME_START_CHAR_TO_NIBE = (byte) 0xC0;
public static final byte PDU_MIN_LEN = 3;
public static final byte PDU_CHECKSUM_LEN = 1;
public static final byte FRAME_START_CHAR_RES = (byte) 0x5C;
public static final byte FRAME_START_CHAR_REQ = (byte) 0xC0;
public static final byte OFFSET_START = 0;
public static final byte OFFSET_ADR = 2;
public static final byte OFFSET_CMD = 3;
public static final byte OFFSET_LEN = 4;
public static final byte OFFSET_DATA = 5;
public static final byte RES_OFFS_ADR = 2;
public static final byte RES_OFFS_CMD = 3;
public static final byte RES_OFFS_LEN = 4;
public static final byte RES_OFFS_DATA = 5;
public static final byte REQ_OFFS_CMD = 1;
public static final byte REQ_OFFS_LEN = 2;
public static final byte RES_HEADER_LEN = 5;
public static final byte REQ_HEADER_LEN = 3;
public static final byte CMD_RMU_DATA_MSG = (byte) 0x62;
public static final byte CMD_MODBUS_DATA_MSG = (byte) 0x68;
@ -43,53 +54,53 @@ public class NibeHeatPumpProtocol {
public static final byte ADR_MODBUS40 = (byte) 0x20;
public static boolean isModbus40DataReadOut(byte[] data) {
if (data[OFFSET_START] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00
&& data[OFFSET_ADR] == ADR_MODBUS40) {
return data[OFFSET_CMD] == CMD_MODBUS_DATA_MSG && data[OFFSET_LEN] >= (byte) 0x50;
if (data[OFFSET_START] == FRAME_START_CHAR_RES && data[1] == (byte) 0x00
&& data[RES_OFFS_ADR] == ADR_MODBUS40) {
return data[RES_OFFS_CMD] == CMD_MODBUS_DATA_MSG && data[RES_OFFS_LEN] >= (byte) 0x50;
}
return false;
}
public static boolean isModbus40ReadResponse(byte[] data) {
if (data[OFFSET_START] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00
&& data[OFFSET_ADR] == ADR_MODBUS40) {
return data[OFFSET_CMD] == CMD_MODBUS_READ_RESP && data[OFFSET_LEN] >= (byte) 0x06;
if (data[OFFSET_START] == FRAME_START_CHAR_RES && data[1] == (byte) 0x00
&& data[RES_OFFS_ADR] == ADR_MODBUS40) {
return data[RES_OFFS_CMD] == CMD_MODBUS_READ_RESP && data[RES_OFFS_LEN] >= (byte) 0x06;
}
return false;
}
public static boolean isRmu40DataReadOut(byte[] data) {
if (data[0] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00 && data[OFFSET_ADR] == ADR_RMU40) {
return data[OFFSET_CMD] == CMD_RMU_DATA_MSG && data[OFFSET_LEN] >= (byte) 0x18;
if (data[0] == FRAME_START_CHAR_RES && data[1] == (byte) 0x00 && data[RES_OFFS_ADR] == ADR_RMU40) {
return data[RES_OFFS_CMD] == CMD_RMU_DATA_MSG && data[RES_OFFS_LEN] >= (byte) 0x18;
}
return false;
}
public static boolean isModbus40WriteResponsePdu(byte[] data) {
return data[OFFSET_START] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00
&& data[OFFSET_ADR] == ADR_MODBUS40 && data[OFFSET_CMD] == CMD_MODBUS_WRITE_RESP;
return data[OFFSET_START] == FRAME_START_CHAR_RES && data[1] == (byte) 0x00
&& data[RES_OFFS_ADR] == ADR_MODBUS40 && data[RES_OFFS_CMD] == CMD_MODBUS_WRITE_RESP;
}
public static boolean isModbus40WriteTokenPdu(byte[] data) {
return data[0] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00 && data[OFFSET_ADR] == ADR_MODBUS40
&& data[OFFSET_CMD] == CMD_MODBUS_WRITE_REQ && data[OFFSET_LEN] == 0x00;
return data[0] == FRAME_START_CHAR_RES && data[1] == (byte) 0x00 && data[RES_OFFS_ADR] == ADR_MODBUS40
&& data[RES_OFFS_CMD] == CMD_MODBUS_WRITE_REQ && data[RES_OFFS_LEN] == 0x00;
}
public static boolean isModbus40ReadTokenPdu(byte[] data) {
return data[OFFSET_START] == FRAME_START_CHAR_FROM_NIBE && data[1] == (byte) 0x00
&& data[OFFSET_ADR] == ADR_MODBUS40 && data[OFFSET_CMD] == CMD_MODBUS_READ_REQ
&& data[OFFSET_LEN] == 0x00;
return data[OFFSET_START] == FRAME_START_CHAR_RES && data[1] == (byte) 0x00
&& data[RES_OFFS_ADR] == ADR_MODBUS40 && data[RES_OFFS_CMD] == CMD_MODBUS_READ_REQ
&& data[RES_OFFS_LEN] == 0x00;
}
public static boolean isModbus40WriteRequestPdu(byte[] data) {
return data[0] == FRAME_START_CHAR_TO_NIBE && data[1] == CMD_MODBUS_WRITE_REQ;
return data[OFFSET_START] == FRAME_START_CHAR_REQ && data[REQ_OFFS_CMD] == CMD_MODBUS_WRITE_REQ;
}
public static boolean isModbus40ReadRequestPdu(byte[] data) {
return data[OFFSET_START] == FRAME_START_CHAR_TO_NIBE && data[1] == CMD_MODBUS_READ_REQ;
return data[OFFSET_START] == FRAME_START_CHAR_REQ && data[REQ_OFFS_CMD] == CMD_MODBUS_READ_REQ;
}
public static byte calculateChecksum(byte[] data) {
@ -108,10 +119,10 @@ public class NibeHeatPumpProtocol {
public static byte getMessageType(byte[] data) {
byte messageType = 0;
if (data[NibeHeatPumpProtocol.OFFSET_START] == NibeHeatPumpProtocol.FRAME_START_CHAR_FROM_NIBE) {
messageType = data[NibeHeatPumpProtocol.OFFSET_CMD];
} else if (data[NibeHeatPumpProtocol.OFFSET_START] == NibeHeatPumpProtocol.FRAME_START_CHAR_TO_NIBE) {
messageType = data[1];
if (data[NibeHeatPumpProtocol.OFFSET_START] == NibeHeatPumpProtocol.FRAME_START_CHAR_RES) {
messageType = data[RES_OFFS_CMD];
} else if (data[NibeHeatPumpProtocol.OFFSET_START] == NibeHeatPumpProtocol.FRAME_START_CHAR_REQ) {
messageType = data[REQ_OFFS_CMD];
}
return messageType;
@ -124,11 +135,11 @@ public class NibeHeatPumpProtocol {
if (NibeHeatPumpProtocol.isModbus40ReadRequestPdu(data)
|| NibeHeatPumpProtocol.isModbus40WriteRequestPdu(data)) {
msglen = 3 + data[2];
msglen = REQ_HEADER_LEN + data[REQ_OFFS_LEN];
startIndex = 0;
stopIndex = msglen;
} else {
msglen = 5 + data[OFFSET_LEN];
msglen = RES_HEADER_LEN + data[RES_OFFS_LEN];
startIndex = 2;
stopIndex = msglen;
}
@ -138,24 +149,56 @@ public class NibeHeatPumpProtocol {
// if checksum is 0x5C (start character), heat pump seems to send 0xC5 checksum
if (checksum == msgChecksum || (checksum == FRAME_START_CHAR_FROM_NIBE && msgChecksum == (byte) 0xC5)) {
if (checksum == msgChecksum || (checksum == FRAME_START_CHAR_RES && msgChecksum == (byte) 0xC5)) {
// if data contains 0x5C (start character), data seems to contains double 0x5C characters
// let's remove doubles
for (int i = 1; i < msglen; i++) {
if (data[i] == FRAME_START_CHAR_FROM_NIBE) {
data = ArrayUtils.remove(data, i);
msglen--;
// fix message len
data[OFFSET_LEN]--;
}
}
return removeEscapedDuplicates(data, msglen);
} else {
throw new NibeHeatPumpException(
"Checksum does not match. Checksum=" + (msgChecksum & 0xFF) + ", expected=" + (checksum & 0xFF));
}
}
private static byte[] removeEscapedDuplicates(byte[] data, int msglen) {
if (dataContainsEscapedDuplicates(data, msglen)) {
ByteArrayOutputStream out = new ByteArrayOutputStream(msglen);
byte newlen = data[RES_OFFS_LEN];
// write start char
out.write(FRAME_START_CHAR_RES);
// remove all duplicates between start char and checksum bytes
// checksum byte can't be 0x5C as it's set to 0xC5 in this case by the heat pump
for (int i = 1; i < msglen; i++) {
if (data[i] == FRAME_START_CHAR_RES && data[i + 1] == FRAME_START_CHAR_RES) {
// write one 0x5C
out.write(FRAME_START_CHAR_RES);
// skip next 0x5C and decrease the length
i++;
newlen--;
} else {
out.write(data[i]);
}
}
// write checksum
out.write(data[msglen]);
// return modified data
byte[] newdata = out.toByteArray();
newdata[RES_OFFS_LEN] = newlen;
return newdata;
}
return data;
}
private static boolean dataContainsEscapedDuplicates(byte[] data, int msglen) {
for (int i = 1; i < msglen; i++) {
if (data[i] == FRAME_START_CHAR_RES && data[i + 1] == FRAME_START_CHAR_RES) {
return true;
}
}
return false;
}
}

View File

@ -34,7 +34,7 @@ public enum NibeHeatPumpProtocolStates implements NibeHeatPumpProtocolState {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Received byte: {}", String.format("%02X", b));
}
if (b == NibeHeatPumpProtocol.FRAME_START_CHAR_FROM_NIBE) {
if (b == NibeHeatPumpProtocol.FRAME_START_CHAR_RES) {
LOGGER.trace("Frame start found");
context.msg().clear();
context.msg().put(b);
@ -144,7 +144,7 @@ public enum NibeHeatPumpProtocolStates implements NibeHeatPumpProtocolState {
int len = byteBuffer.remaining();
if (len >= 1) {
if (byteBuffer.get(0) != NibeHeatPumpProtocol.FRAME_START_CHAR_FROM_NIBE) {
if (byteBuffer.get(0) != NibeHeatPumpProtocol.FRAME_START_CHAR_RES) {
return msgStatus.INVALID;
}
@ -155,7 +155,7 @@ public enum NibeHeatPumpProtocolStates implements NibeHeatPumpProtocolState {
}
if (len >= 6) {
int datalen = byteBuffer.get(NibeHeatPumpProtocol.OFFSET_LEN);
int datalen = byteBuffer.get(NibeHeatPumpProtocol.RES_OFFS_LEN);
// check if all bytes received
if (len < datalen + 6) {

View File

@ -92,6 +92,37 @@ public class ModbusDataReadOutMessageTest {
checkRegisters(message, expectedValues);
}
@Test
public void parseHeavilyEscapedModbusDataReadOutMessageTest() throws NibeHeatPumpException {
final String message = "5C0020685401A81F0100A86400FDA7D003449C1E004F9CA000509C7800519C0301529C1B01879C14014E9CC601479C010115B9B0FF3AB94B00C9AF0000489C0D014C9CE7004B9C0000FFFF0000FFFF00005C5C5C5C5C5C5C5C41";
@SuppressWarnings("serial")
final ArrayList<ModbusValue> expectedValues = new ArrayList<ModbusValue>() {
{
add(new ModbusValue(43009, 287));
add(new ModbusValue(43008, 100));
add(new ModbusValue(43005, 976));
add(new ModbusValue(40004, 30));
add(new ModbusValue(40015, 160));
add(new ModbusValue(40016, 120));
add(new ModbusValue(40017, 259));
add(new ModbusValue(40018, 283));
add(new ModbusValue(40071, 276));
add(new ModbusValue(40014, 454));
add(new ModbusValue(40007, 257));
add(new ModbusValue(47381, 65456));
add(new ModbusValue(47418, 75));
add(new ModbusValue(45001, 0));
add(new ModbusValue(40008, 269));
add(new ModbusValue(40012, 231));
add(new ModbusValue(40011, 0));
add(new ModbusValue(23644, 23644));
}
};
checkRegisters(message, expectedValues);
}
@Test
public void specialLen1Test() throws NibeHeatPumpException {
final String message = "5C00206851449C2500489CFC004C9CF1004E9CC7014D9C0B024F9C2500509C3300519C0B01529C5C5C01569C3100C9AF000001A80C01FDA716FAFAA9070098A91B1BFFFF0000A0A9CA02FFFF00009CA99212FFFF0000BE";

View File

@ -25,12 +25,11 @@ import org.openhab.core.util.HexUtils;
*/
public class ModbusReadResponseMessageTest {
private final int coilAddress = 513;
private final int value = 100992003;
private final String okMessage = "5C00206A060102030405064B";
@Test
public void createMessageTest() throws NibeHeatPumpException {
final int coilAddress = 513;
final int value = 100992003;
final String okMessage = "5C00206A060102030405064B";
ModbusReadResponseMessage m = new ModbusReadResponseMessage.MessageBuilder().coilAddress(coilAddress)
.value(value).build();
byte[] byteMessage = m.decodeMessage();
@ -39,23 +38,35 @@ public class ModbusReadResponseMessageTest {
@Test
public void parseMessageTest() throws NibeHeatPumpException {
byte[] msg = HexUtils.hexToBytes(okMessage);
ModbusReadResponseMessage m = (ModbusReadResponseMessage) MessageFactory.getMessage(msg);
final int coilAddress = 513;
final int value = 100992003;
final String message = "5C00206A060102030405064B";
ModbusReadResponseMessage m = (ModbusReadResponseMessage) MessageFactory
.getMessage(HexUtils.hexToBytes(message));
assertEquals(coilAddress, m.getCoilAddress());
assertEquals(value, m.getValue());
}
@Test
public void badCrcTest() {
final String strMessage = "5C00206A060102030405064C";
final byte[] byteMessage = HexUtils.hexToBytes(strMessage);
assertThrows(NibeHeatPumpException.class, () -> MessageFactory.getMessage(byteMessage));
final String message = "5C00206A060102030405064C";
assertThrows(NibeHeatPumpException.class, () -> MessageFactory.getMessage(HexUtils.hexToBytes(message)));
}
@Test
public void notReadResponseMessageTest() {
final String strMessage = "5C00206B060102030405064A";
final byte[] byteMessage = HexUtils.hexToBytes(strMessage);
assertThrows(NibeHeatPumpException.class, () -> new ModbusReadResponseMessage(byteMessage));
final String message = "5C00206B060102030405064A";
assertThrows(NibeHeatPumpException.class, () -> new ModbusReadResponseMessage(HexUtils.hexToBytes(message)));
}
@Test
public void parseEscapedMessageTest() throws NibeHeatPumpException {
final int coilAddress = 513;
final int value = 0x05E65C;
final String message = "5C00206A0701025C5CE60500AD";
ModbusReadResponseMessage m = (ModbusReadResponseMessage) MessageFactory
.getMessage(HexUtils.hexToBytes(message));
assertEquals(coilAddress, m.getCoilAddress());
assertEquals(value, m.getValue());
}
}