mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-25 14:55:55 +01:00
[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:
parent
0c390ab9c9
commit
9b3adfc372
@ -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) {
|
||||
|
@ -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<>();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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";
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user