]> git.basschouten.com Git - openhab-addons.git/commitdiff
[knx] Fix decoding of DPT 242.600 and add tests (#14875)
authorHolger Friedrich <holgerfriedrich@users.noreply.github.com>
Sun, 30 Apr 2023 12:03:54 +0000 (14:03 +0200)
committerGitHub <noreply@github.com>
Sun, 30 Apr 2023 12:03:54 +0000 (14:03 +0200)
* [knx] Fix decoding of DPT 242.600 and add tests

Correct handling of parameter Y for DPT 242.600.

Add back to back tests of DPT 232.600 and 242.600,
testing color conversion from raw bytes to HSB and
back to Calimero color representation as String.

Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialTransportAdapter.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/ValueDecoder.java
bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/dpt/DPTTest.java

index e20afc15fa0898f1c4c906767554bcd7d0026f30..7028d4b668fbd6593ce649bed4ecea4fa3d2cf61 100644 (file)
@@ -39,12 +39,12 @@ import tuwien.auto.calimero.serial.spi.SerialCom;
  * The {@link SerialTransportAdapter} provides org.openhab.core.io.transport.serial
  * services to the Calimero library.
  * 
- * @ServiceProvider annotation (biz.aQute.bnd.annotation) automatically creates the file
- *                  /META-INF/services/tuwien.auto.calimero.serial.spi.SerialCom
- *                  to register SerialTransportAdapter to the service loader.
- *                  Additional attributes for SerialTansportAdapter can be specified as well, e.g.
- *                  attribute = { "position=1" }
- *                  and will be part of MANIFEST.MF
+ * {@literal @}ServiceProvider annotation (biz.aQute.bnd.annotation) automatically creates the file
+ * /META-INF/services/tuwien.auto.calimero.serial.spi.SerialCom
+ * to register SerialTransportAdapter to the service loader.
+ * Additional attributes for SerialTansportAdapter can be specified as well, e.g.
+ * attribute = { "position=1" }
+ * and will be part of MANIFEST.MF
  * 
  * @author Holger Friedrich - Initial contribution
  */
index 28d3ecae9e95efa8d93271b1ea8e76b3ba29f8b4..b94f0ac166487448dc0787d56895191599d4221f 100644 (file)
@@ -73,9 +73,10 @@ public class ValueDecoder {
     // RGBW: "100 27 25 12 %", value range: 0-100, invalid values: "-"
     private static final Pattern RGBW_PATTERN = Pattern
             .compile("(?:(?<r>[\\d,.]+)|-)\\s(?:(?<g>[\\d,.]+)|-)\\s(?:(?<b>[\\d,.]+)|-)\\s(?:(?<w>[\\d,.]+)|-)\\s%");
-    // xyY: "(0,123 0,123) 56 %", value range 0-1 for xy (comma as decimal point), 0-100 for Y, invalid values omitted
-    private static final Pattern XYY_PATTERN = Pattern
-            .compile("(?:\\((?<x>\\d+(?:,\\d+)?) (?<y>\\d+(?:,\\d+)?)\\))?\\s*(?:(?<Y>\\d+(?:,\\d+)?)\\s%)?");
+    // xyY: "(0,123 0,123) 56 %", value range 0-1 for xy (comma or point as decimal point), 0-100 for Y, invalid values
+    // omitted
+    public static final Pattern XYY_PATTERN = Pattern
+            .compile("(?:\\((?<x>\\d+(?:[,.]\\d+)?) (?<y>\\d+(?:[,.]\\d+)?)\\))?\\s*(?:(?<Y>\\d+(?:[,.]\\d+)?)\\s%)?");
 
     /**
      * convert the raw value received to the corresponding openHAB value
@@ -311,7 +312,7 @@ public class ValueDecoder {
                     return ColorUtil.xyToHsb(new double[] { x, y });
                 } else {
                     double pY = Double.parseDouble(stringY.replace(",", "."));
-                    return ColorUtil.xyToHsb(new double[] { x, y, pY });
+                    return ColorUtil.xyToHsb(new double[] { x, y, pY / 100.0 });
                 }
             }
         }
index 049192499a08adeb406ba9d91d200dc7985be870..71c3b7ab56de09a871725bcbc3e3fbde7e1b7530 100644 (file)
@@ -14,8 +14,9 @@ package org.openhab.binding.knx.internal.dpt;
 
 import static org.junit.jupiter.api.Assertions.*;
 
-import java.nio.charset.StandardCharsets;
 import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.stream.IntStream;
 import java.util.stream.Stream;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -402,13 +403,79 @@ class DPTTest {
         Assertions.assertNotNull(value);
     }
 
-    private static Stream<String> rgbValueProvider() {
-        return Stream.of("r:0 g:0 b:0", "r:255 g:255 b:255");
+    private static Stream<byte[]> rgbValueProvider() {
+        // Returning all combinations is too much. Implementation tries to catch rounding errors
+        // but is still deterministic to get reproducible test results.
+        return IntStream.range(0, 3 * 256)
+                .mapToObj(i -> new byte[] { (byte) (i & 0xff), (byte) ((i / 2) & 0xff), (byte) ((i / 3) & 0xff) });
     }
 
     @ParameterizedTest
     @MethodSource("rgbValueProvider")
-    public void rgbTest(String value) {
-        Assertions.assertNotNull(ValueDecoder.decode("232.600", value.getBytes(StandardCharsets.UTF_8), HSBType.class));
+    public void dpt232BackToBackTest(byte[] value) {
+        backToBackTest232(value, "232.600");
+        backToBackTest232(value, "232.60000");
+    }
+
+    private void backToBackTest232(byte[] value, String dpt) {
+        // decode will apply transformation from raw bytes to String internally
+        HSBType hsb = (HSBType) ValueDecoder.decode(dpt, value, HSBType.class);
+        Assertions.assertNotNull(hsb);
+
+        // encoding will return a String in notation defined by Calimero: "r:xxx g:xxx b:xxx"
+        String result = ValueEncoder.encode(hsb, dpt);
+
+        // for back to back test, compare String representation
+        Assertions.assertEquals("r:" + (value[0] & 0xff) + " g:" + (value[1] & 0xff) + " b:" + (value[2] & 0xff),
+                result);
+    }
+
+    private static Stream<byte[]> xyYValueProvider() {
+        // Returning all combinations is too much. Implementation tries to catch rounding errors
+        // but is still deterministic to get reproducible test results.
+        //
+        // Cannot run make use of full numeric range, as colors cannot be represented in CIE space and
+        // back conversion would return different results.
+        // Use x: 0.1 .. 0.3, y: 0.3 .. 0.5 to be inside the triangle which can be converted without loss
+        return IntStream.range(77, 115).mapToObj(i -> new byte[] { (byte) ((i / 2) & 0xff), (byte) ((i) & 0xff),
+                (byte) (i & 0xff), (byte) ((i / 3) & 0xff), (byte) (((i - 50) * 3) & 0xff), 3 });
+        // last byte has value 3: c+b valid
+    }
+
+    @ParameterizedTest
+    @MethodSource("xyYValueProvider")
+    public void dpt242BackToBackTest(byte[] value) {
+        final String dpt = "242.600";
+
+        // decode will apply transformation from raw bytes to String internally
+        HSBType hsb = (HSBType) ValueDecoder.decode(dpt, value, HSBType.class);
+        Assertions.assertNotNull(hsb);
+
+        // encoding will return a String in notation defined by Calimero: "(x,xxxx y,yyyy) YY,Y %"
+        String result = ValueEncoder.encode(hsb, dpt);
+
+        // for back to back test, compare numerical values to allow tolerances
+        double dx = (((value[0] & 0xff) << 8) | (value[1] & 0xff)) / 65535.0;
+        double dy = (((value[2] & 0xff) << 8) | (value[3] & 0xff)) / 65535.0;
+        double dY = ((double) (value[4] & 0xff)) * 100.0 / 255.0;
+
+        Matcher matcher = ValueDecoder.XYY_PATTERN.matcher(result);
+        Assertions.assertTrue(matcher.matches());
+        String stringx = matcher.group("x");
+        String stringy = matcher.group("y");
+        String stringY = matcher.group("Y");
+        Assertions.assertNotNull(stringx);
+        Assertions.assertNotNull(stringy);
+        Assertions.assertNotNull(stringY);
+        double rx = Double.parseDouble(stringx.replace(',', '.'));
+        double ry = Double.parseDouble(stringy.replace(',', '.'));
+        double rY = Double.parseDouble(stringY.replace(',', '.'));
+
+        final double tolerance = 0.001;
+        if ((Math.abs(dx - rx) > tolerance) || (Math.abs(dy - ry) > tolerance)
+                || (Math.abs(dY - rY) > tolerance * 100)) {
+            // print failures in a useful format
+            Assertions.assertEquals(String.format("(%.4f %.4f) %.1f %%", dx, dy, dY), result);
+        }
     }
 }