diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java index 034d6b4686..b1d0ba6dfd 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java @@ -170,7 +170,7 @@ public class TbUtils { byte[].class, int.class, boolean.class))); parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", List.class, int.class, boolean.class))); - parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", + parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", byte[].class, int.class, int.class, boolean.class))); parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", List.class, int.class, int.class, boolean.class))); @@ -186,10 +186,14 @@ public class TbUtils { byte[].class, int.class))); parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", byte[].class, int.class, boolean.class))); + parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", + byte[].class, int.class, int.class, boolean.class))); parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", List.class, int.class))); parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", List.class, int.class, boolean.class))); + parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", + List.class, int.class, int.class, boolean.class))); parserConfig.addImport("toFixed", new MethodStub(TbUtils.class.getMethod("toFixed", double.class, int.class))); parserConfig.addImport("toFixed", new MethodStub(TbUtils.class.getMethod("toFixed", @@ -798,11 +802,11 @@ public class TbUtils { if (offset + length > data.length) { throw new IllegalArgumentException("Offset: " + offset + " and Length: " + length + " is out of bounds for array with length: " + data.length + "!"); } - var bb = ByteBuffer.allocate(8); + var bb = ByteBuffer.allocate(BYTES_LEN_LONG_MAX); if (!bigEndian) { bb.order(ByteOrder.LITTLE_ENDIAN); } - bb.position(bigEndian ? 8 - length : 0); + bb.position(bigEndian ? BYTES_LEN_LONG_MAX - length : 0); bb.put(data, offset, length); bb.position(0); return bb.getLong(); @@ -831,21 +835,21 @@ public class TbUtils { public static float parseBytesToFloat(byte[] data, int offset, int length, boolean bigEndian) { byte[] bytesToNumber = prepareBytesToNumber(data, offset, length, bigEndian); if (length > BYTES_LEN_INT_MAX) { - throw new IllegalArgumentException("Length: " + length + " is too large. Maximum 4 bytes is allowed!"); + throw new IllegalArgumentException("Length: " + length + " is too large. Maximum " + BYTES_LEN_INT_MAX + " bytes is allowed!"); } if (bytesToNumber.length < BYTES_LEN_INT_MAX) { - byte[] extendedBytes = new byte[4]; + byte[] extendedBytes = new byte[BYTES_LEN_INT_MAX]; Arrays.fill(extendedBytes, (byte) 0); System.arraycopy(bytesToNumber, 0, extendedBytes, 0, bytesToNumber.length); bytesToNumber = extendedBytes; } float floatValue = ByteBuffer.wrap(bytesToNumber).getFloat(); - if (!Float.isNaN(floatValue)){ + if (!Float.isNaN(floatValue)) { return floatValue; } else { long longValue = parseBytesToLong(bytesToNumber, offset, length, bigEndian); BigDecimal bigDecimalValue = new BigDecimal(longValue); - return bigDecimalValue.floatValue(); + return bigDecimalValue.floatValue(); } } @@ -861,9 +865,33 @@ public class TbUtils { return parseBytesToDouble(Bytes.toArray(data), offset, bigEndian); } + public static double parseBytesToDouble(List data, int offset, int length, boolean bigEndian) { + return parseBytesToDouble(Bytes.toArray(data), offset, length, bigEndian); + } + public static double parseBytesToDouble(byte[] data, int offset, boolean bigEndian) { - byte[] bytesToNumber = prepareBytesToNumber(data, offset, BYTES_LEN_LONG_MAX, bigEndian); - return ByteBuffer.wrap(bytesToNumber).getDouble(); + return parseBytesToDouble(data, offset, BYTES_LEN_LONG_MAX, bigEndian); + } + + public static double parseBytesToDouble(byte[] data, int offset, int length, boolean bigEndian) { + byte[] bytesToNumber = prepareBytesToNumber(data, offset, length, bigEndian); + if (length > BYTES_LEN_LONG_MAX) { + throw new IllegalArgumentException("Length: " + length + " is too large. Maximum " + BYTES_LEN_LONG_MAX + " bytes is allowed!"); + } + if (bytesToNumber.length < BYTES_LEN_LONG_MAX) { + byte[] extendedBytes = new byte[BYTES_LEN_LONG_MAX]; + Arrays.fill(extendedBytes, (byte) 0); + System.arraycopy(bytesToNumber, 0, extendedBytes, 0, bytesToNumber.length); + bytesToNumber = extendedBytes; + } + double doubleValue = ByteBuffer.wrap(bytesToNumber).getDouble(); + if (!Double.isNaN(doubleValue)) { + return doubleValue; + } else { + BigInteger bigInt = new BigInteger(1, bytesToNumber); + BigDecimal bigDecimalValue = new BigDecimal(bigInt); + return bigDecimalValue.doubleValue(); + } } private static byte[] prepareBytesToNumber(byte[] data, int offset, int length, boolean bigEndian) { diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java index 1df1fc93cc..e0eb90802c 100644 --- a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java @@ -30,6 +30,7 @@ import org.mvel2.execution.ExecutionArrayList; import org.mvel2.execution.ExecutionHashMap; import java.io.IOException; +import java.math.BigDecimal; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -279,36 +280,36 @@ public class TbUtilsTest { Assertions.assertEquals(0, Float.compare(floatVal, TbUtils.parseBytesToFloat(floatValByte, 0))); Assertions.assertEquals(0, Float.compare(floatValRev, TbUtils.parseBytesToFloat(floatValByte, 0, false))); - List floatVaList = Bytes.asList(floatValByte); - Assertions.assertEquals(0, Float.compare(floatVal, TbUtils.parseBytesToFloat(floatVaList, 0))); - Assertions.assertEquals(0, Float.compare(floatValRev, TbUtils.parseBytesToFloat(floatVaList, 0, false))); + List floatValList = Bytes.asList(floatValByte); + Assertions.assertEquals(0, Float.compare(floatVal, TbUtils.parseBytesToFloat(floatValList, 0))); + Assertions.assertEquals(0, Float.compare(floatValRev, TbUtils.parseBytesToFloat(floatValList, 0, false))); // 4 294 967 295L == {0xFF, 0xFF, 0xFF, 0xFF} - floatValByte = new byte[] {-1, -1, -1, -1}; + floatValByte = new byte[]{-1, -1, -1, -1}; float floatExpectedBe = 4294.9673f; float floatExpectedLe = 4.2949673E9f; float actualBe = TbUtils.parseBytesToFloat(floatValByte, 0, 4, true); - Assertions.assertEquals(0, Float.compare(floatExpectedBe, actualBe/1000000)); + Assertions.assertEquals(0, Float.compare(floatExpectedBe, actualBe / 1000000)); Assertions.assertEquals(0, Float.compare(floatExpectedLe, TbUtils.parseBytesToFloat(floatValByte, 0, false))); - floatVaList = Bytes.asList(floatValByte); - actualBe = TbUtils.parseBytesToFloat(floatVaList, 0); - Assertions.assertEquals(0, Float.compare(floatExpectedBe, actualBe/1000000)); - Assertions.assertEquals(0, Float.compare(floatExpectedLe, TbUtils.parseBytesToFloat(floatVaList, 0, false))); + floatValList = Bytes.asList(floatValByte); + actualBe = TbUtils.parseBytesToFloat(floatValList, 0); + Assertions.assertEquals(0, Float.compare(floatExpectedBe, actualBe / 1000000)); + Assertions.assertEquals(0, Float.compare(floatExpectedLe, TbUtils.parseBytesToFloat(floatValList, 0, false))); // 2 143 289 344L == {0x7F, 0xC0, 0x00, 0x00} - floatValByte = new byte[] {0x7F, (byte) 0xC0, (byte) 0xFF, 0x00}; + floatValByte = new byte[]{0x7F, (byte) 0xC0, (byte) 0xFF, 0x00}; floatExpectedBe = 2143.3547f; floatExpectedLe = -3.984375f; actualBe = TbUtils.parseBytesToFloat(floatValByte, 0, 4, true); - Assertions.assertEquals(0, Float.compare(floatExpectedBe, actualBe/1000000)); - Assertions.assertEquals(0, Float.compare(floatExpectedLe, TbUtils.parseBytesToFloat(floatValByte, 0, 2,false))); + Assertions.assertEquals(0, Float.compare(floatExpectedBe, actualBe / 1000000)); + Assertions.assertEquals(0, Float.compare(floatExpectedLe, TbUtils.parseBytesToFloat(floatValByte, 0, 2, false))); - floatVaList = Bytes.asList(floatValByte); + floatValList = Bytes.asList(floatValByte); floatExpectedLe = 8372479.0f; - actualBe = TbUtils.parseBytesToFloat(floatVaList, 0); - Assertions.assertEquals(0, Float.compare(floatExpectedBe, actualBe/1000000)); - Assertions.assertEquals(0, Float.compare(floatExpectedLe, TbUtils.parseBytesToFloat(floatVaList, 0, 3, false))); + actualBe = TbUtils.parseBytesToFloat(floatValList, 0); + Assertions.assertEquals(0, Float.compare(floatExpectedBe, actualBe / 1000000)); + Assertions.assertEquals(0, Float.compare(floatExpectedLe, TbUtils.parseBytesToFloat(floatValList, 0, 3, false))); } @Test @@ -385,9 +386,38 @@ public class TbUtilsTest { Assertions.assertEquals(0, Double.compare(doubleVal, TbUtils.parseBytesToDouble(doubleValByte, 0))); Assertions.assertEquals(0, Double.compare(doubleValRev, TbUtils.parseBytesToDouble(doubleValByte, 0, false))); - List doubleVaList = Bytes.asList(doubleValByte); - Assertions.assertEquals(0, Double.compare(doubleVal, TbUtils.parseBytesToDouble(doubleVaList, 0))); - Assertions.assertEquals(0, Double.compare(doubleValRev, TbUtils.parseBytesToDouble(doubleVaList, 0, false))); + List doubleValList = Bytes.asList(doubleValByte); + Assertions.assertEquals(0, Double.compare(doubleVal, TbUtils.parseBytesToDouble(doubleValList, 0))); + Assertions.assertEquals(0, Double.compare(doubleValRev, TbUtils.parseBytesToDouble(doubleValList, 0, false))); + + // 4 294 967 295L == {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} + doubleValByte = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1}; + double doubleExpectedBe = 18446.744073709553d; + double doubleExpectedLe = 1.8446744073709552E19d; + double actualBe = TbUtils.parseBytesToDouble(doubleValByte, 0, 8, true); + Assertions.assertEquals(0, Double.compare(doubleExpectedBe, actualBe / 1000000000000000L)); + Assertions.assertEquals(0, Double.compare(doubleExpectedLe, TbUtils.parseBytesToDouble(doubleValByte, 0, false))); + + doubleValList = Bytes.asList(doubleValByte); + Assertions.assertEquals(0, Double.compare(doubleExpectedBe, TbUtils.parseBytesToDouble(doubleValList, 0) / 1000000000000000L)); + Assertions.assertEquals(0, Double.compare(doubleExpectedLe, TbUtils.parseBytesToDouble(doubleValList, 0, false))); + + doubleValByte = new byte[]{0x7F, (byte) 0xC0, (byte) 0xFF, 0x00, 0x7F, (byte) 0xC0, (byte) 0xFF, 0x00}; + doubleExpectedBe = 2387013.651780523d; + doubleExpectedLe = 7.234601680440024E-304d; + actualBe = TbUtils.parseBytesToDouble(doubleValByte, 0, 8, true); + BigDecimal bigDecimal = new BigDecimal(actualBe); + // We move the decimal point to the left by 301 positions + actualBe = bigDecimal.movePointLeft(301).doubleValue(); + Assertions.assertEquals(0, Double.compare(doubleExpectedBe, actualBe)); + Assertions.assertEquals(0, Double.compare(doubleExpectedLe, TbUtils.parseBytesToDouble(doubleValByte, 0, 8, false))); + doubleValList = Bytes.asList(doubleValByte); + doubleExpectedLe = 5.828674572203954E303d; + actualBe = TbUtils.parseBytesToDouble(doubleValList, 0); + bigDecimal = new BigDecimal(actualBe); + actualBe = bigDecimal.movePointLeft(301).doubleValue(); + Assertions.assertEquals(0, Double.compare(doubleExpectedBe, actualBe)); + Assertions.assertEquals(0, Double.compare(doubleExpectedLe, TbUtils.parseBytesToDouble(doubleValList, 0, 5, false))); } @Test @@ -660,7 +690,8 @@ public class TbUtilsTest { valueActual = TbUtils.parseHexToFloat(valueHexRev, false); Assertions.assertEquals(value, valueActual); } - // If the length is not equal to 8 characters, we process it as an integer (eg "0x0A" for 10.0f). + + // If the length is not equal to 8 characters, we process it as an integer (eg "0x0A" for 10.0f). @Test public void parseHexIntLongToFloat_Test() { Float valueExpected = 10.0f;