[knx] Fix decoding of DPT 242.600 and add tests (#14875)

* [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>
This commit is contained in:
Holger Friedrich 2023-04-30 14:03:54 +02:00 committed by GitHub
parent 6e2dddfe73
commit 0437595678
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 15 deletions

View 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
*/

View 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 });
}
}
}

View 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);
}
}
}