]> git.basschouten.com Git - openhab-addons.git/commitdiff
[nibeheatpump] Data parsing fix (#9958)
authorpali <pauli.anttila@gmail.com>
Sat, 24 Apr 2021 10:43:01 +0000 (13:43 +0300)
committerGitHub <noreply@github.com>
Sat, 24 Apr 2021 10:43:01 +0000 (12:43 +0200)
* Fixed escaped message parsing
* Removed also Apache commons dependency
* Generic improvements

Signed-off-by: Pauli Anttila <pauli.anttila@gmail.com>
bundles/org.openhab.binding.nibeheatpump/src/main/java/org/openhab/binding/nibeheatpump/internal/connection/SerialConnector.java
bundles/org.openhab.binding.nibeheatpump/src/main/java/org/openhab/binding/nibeheatpump/internal/message/ModbusDataReadOutMessage.java
bundles/org.openhab.binding.nibeheatpump/src/main/java/org/openhab/binding/nibeheatpump/internal/message/ModbusReadRequestMessage.java
bundles/org.openhab.binding.nibeheatpump/src/main/java/org/openhab/binding/nibeheatpump/internal/message/ModbusReadResponseMessage.java
bundles/org.openhab.binding.nibeheatpump/src/main/java/org/openhab/binding/nibeheatpump/internal/message/ModbusWriteRequestMessage.java
bundles/org.openhab.binding.nibeheatpump/src/main/java/org/openhab/binding/nibeheatpump/internal/message/ModbusWriteResponseMessage.java
bundles/org.openhab.binding.nibeheatpump/src/main/java/org/openhab/binding/nibeheatpump/internal/message/NibeHeatPumpBaseMessage.java
bundles/org.openhab.binding.nibeheatpump/src/main/java/org/openhab/binding/nibeheatpump/internal/protocol/NibeHeatPumpProtocol.java
bundles/org.openhab.binding.nibeheatpump/src/main/java/org/openhab/binding/nibeheatpump/internal/protocol/NibeHeatPumpProtocolStates.java
bundles/org.openhab.binding.nibeheatpump/src/test/java/org/openhab/binding/nibeheatpump/internal/message/ModbusDataReadOutMessageTest.java
bundles/org.openhab.binding.nibeheatpump/src/test/java/org/openhab/binding/nibeheatpump/internal/message/ModbusReadResponseMessageTest.java

index d1db2ebb0cf7166e1c29da666dfc090d516a817b..f3a4c3eb8d322bbe045e2e1398469ab1813398b6 100644 (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) {
index 3a90e48973e9d9fcb67db10283b5beeccbcffc40..1f246baf8cb75e710f1317abb1eb904d83f1ebe6 100644 (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<>();
 
index 797705056e4776795104692c282b38c5534277c0..b8230659eddd1359bf69a68a43b54492216a6c04 100644 (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);
index 8ec86cbe9a34f9086697d1d04a394f7d4f4f4308..8f852bb2f4fb379a1c3bd1de5a2d1306bb430f85 100644 (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;
index ade7e176a587fbb242ad6ac0c0cc96169041a9da..88339093f195fd875f29ab7b52aa7646c6504149 100644 (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;
 
index d54cc0b0cc88af2da48835ddd429b8a8f8e63289..96b2bb4abb29569aa7ae5f42ccfc0dcbac2573a0 100644 (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;
 
index 86b8a23e9cdd03f1bb7ad3c5e161e658535aa188..c6fd92cb40827e2b9d32b9ad2fabc3aaa271f253 100644 (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
index 0ef1c81010172efef36b3c69f8f5ac3484d2e8b6..309f5a312f8e78389626c3dc18913b26deb0816a 100644 (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
+            return removeEscapedDuplicates(data, msglen);
+        } else {
+            throw new NibeHeatPumpException(
+                    "Checksum does not match. Checksum=" + (msgChecksum & 0xFF) + ", expected=" + (checksum & 0xFF));
+        }
+    }
 
-            // 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--;
+    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);
 
-                    // fix message len
-                    data[OFFSET_LEN]--;
+            // 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]);
                 }
             }
-        } else {
-            throw new NibeHeatPumpException(
-                    "Checksum does not match. Checksum=" + (msgChecksum & 0xFF) + ", expected=" + (checksum & 0xFF));
+
+            // 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;
+    }
 }
index 7ed443570eba5759215366595ff88ca0f991bfeb..8f52aaa412e0a6f5bc306cfce47a02266277d515 100644 (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) {
index 926a9ef4357644061495c9f5d6aacdab7646f4d0..892ca812afed38d224d74f4869248ae525a0097c 100644 (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";
index 0dfeee4615fc437f1fa21371f1841a09c24e2b4e..e7f44f4c697a9a91e960c54518bdc920845a84c8 100644 (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());
     }
 }