From 67c3e1063a74bd733211414b6e660f2e14978596 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sat, 29 Aug 2026 14:46:15 +0300 Subject: [PATCH] lwm2m: fix_bug UNSIGNED_INTEGER-values-encoding-for-LwM2M-multi-resources --- .../transport/lwm2m/Lwm2mTestHelper.java | 3 +- .../rpc/sql/RpcLwm2mIntegrationWriteTest.java | 169 ++++++++++++++++-- .../lwm2m/utils/LwM2MTransportUtil.java | 14 +- .../lwm2m/utils/LwM2mValueConverterImpl.java | 24 +++ .../common/transport/util/JsonUtils.java | 3 + 5 files changed, 196 insertions(+), 17 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java index 5d8f4f1c8b..72994c7223 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java @@ -43,7 +43,8 @@ public class Lwm2mTestHelper { public static final int RESOURCE_ID_14 = 14; public static final int RESOURCE_ID_15 = 15; public static final int RESOURCE_ID_120 = 120; // INTEGER_VALUE - public static final int RESOURCE_ID_125 = 125; // UNSIGNED_INTEGER_VALUE + public static final int RESOURCE_ID_125 = 125; // UNSIGNED_INTEGER_VALUE single-ресурс + public static final int RESOURCE_ID_1125 = 1125; // UNSIGNED_INTEGER_VALUE multi-instance public static final int RESOURCE_ID_5700 = 5700; public static final int RESOURCE_INSTANCE_ID_0 = 0; public static final int RESOURCE_INSTANCE_ID_2 = 2; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java index c409d4269d..dcbe5f49b1 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java @@ -82,22 +82,112 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes } /** - * update SingleResource: - * WriteReplace {"id":"3442/0/120","value":"18446744073709551615} // ULong.MAX + * UnsignedInteger -> SingleResource (WriteReplace): + * {"method": "WriteReplace", "params": {"id": "/3442_1.0/0/125", "value": 18446744073709551615 }} // ULong.MAX * {"result":"CHANGED"} */ @Test public void testWriteReplaceUnIntegerValuesSingleResourceById_Result_CHANGED() throws Exception { - String expectedPath = objectInstanceIdVer_3442 + "/" + RESOURCE_ID_125; - ULong expectedValue = ULong.MAX; - String actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedValue); - ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); - assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); - actualResult = sendRPCReadById(expectedPath); - rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); - String actualValues = rpcActualResult.get("value").asText(); - String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_125 + ", value=" + expectedValue + ", type=" + UNSIGNED_INTEGER.name() + "]"; - assertTrue(actualValues.contains(expected)); + ULong expectedValueId125 = ULong.valueOf(0); + sendRPC_3442_ResourceId_125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.valueOf(-1); + sendRPC_3442_ResourceId_125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.valueOf(-100); + sendRPC_3442_ResourceId_125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.valueOf(Integer.MIN_VALUE); + sendRPC_3442_ResourceId_125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.valueOf(Integer.MAX_VALUE); + sendRPC_3442_ResourceId_125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.valueOf(Long.MIN_VALUE); + sendRPC_3442_ResourceId_125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.valueOf(Long.MAX_VALUE); + sendRPC_3442_ResourceId_125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.MIN; + sendRPC_3442_ResourceId_125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.MAX; + sendRPC_3442_ResourceId_125_WriteReplace(expectedValueId125); + } + + /** + * UnsignedInteger -> SingleResource (WriteUpdate): + * {"method": "WriteUpdate", "params": {"id": "/3442_1.0/0", "value": {"125":18446744073709551615} }} // ULong.MAX + * {"result":"CHANGED"} + */ + @Test + public void testWriteUpdateUnsignedIntegerValuesSingleResourceById_Result_CHANGED() throws Exception { + ULong expectedValueId125 = ULong.valueOf(0); + sendRPC_3442_ResourceId_125_WriteUpdate(expectedValueId125); + expectedValueId125 = ULong.valueOf(-1); + sendRPC_3442_ResourceId_125_WriteUpdate(expectedValueId125); + expectedValueId125 = ULong.valueOf(-100); + sendRPC_3442_ResourceId_125_WriteUpdate(expectedValueId125); + expectedValueId125 = ULong.valueOf(Integer.MIN_VALUE); + sendRPC_3442_ResourceId_125_WriteUpdate(expectedValueId125); + expectedValueId125 = ULong.valueOf(Integer.MAX_VALUE); + sendRPC_3442_ResourceId_125_WriteUpdate(expectedValueId125); + expectedValueId125 = ULong.valueOf(Long.MIN_VALUE); + sendRPC_3442_ResourceId_125_WriteUpdate(expectedValueId125); + expectedValueId125 = ULong.valueOf(Long.MAX_VALUE); + sendRPC_3442_ResourceId_125_WriteUpdate(expectedValueId125); + expectedValueId125 = ULong.MAX; + sendRPC_3442_ResourceId_125_WriteUpdate(expectedValueId125); + expectedValueId125 = ULong.MIN; + sendRPC_3442_ResourceId_125_WriteUpdate(expectedValueId125); + } + + /** + * UnsignedInteger -> MultipleResource (WriteReplace): + * {"method": "WriteReplace", "params": {"id": "/3442_1.0/0/1125/0", "value": 18446744073709551615 }} // ULong.MAX + * {"result":"CHANGED"} + */ + @Test + public void testWriteReplaceUnsignedIntegerValuesMultipleResourceById_Result_CHANGED() throws Exception { + ULong expectedValueId125 = ULong.valueOf(0); + sendRPC_3442_ResourceId_1125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.valueOf(-1); + sendRPC_3442_ResourceId_1125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.valueOf(-100); + sendRPC_3442_ResourceId_1125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.valueOf(Integer.MIN_VALUE); + sendRPC_3442_ResourceId_1125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.valueOf(Integer.MAX_VALUE); + sendRPC_3442_ResourceId_1125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.valueOf(Long.MIN_VALUE); + sendRPC_3442_ResourceId_1125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.valueOf(Long.MAX_VALUE); + sendRPC_3442_ResourceId_1125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.MAX; + sendRPC_3442_ResourceId_1125_WriteReplace(expectedValueId125); + expectedValueId125 = ULong.MIN; + sendRPC_3442_ResourceId_1125_WriteReplace(expectedValueId125); + } + + /** + * UnsignedInteger -> MultipleResource (WriteUpdate): + * {"method": "WriteUpdate", "params": {"id": "/3442_1.0/0", "value": {"1125":{"0":"18446744073709551615"}} }} // 0 ->ULong.MAX + * {"method": "WriteUpdate", "params": {"id": "/3442_1.0/0", "value": {"1125":{"0":"18446744073709551615", "12":"0"}} }} // 0 ->ULong.MAX, 12 - > 0 + * {"result":"CHANGED"} + */ + @Test + public void testWriteUpdateUnsignedIntegerValuesMultipleResourceById_Result_CHANGED() throws Exception { + ULong expectedValueId1125 = ULong.valueOf(0); + sendRPC_3442_ResourceId_1125_WriteUpdate(expectedValueId1125); + expectedValueId1125 = ULong.valueOf(-1); + sendRPC_3442_ResourceId_1125_WriteUpdate(expectedValueId1125); + expectedValueId1125 = ULong.valueOf(-100); + sendRPC_3442_ResourceId_1125_WriteUpdate(expectedValueId1125); + expectedValueId1125 = ULong.valueOf(Integer.MIN_VALUE); + sendRPC_3442_ResourceId_1125_WriteUpdate(expectedValueId1125); + expectedValueId1125 = ULong.valueOf(Integer.MAX_VALUE); + sendRPC_3442_ResourceId_1125_WriteUpdate(expectedValueId1125); + expectedValueId1125 = ULong.valueOf(Long.MIN_VALUE); + sendRPC_3442_ResourceId_1125_WriteUpdate(expectedValueId1125); + expectedValueId1125 = ULong.valueOf(Long.MAX_VALUE); + sendRPC_3442_ResourceId_1125_WriteUpdate(expectedValueId1125); + expectedValueId1125 = ULong.MAX; + sendRPC_3442_ResourceId_1125_WriteUpdate(expectedValueId1125); + expectedValueId1125 = ULong.MIN; + sendRPC_3442_ResourceId_1125_WriteUpdate(expectedValueId1125); } /** @@ -646,4 +736,59 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_120 + ", value=" + expectedValue + ", type=" + INTEGER.name() + "]"; assertTrue(actualValues.contains(expected)); } + + private void sendRPC_3442_ResourceId_125_WriteReplace (ULong expectedValue) throws Exception { + String expectedPath = objectInstanceIdVer_3442 + "/" + RESOURCE_ID_125; + String actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + actualResult = sendRPCReadById(expectedPath); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + String actualValues = rpcActualResult.get("value").asText(); + String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_125 + ", value=" + expectedValue + ", type=" + UNSIGNED_INTEGER.name() + "]"; + assertTrue(actualValues.contains(expected)); + } + + private void sendRPC_3442_ResourceId_125_WriteUpdate(ULong expectedValueId125) throws Exception { + String expectedPath = objectInstanceIdVer_3442; + String expectedValue = "{\"" + RESOURCE_ID_125 + "\":" + expectedValueId125 + "}"; + String actualResult = sendRPCWriteObjectById("WriteUpdate", expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + String expectedPath125 = objectInstanceIdVer_3442 + "/" + RESOURCE_ID_125; + actualResult = sendRPCReadById(expectedPath125); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + String actualValues = rpcActualResult.get("value").asText(); + String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_125 + ", value=" + expectedValueId125 + ", type=" + UNSIGNED_INTEGER.name() + "]"; + assertTrue(actualValues.contains(expected)); + } + + private void sendRPC_3442_ResourceId_1125_WriteReplace(ULong expectedValue) throws Exception { + int resourceInstanceId0 = 0; + String expectedPath = objectInstanceIdVer_3442 + "/" + RESOURCE_ID_1125 + "/" + resourceInstanceId0; + String actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + actualResult = sendRPCReadById(expectedPath); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + String actualValues = rpcActualResult.get("value").asText(); + String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue + ", type=" + UNSIGNED_INTEGER.name() + "]"; + assertTrue(actualValues.contains(expected)); + } + + private void sendRPC_3442_ResourceId_1125_WriteUpdate(ULong expectedValueId1125) throws Exception { + String expectedPath = objectInstanceIdVer_3442; + int resourceInstanceId0 = 0; + String expectedValue = "{\"" + RESOURCE_ID_1125 + "\":{\"" + RESOURCE_ID_0 + "\":" + expectedValueId1125 + "}}"; + String actualResult = sendRPCWriteObjectById("WriteUpdate", expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); + String expectedPath1125 = objectInstanceIdVer_3442 + "/" + RESOURCE_ID_1125 + "/" + resourceInstanceId0; + actualResult = sendRPCReadById(expectedPath1125); + rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + String actualValues = rpcActualResult.get("value").asText(); + String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValueId1125 + ", type=" + UNSIGNED_INTEGER.name() + "]"; + assertTrue(actualValues.contains(expected)); + } } + diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java index cfa21a3bec..9ec8baa0e2 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2MTransportUtil.java @@ -45,6 +45,7 @@ import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdate import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateResult; import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateState; +import java.math.BigInteger; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -186,6 +187,7 @@ public class LwM2MTransportUtil { return FLOAT; case "Integer": case "Long": + case "BigInteger": return INTEGER; case "String": return STRING; @@ -212,10 +214,14 @@ public class LwM2MTransportUtil { try { return Long.valueOf(value.toString()); } catch (NumberFormatException l) { - if (value.getAsFloat() >= Float.MIN_VALUE && value.getAsFloat() <= Float.MAX_VALUE) { - return value.getAsFloat(); - } else { - return value.getAsDouble(); + try { + return new BigInteger(value.toString()); + } catch (NumberFormatException k) { + if (value.getAsFloat() >= Float.MIN_VALUE && value.getAsFloat() <= Float.MAX_VALUE) { + return value.getAsFloat(); + } else { + return value.getAsDouble(); + } } } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java index 43867564ae..a586c95ab1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java @@ -22,6 +22,7 @@ import org.eclipse.leshan.core.node.ObjectLink; import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.node.codec.LwM2mValueConverter; import org.eclipse.leshan.core.util.Hex; +import org.eclipse.leshan.core.util.datatype.ULong; import org.thingsboard.server.common.data.StringUtils; import java.math.BigInteger; @@ -73,6 +74,7 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter { if ((double) value == longValue.doubleValue()) { return longValue; } + break; case STRING: log.debug("Trying to convert String value [{}] to Integer", value); return Long.parseLong((String) value); @@ -80,6 +82,28 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter { break; } break; + case UNSIGNED_INTEGER: + switch (currentType) { + case INTEGER: + if (value instanceof Integer) { + log.debug("Trying to convert Integer value [{}] to Unsigned Integer", value); + return ULong.valueOf(Integer.toUnsignedLong((Integer) value)); + } else if (value instanceof Long) { + log.debug("Trying to convert Long value [{}] to Unsigned Integer", value); + return ULong.valueOf((Long) value); + } else if (value instanceof BigInteger) { + log.debug("Trying to convert Biginteger value [{}] to Unsigned Integer", value); + return ULong.valueOf((BigInteger) value); + } + throw new IllegalArgumentException("Trying to convert value [" + value + "] to Unsigned Integer. Unsupported value type: " + value.getClass()); + case FLOAT: + log.debug("Trying to convert float value [{}] to Unsigned Integer", value); + return ULong.valueOf(((Float) value).longValue()); + case STRING: + log.debug("Trying to convert string value [{}] to Unsigned Integer", value); + return ULong.valueOf((String) value); + } + break; case FLOAT: switch (currentType) { case INTEGER: diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java index 4d5451d4ca..ee7f45e250 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/util/JsonUtils.java @@ -21,6 +21,7 @@ import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; +import java.math.BigInteger; import java.util.List; import java.util.Map; import java.util.regex.Pattern; @@ -58,6 +59,8 @@ public class JsonUtils { return new JsonPrimitive((Integer) value); } else if (value instanceof Long) { return new JsonPrimitive((Long) value); + } else if (value instanceof BigInteger) { + return new JsonPrimitive((BigInteger) value); } else if (value instanceof String) { try { return JsonParser.parseString((String) value);