Browse Source

tbel: added some float methods

pull/11255/head
nick 2 years ago
parent
commit
7e0a9bbebf
  1. 163
      common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java
  2. 75
      common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java

163
common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java

@ -61,6 +61,7 @@ public class TbUtils {
private static final int HEX_LEN_MIN = -1;
private static final int HEX_LEN_INT_MAX = 8;
private static final int HEX_LEN_LONG_MAX = 16;
private static final int BYTES_LEN_INT_MAX = 4;
private static final int BYTES_LEN_LONG_MAX = 8;
private static final LinkedHashMap<String, String> mdnEncodingReplacements = new LinkedHashMap<>();
@ -117,6 +118,8 @@ public class TbUtils {
String.class)));
parserConfig.addImport("parseFloat", new MethodStub(TbUtils.class.getMethod("parseFloat",
String.class, int.class)));
parserConfig.addImport("parseHexIntLongToFloat", new MethodStub(TbUtils.class.getMethod("parseHexIntLongToFloat",
String.class, boolean.class)));
parserConfig.addImport("parseDouble", new MethodStub(TbUtils.class.getMethod("parseDouble",
String.class)));
parserConfig.addImport("parseLittleEndianHexToInt", new MethodStub(TbUtils.class.getMethod("parseLittleEndianHexToInt",
@ -159,14 +162,18 @@ public class TbUtils {
String.class)));
parserConfig.addImport("parseHexToFloat", new MethodStub(TbUtils.class.getMethod("parseHexToFloat",
String.class, boolean.class)));
parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat",
byte[].class, int.class, boolean.class)));
parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat",
byte[].class, int.class)));
parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat",
List.class, int.class)));
parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat",
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",
byte[].class, int.class, int.class, boolean.class)));
parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat",
List.class, int.class)));
List.class, int.class, int.class, boolean.class)));
parserConfig.addImport("parseLittleEndianHexToDouble", new MethodStub(TbUtils.class.getMethod("parseLittleEndianHexToDouble",
String.class)));
parserConfig.addImport("parseBigEndianHexToDouble", new MethodStub(TbUtils.class.getMethod("parseBigEndianHexToDouble",
@ -201,17 +208,17 @@ public class TbUtils {
Long.class)));
parserConfig.addImport("longToHex", new MethodStub(TbUtils.class.getMethod("longToHex",
Long.class, boolean.class)));
parserConfig.addImport("longToHex", new MethodStub(TbUtils.class.getMethod("longToHex",
parserConfig.addImport("longToHex", new MethodStub(TbUtils.class.getMethod("longToHex",
Long.class, boolean.class, boolean.class)));
parserConfig.addImport("longToHex", new MethodStub(TbUtils.class.getMethod("longToHex",
parserConfig.addImport("longToHex", new MethodStub(TbUtils.class.getMethod("longToHex",
Long.class, boolean.class, boolean.class, int.class)));
parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString",
parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString",
Long.class)));
parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString",
parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString",
Long.class, int.class)));
parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString",
parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString",
Long.class, int.class, boolean.class)));
parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString",
parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString",
Long.class, int.class, boolean.class, boolean.class)));
parserConfig.addImport("floatToHex", new MethodStub(TbUtils.class.getMethod("floatToHex",
Float.class)));
@ -334,10 +341,14 @@ public class TbUtils {
}
public static Integer parseInt(String value, int radix) {
if (StringUtils.isNotBlank(value)) {
String valueP = prepareNumberString(value);
return parseInt(value, radix, true);
}
private static Integer parseInt(String value, int radix, boolean bigEndian) {
String valueP = prepareNumberString(value, bigEndian);
if (valueP != null) {
int radixValue = isValidStringAndRadix(valueP, radix, value);
if (radixValue >= 25 && radixValue <= MAX_RADIX) {
if (radixValue >= 25 && radixValue <= MAX_RADIX) {
return (Integer) compareIntLongValueMinMax(valueP, radixValue, Integer.MAX_VALUE, Integer.MIN_VALUE);
}
return switch (radixValue) {
@ -354,10 +365,14 @@ public class TbUtils {
}
public static Long parseLong(String value, int radix) {
if (StringUtils.isNotBlank(value)) {
String valueP = prepareNumberString(value);
return parseLong(value, radix, true);
}
private static Long parseLong(String value, int radix, boolean bigEndian) {
String valueP = prepareNumberString(value, bigEndian);
if (valueP != null) {
int radixValue = isValidStringAndRadix(valueP, radix, value);
if (radixValue >= 25 && radixValue <= MAX_RADIX) {
if (radixValue >= 25 && radixValue <= MAX_RADIX) {
return (Long) compareIntLongValueMinMax(valueP, radixValue, Long.MAX_VALUE, Long.MIN_VALUE);
}
return switch (radixValue) {
@ -385,6 +400,7 @@ public class TbUtils {
return Integer.parseInt(binaryString, MIN_RADIX);
}
}
private static long parseBinaryStringAsSignedLong(String binaryString) {
if (binaryString.length() != 64) {
// Pad the binary string to 64 bits if it is not already
@ -420,34 +436,58 @@ public class TbUtils {
}
public static Float parseFloat(String value) {
return parseFloat(value, 0);
return parseFloat(value, ZERO_RADIX);
}
public static Float parseFloat(String value, int radix) {
if (StringUtils.isNotBlank(value)) {
String valueP = prepareNumberString(value);
int radixValue = isValidStringAndRadix(valueP, radix, value);
if (radixValue == DEC_RADIX) {
return Float.parseFloat(value);
} else {
int bits = Integer.parseUnsignedInt(valueP, HEX_RADIX);
return Float.intBitsToFloat(bits);
String valueP = prepareNumberString(value, true);
if (valueP != null) {
return parseFloatFromString(value, valueP, radix);
}
return null;
}
private static Float parseFloatFromString(String value, String valueP, int radix) {
int radixValue = isValidStringAndRadix(valueP, radix, value);
if (radixValue == HEX_RADIX) {
int bits = (int) Long.parseLong(valueP, HEX_RADIX);
// Hex representation is a standard IEEE 754 float value (eg "0x41200000" for 10.0f).
return Float.intBitsToFloat(bits);
} else {
return Float.parseFloat(value);
}
}
public static Float parseHexIntLongToFloat(String value, boolean bigEndian) {
String valueP = prepareNumberString(value, bigEndian);
if (valueP != null) {
int radixValue = isValidStringAndRadix(valueP, HEX_RADIX, value);
if (radixValue == HEX_RADIX) {
int bits = (int) Long.parseLong(valueP, HEX_RADIX);
// If the length is not equal to 8 characters, we process it as an integer (eg "0x0A" for 10.0f).
float floatValue = (float) bits;
return Float.valueOf(floatValue);
}
}
return null;
}
public static Double parseDouble(String value) {
int radix = getRadix10_16(value);
return parseDouble(value, radix);
}
public static Double parseDouble(String value, int radix) {
if (value != null) {
String valueP = prepareNumberString(value);
return parseDouble(value, radix, true);
}
private static Double parseDouble(String value, int radix, boolean bigEndian) {
String valueP = prepareNumberString(value, bigEndian);
if (valueP != null) {
int radixValue = isValidStringAndRadix(valueP, radix, value);
if (radixValue == DEC_RADIX) {
return Double.parseDouble(prepareNumberString(value));
return Double.parseDouble(valueP);
} else {
long bits = Long.parseUnsignedLong(valueP, HEX_RADIX);
return Double.longBitsToDouble(bits);
@ -469,9 +509,7 @@ public class TbUtils {
}
public static Integer parseHexToInt(String value, boolean bigEndian) {
String hexValue = prepareNumberString(value);
String hex = bigEndian ? hexValue : reverseHexStringByOrder(hexValue);
return parseInt(hex, HEX_RADIX);
return parseInt(value, HEX_RADIX, bigEndian);
}
public static long parseLittleEndianHexToLong(String hex) {
@ -487,9 +525,7 @@ public class TbUtils {
}
public static Long parseHexToLong(String value, boolean bigEndian) {
String hexValue = prepareNumberString(value);
String hex = bigEndian ? value : reverseHexStringByOrder(hexValue);
return parseLong(hex, HEX_RADIX);
return parseLong(value, HEX_RADIX, bigEndian);
}
public static float parseLittleEndianHexToFloat(String hex) {
@ -505,9 +541,11 @@ public class TbUtils {
}
public static Float parseHexToFloat(String value, boolean bigEndian) {
String hexValue = prepareNumberString(value);
String hex = bigEndian ? value : reverseHexStringByOrder(hexValue);
return parseFloat(hex, HEX_RADIX);
String valueP = prepareNumberString(value, bigEndian);
if (valueP != null) {
return parseFloatFromString(value, valueP, HEX_RADIX);
}
return null;
}
public static double parseLittleEndianHexToDouble(String hex) {
@ -523,13 +561,11 @@ public class TbUtils {
}
public static double parseHexToDouble(String value, boolean bigEndian) {
String hexValue = prepareNumberString(value);
String hex = bigEndian ? value : reverseHexStringByOrder(hexValue);
return parseDouble(hex, HEX_RADIX);
return parseDouble(value, HEX_RADIX, bigEndian);
}
public static ExecutionArrayList<Byte> hexToBytes(ExecutionContext ctx, String value) {
String hex = prepareNumberString(value);
String hex = prepareNumberString(value, true);
int len = hex.length();
if (len % 2 > 0) {
throw new IllegalArgumentException("Hex string must be even-length.");
@ -575,6 +611,7 @@ public class TbUtils {
public static String longToHex(Long l) {
return prepareNumberHexString(l, true, false, HEX_LEN_MIN, HEX_LEN_LONG_MAX);
}
public static String longToHex(Long l, boolean bigEndian) {
return prepareNumberHexString(l, bigEndian, false, HEX_LEN_MIN, HEX_LEN_LONG_MAX);
}
@ -604,7 +641,7 @@ public class TbUtils {
return Long.toString(number, radix);
}
return switch (radix) {
case MIN_RADIX -> Long.toBinaryString(number);
case MIN_RADIX -> Long.toBinaryString(number);
case OCTAL_RADIX -> Long.toOctalString(number);
case DEC_RADIX -> Long.toString(number);
case HEX_RADIX -> prepareNumberHexString(number, bigEndian, pref, -1, -1);
@ -646,13 +683,13 @@ public class TbUtils {
private static String removeLeadingZero_FF(String hex, Long number, int hexLenMax) {
String hexWithoutZero = hex.replaceFirst("^0+(?!$)", ""); // Remove leading zeros except for the last one
hexWithoutZero = hexWithoutZero.length() % 2 > 0 ? "0" + hexWithoutZero : hexWithoutZero;
hexWithoutZero = hexWithoutZero.length() % 2 > 0 ? "0" + hexWithoutZero : hexWithoutZero;
if (number >= 0) {
return hexWithoutZero;
} else {
String hexWithoutZeroFF = hexWithoutZero.replaceFirst("^F+(?!$)", "");
hexWithoutZeroFF = hexWithoutZeroFF.length() % 2 > 0 ? "F" + hexWithoutZeroFF : hexWithoutZeroFF;
if (hexWithoutZeroFF.length() > hexLenMax) {
hexWithoutZeroFF = hexWithoutZeroFF.length() % 2 > 0 ? "F" + hexWithoutZeroFF : hexWithoutZeroFF;
if (hexWithoutZeroFF.length() > hexLenMax) {
return hexWithoutZeroFF.substring(hexWithoutZeroFF.length() - hexLenMax);
} else if (hexWithoutZeroFF.length() == hexLenMax) {
return hexWithoutZeroFF;
@ -668,7 +705,7 @@ public class TbUtils {
public static String floatToHex(Float f, boolean bigEndian) {
// Convert the float to its raw integer bits representation
int bits = Float.floatToRawIntBits(f);
int bits = Float.floatToIntBits(f);
// Format the integer bits as a hexadecimal string
String result = String.format("0x%08X", bits);
@ -719,7 +756,7 @@ public class TbUtils {
if (offset > data.length) {
throw new IllegalArgumentException("Offset: " + offset + " is out of bounds for array with length: " + data.length + "!");
}
if (length > 4) {
if (length > BYTES_LEN_INT_MAX) {
throw new IllegalArgumentException("Length: " + length + " is too large. Maximum 4 bytes is allowed!");
}
if (offset + length > data.length) {
@ -784,10 +821,33 @@ public class TbUtils {
}
public static float parseBytesToFloat(byte[] data, int offset, boolean bigEndian) {
byte[] bytesToNumber = prepareBytesToNumber(data, offset, 4, bigEndian);
return ByteBuffer.wrap(bytesToNumber).getFloat();
return parseBytesToFloat(data, offset, BYTES_LEN_INT_MAX, bigEndian);
}
public static float parseBytesToFloat(List data, int offset, int length, boolean bigEndian) {
return parseBytesToFloat(Bytes.toArray(data), offset, length, bigEndian);
}
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!");
}
if (bytesToNumber.length < BYTES_LEN_INT_MAX) {
byte[] extendedBytes = new byte[4];
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)){
return floatValue;
} else {
long longValue = parseBytesToLong(bytesToNumber, offset, length, bigEndian);
BigDecimal bigDecimalValue = new BigDecimal(longValue);
return bigDecimalValue.floatValue();
}
}
public static double parseBytesToDouble(byte[] data, int offset) {
return parseBytesToDouble(data, offset, true);
@ -931,14 +991,15 @@ public class TbUtils {
}
}
private static String prepareNumberString(String value) {
if (value != null) {
private static String prepareNumberString(String value, boolean bigEndian) {
if (StringUtils.isNotBlank(value)) {
value = value.trim();
value = value.replace("0x", "");
value = value.replace("0X", "");
value = value.replace(",", ".");
return bigEndian ? value : reverseHexStringByOrder(value);
}
return value;
return null;
}
private static int isValidStringAndRadix(String valueP, int radix, String value) {

75
common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java

@ -47,7 +47,7 @@ public class TbUtilsTest {
private ExecutionContext ctx;
private final float floatVal = 29.29824f;
private final Float floatVal = 29.29824f;
private final float floatValRev = -5.948442E7f;
@ -256,7 +256,7 @@ public class TbUtilsTest {
@Test
public void parseFloat() {
String floatValStr = "29.29824";
String floatValStr = floatVal.toString();
Assertions.assertEquals(java.util.Optional.of(floatVal).get(), TbUtils.parseFloat(floatValStr));
String floatValHex = "41EA62CC";
Assertions.assertEquals(0, Float.compare(floatVal, TbUtils.parseHexToFloat(floatValHex)));
@ -274,7 +274,7 @@ public class TbUtilsTest {
}
@Test
public void arseBytesToFloat() {
public void parseBytesToFloat() {
byte[] floatValByte = {65, -22, 98, -52};
Assertions.assertEquals(0, Float.compare(floatVal, TbUtils.parseBytesToFloat(floatValByte, 0)));
Assertions.assertEquals(0, Float.compare(floatValRev, TbUtils.parseBytesToFloat(floatValByte, 0, false)));
@ -282,6 +282,33 @@ public class TbUtilsTest {
List<Byte> 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)));
// 4 294 967 295L == {0xFF, 0xFF, 0xFF, 0xFF}
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(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)));
// 2 143 289 344L == {0x7F, 0xC0, 0x00, 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)));
floatVaList = 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)));
}
@Test
@ -605,15 +632,51 @@ public class TbUtilsTest {
@Test
public void floatToHex_Test() {
Float value = 20.89f;
String expectedHex = "0x41A71EB8";
String valueHexRev = "0xB81EA741";
Float value = 123456789.00f;
String expectedHex = "0x4CEB79A3";
String valueHexRev = "0xA379EB4C";
String actual = TbUtils.floatToHex(value);
Assertions.assertEquals(expectedHex, actual);
Float valueActual = TbUtils.parseHexToFloat(actual);
Assertions.assertEquals(value, valueActual);
valueActual = TbUtils.parseHexToFloat(valueHexRev, false);
Assertions.assertEquals(value, valueActual);
value = 123456789.67f;
expectedHex = "0x4CEB79A3";
valueHexRev = "0xA379EB4C";
actual = TbUtils.floatToHex(value);
Assertions.assertEquals(expectedHex, actual);
valueActual = TbUtils.parseHexToFloat(actual);
Assertions.assertEquals(value, valueActual);
valueActual = TbUtils.parseHexToFloat(valueHexRev, false);
Assertions.assertEquals(value, valueActual);
value = 10.0f;
expectedHex = "0x41200000";
valueHexRev = "0x00002041";
actual = TbUtils.floatToHex(value);
Assertions.assertEquals(expectedHex, actual);
valueActual = TbUtils.parseHexToFloat(actual);
Assertions.assertEquals(value, valueActual);
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).
@Test
public void parseHexIntLongToFloat_Test() {
Float valueExpected = 10.0f;
Float valueActual = TbUtils.parseHexIntLongToFloat("0x0A", true);
Assertions.assertEquals(valueExpected, valueActual);
valueActual = TbUtils.parseHexIntLongToFloat("0x0A", false);
Assertions.assertEquals(valueExpected, valueActual);
valueActual = TbUtils.parseHexIntLongToFloat("0x00000A", true);
Assertions.assertEquals(valueExpected, valueActual);
valueActual = TbUtils.parseHexIntLongToFloat("0x0A0000", false);
Assertions.assertEquals(valueExpected, valueActual);
valueExpected = 2570.0f;
valueActual = TbUtils.parseHexIntLongToFloat("0x000A0A", true);
Assertions.assertEquals(valueExpected, valueActual);
valueActual = TbUtils.parseHexIntLongToFloat("0x0A0A00", false);
Assertions.assertEquals(valueExpected, valueActual);
}
@Test

Loading…
Cancel
Save