lwm2m: add tests

This commit is contained in:
nick 2024-10-17 19:10:50 +03:00
parent 7422ec38e1
commit e5c6be6e08
7 changed files with 267 additions and 49 deletions

View File

@ -129,7 +129,7 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
fireResourceChange(resourceId);
return WriteResponse.success();
} else {
WriteResponse.badRequest("Invalidate value ...");
return WriteResponse.badRequest("Invalidate value ...");
}
case 1:
setPriority((Integer) (value.getValue() instanceof Long ? ((Long) value.getValue()).intValue() : value.getValue()));

View File

@ -20,6 +20,7 @@ import org.eclipse.leshan.core.ResponseCode;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.script.api.tbel.TbUtils;
import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest;
import static org.junit.Assert.assertEquals;
@ -76,7 +77,157 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue + ", type=STRING]";
assertTrue(actualValues.contains(expected));
}
@Test
public void testWriteReplaceValueMultipleResource_Result_CHANGED_Multi_Instance_Resource_must_One() throws Exception {
int resourceInstanceId0 = 0;
String expectedPath = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId0;
// base64/String
String expectedValue = "QUJDREVGRw";
String actualResult = sendRPCWriteStringById("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();
byte[] expectedValue0 = TbUtils.base64ToBytes(expectedValue);
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
// base64/String
expectedValue = "ABCDEFG";
actualResult = sendRPCWriteStringById("WriteReplace", expectedPath, expectedValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expectedValue0 = TbUtils.base64ToBytes(expectedValue);
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
// hexDecimal/String
expectedValue = "01ABCDEF";
actualResult = sendRPCWriteStringById("WriteReplace", expectedPath, expectedValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue.length()/2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
// Integer
Integer expectedIntegerValue = 1234566;
actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedIntegerValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + 4 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
expectedIntegerValue = Integer.MAX_VALUE;
actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedIntegerValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + 4 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
expectedIntegerValue = Integer.MIN_VALUE;
actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedIntegerValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + 4 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
// Long
Long expectedLongValue = 4406483977L;
actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedLongValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + 8 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
expectedLongValue = Long.MAX_VALUE;
actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedLongValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + 8 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
expectedLongValue = Long.MIN_VALUE;
actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedLongValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + 8 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
// Float to byte[]: byte[] bytes = ByteBuffer.allocate(4).putFloat(((Float) value).floatValue()).array();
// Float from byte[]: float f = ByteBuffer.wrap(bytes).getFloat();
Float expectedFloatValue = 8.02f;
actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedFloatValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + 4 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
expectedFloatValue = Float.MAX_VALUE;
actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedFloatValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + 4 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
expectedFloatValue = Float.MIN_VALUE;
actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedFloatValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + 4 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
// Double to byte[]: byte[] bytes = ByteBuffer.allocate(8).putDouble(((Double) value).doubleValue()).array();
// Double from byte[]: double d = ByteBuffer.wrap(bytes).getDouble();
Double expectedDoubleValue = 1022.5906d;
actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedDoubleValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + 4 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
expectedDoubleValue = Double.MAX_VALUE;
actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedDoubleValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + 8 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
expectedDoubleValue = Double.MIN_VALUE;
actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedDoubleValue);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + 8 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
}
/**
* id
@ -88,9 +239,9 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
String expectedPath = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0;
int resourceInstanceId0 = 0;
int resourceInstanceId15 = 15;
String expectedValue0 = "0000ad45675600";
String expectedValue15 = "1525ad45675600cdef";
String expectedValue = "{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId15 + "\":\"" + expectedValue15 + "\"}";
String expectedValue0 = "1525ad45675600cdef";
Integer expectedValue15 = Integer.MAX_VALUE;
String expectedValue = "{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId15 + "\":" + expectedValue15 + "}";
String actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
@ -99,12 +250,12 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
actualResult = sendRPCReadById(expectedPath0);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String actualValues = rpcActualResult.get("value").asText();
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length()/2 + "Bytes, type=OPAQUE]";
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length() / 2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
actualResult = sendRPCReadById(expectedPath15);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId15 + ", value=" + expectedValue15.length()/2 + "Bytes, type=OPAQUE]";
expected = "LwM2mResourceInstance [id=" + resourceInstanceId15 + ", value=" + Integer.toHexString(expectedValue15).length()/2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
}
@ -130,7 +281,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
actualResult = sendRPCReadById(expectedPath0);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String actualValues = rpcActualResult.get("value").asText();
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length()/2 + "Bytes, type=OPAQUE]";
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length() / 2 + "Bytes, type=OPAQUE]";
assertFalse(actualValues.contains(expected));
}
@ -193,16 +344,16 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
String expectedPath0 = expectedPath + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId0;
String expectedPath25 =expectedPath + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId25;
String expectedPath25 = expectedPath + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId25;
actualResult = sendRPCReadById(expectedPath0);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String actualValues = rpcActualResult.get("value").asText();
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length()/2 + "Bytes, type=OPAQUE]";
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length() / 2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
actualResult = sendRPCReadById(expectedPath25);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId25 + ", value=" + expectedValue25.length()/2 + "Bytes, type=OPAQUE]";
expected = "LwM2mResourceInstance [id=" + resourceInstanceId25 + ", value=" + expectedValue25.length() / 2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
}
@ -232,13 +383,14 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
actualResult = sendRPCReadById(expectedPath_19_0 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId0);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String actualValues = rpcActualResult.get("value").asText();
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length()/2 + "Bytes, type=OPAQUE]";
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length() / 2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
actualResult = sendRPCReadById(expectedPath_19_0 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId25);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId25 + ", value=" + expectedValue25.length()/2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected)); actualResult = sendRPCReadByKey(expectedKey3_0_14);
expected = "LwM2mResourceInstance [id=" + resourceInstanceId25 + ", value=" + expectedValue25.length() / 2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
actualResult = sendRPCReadByKey(expectedKey3_0_14);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]";
@ -270,12 +422,12 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
actualResult = sendRPCReadById(expectedPath_19_0 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId0);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String actualValues = rpcActualResult.get("value").asText();
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length()/2 + "Bytes, type=OPAQUE]";
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId0 + ", value=" + expectedValue0.length() / 2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
actualResult = sendRPCReadById(expectedPath_19_0 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId25);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mResourceInstance [id=" + resourceInstanceId25 + ", value=" + expectedValue25.length()/2 + "Bytes, type=OPAQUE]";
expected = "LwM2mResourceInstance [id=" + resourceInstanceId25 + ", value=" + expectedValue25.length() / 2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
}
@ -301,7 +453,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
actualResult = sendRPCReadById(expectedPath19_1_0_2);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String actualValues = rpcActualResult.get("value").asText();
String expected = "LwM2mResourceInstance [id=" + RESOURCE_INSTANCE_ID_2 + ", value=" + expectedValue19_1_0_2.length()/2 + "Bytes, type=OPAQUE]";
String expected = "LwM2mResourceInstance [id=" + RESOURCE_INSTANCE_ID_2 + ", value=" + expectedValue19_1_0_2.length() / 2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
actualResult = sendRPCReadByKey(expectedKey3_0_14);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);

View File

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.transport.lwm2m.server.downlink;
import com.google.gson.JsonParser;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
@ -396,7 +397,12 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
String msgError = "";
if (resourceModelWrite.multiple) {
try {
Map<Integer, Object> value = convertMultiResourceValuesFromRpcBody(request.getValue(), resourceModelWrite.type, request.getObjectId());
Object valueForMultiResource = request.getValue();
if (resultIds.isResourceInstance()) {
String resourceInstance = "{" + resultIds.getResourceInstanceId() + "=" + request.getValue() + "}";
valueForMultiResource = JsonParser.parseString(resourceInstance);
}
Map<Integer, Object> value = convertMultiResourceValuesFromRpcBody(valueForMultiResource, resourceModelWrite.type, request.getObjectId());
downlink = new WriteRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(),
value, resourceModelWrite.type);
} catch (Exception e) {
@ -707,7 +713,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(versionedId));
if (pathIds.isResourceInstance() || pathIds.isResource()) {
ResourceModel resourceModel = client.getResourceModel(versionedId, modelProvider);
if (resourceModel != null && (pathIds.isResourceInstance() || (pathIds.isResource() && !resourceModel.multiple))) {
if (resourceModel != null && !resourceModel.multiple) {
ContentFormat[] desiredFormats;
if (OBJLNK.equals(resourceModel.type)) {
desiredFormats = new ContentFormat[]{ContentFormat.LINK, ContentFormat.CBOR, ContentFormat.SENML_CBOR, ContentFormat.SENML_JSON};

View File

@ -277,17 +277,6 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
private void sendWriteReplaceRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) {
RpcWriteReplaceRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteReplaceRequest.class);
LwM2mPath path = new LwM2mPath(fromVersionedIdToObjectId(versionedId));
if (path.isResource()) {
ResourceModel resourceModel = client.getResourceModel(versionedId, modelProvider);
if (resourceModel != null && resourceModel.multiple) {
try {
Map<Integer, Object> value = convertMultiResourceValuesFromRpcBody(requestBody.getValue(), resourceModel.type, versionedId);
requestBody.setValue(value);
} catch (Exception e) {
}
}
}
TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId)
.value(requestBody.getValue())
.timeout(clientContext.getRequestTimeout(client)).build();
@ -330,7 +319,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
if (versionedId == null) {
if (path.isResourceInstance()) {
setValueToCompositeNodes(client, newNodes, nodes, key, value.toString());
setValueToCompositeNodes(client, newNodes, nodes, key, value);
} else if (path.isResource()) {
validateResource(client, newNodes, nodes, key , value);
} else if (path.isObjectInstance() && value instanceof Map<?, ?>) {
@ -342,7 +331,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
"The WriteComposite operation is only used for SingleResources or/and ResourceInstance.", nodes));
}
} else {
setValueToCompositeNodes(client, newNodes, nodes, versionedId, value.toString());
setValueToCompositeNodes(client, newNodes, nodes, versionedId, value);
}
});
return newNodes;
@ -351,10 +340,10 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
private void validateResource(LwM2mClient client, Map newNodes, Map nodes, String resourceId , Object value) {
if (value instanceof Map<?, ?>) {
((Map<?, ?>) value).forEach((k, v) -> {
setValueToCompositeNodes(client, newNodes, nodes, validateResourceId (resourceId, k.toString(), nodes), v.toString());
setValueToCompositeNodes(client, newNodes, nodes, validateResourceId (resourceId, k.toString(), nodes), v);
});
} else {
setValueToCompositeNodes(client, newNodes, nodes, resourceId, value.toString());
setValueToCompositeNodes(client, newNodes, nodes, resourceId, value);
}
}
@ -368,7 +357,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
}
}
private void setValueToCompositeNodes (LwM2mClient client, Map newNodes, Map nodes, String versionedId , String value) {
private void setValueToCompositeNodes (LwM2mClient client, Map newNodes, Map nodes, String versionedId , Object value) {
// validate value. Must be only primitive, not JsonObject or JsonArray
try {
JsonElement element = JsonUtils.parse(value);
@ -378,7 +367,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
}
// convert value from JsonPrimitive() to resource/ResourceInstance type
ResourceModel resourceModel = client.getResourceModel(versionedId, modelProvider);
Object newValue = convertValueByTypeResource(value, resourceModel.type, versionedId);
Object newValue = convertValueByTypeResource(element, resourceModel.type, versionedId);
// add new value after convert
newNodes.put(fromVersionedIdToObjectId(versionedId), newValue);

View File

@ -16,6 +16,7 @@
package org.thingsboard.server.transport.lwm2m.utils;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.elements.config.Configuration;
import org.eclipse.leshan.core.model.LwM2mModel;
@ -27,7 +28,6 @@ import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.util.Hex;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.StringUtils;
@ -35,7 +35,6 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential;
import org.thingsboard.server.common.data.ota.OtaPackageKey;
import org.thingsboard.server.common.transport.util.JsonUtils;
import org.thingsboard.server.transport.lwm2m.config.TbLwM2mVersion;
import org.thingsboard.server.transport.lwm2m.server.LwM2mOtaConvert;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
@ -62,6 +61,7 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING;
import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
import static org.thingsboard.server.common.transport.util.JsonUtils.convertToJsonObject;
import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_RESULT_ID;
import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_STATE_ID;
import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_RESULT_ID;
@ -180,9 +180,11 @@ public class LwM2MTransportUtil {
*/
public static ResourceModel.Type equalsResourceTypeGetSimpleName(Object value) {
switch (value.getClass().getSimpleName()) {
case "Float":
case "Double":
return FLOAT;
case "Integer":
case "Long":
return INTEGER;
case "String":
return STRING;
@ -199,6 +201,30 @@ public class LwM2MTransportUtil {
}
}
public static Object getJsonPrimitiveValue(JsonPrimitive value) {
if(value.isString()) {
return value.getAsString();
} else if (value.isNumber()){
try {
return Integer.valueOf(value.toString());
} catch (NumberFormatException i) {
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();
}
}
}
} else if (value.isBoolean()){
return value.getAsBoolean();
} else {
return null;
}
}
public static void validateVersionedId(LwM2mClient client, HasVersionedId request) {
String msgExceptionStr = "";
if (request.getObjectId() == null) {
@ -212,22 +238,29 @@ public class LwM2MTransportUtil {
}
public static Map<Integer, Object> convertMultiResourceValuesFromRpcBody(Object value, ResourceModel.Type type, String versionedId) throws Exception {
String valueJsonStr = JacksonUtil.toString(value);
JsonElement element = JsonUtils.parse(valueJsonStr);
return convertMultiResourceValuesFromJson(element, type, versionedId);
if (value instanceof JsonElement) {
return convertMultiResourceValuesFromJson((JsonElement) value, type, versionedId);
} else if (value instanceof Map) {
JsonElement valueConvert = convertToJsonObject((Map<String, ?>) value);
return convertMultiResourceValuesFromJson(valueConvert, type, versionedId);
} else {
return null;
}
}
public static Map<Integer, Object> convertMultiResourceValuesFromJson(JsonElement newValProto, ResourceModel.Type type, String versionedId) {
Map<Integer, Object> newValues = new HashMap<>();
newValProto.getAsJsonObject().entrySet().forEach((obj) -> {
newValues.put(Integer.valueOf(obj.getKey()), convertValueByTypeResource(obj.getValue().getAsString(), type, versionedId));
Object valueByTypeResource = convertValueByTypeResource(obj.getValue(), type, versionedId);
newValues.put(Integer.valueOf(obj.getKey()), valueByTypeResource);
});
return newValues;
}
public static Object convertValueByTypeResource(String value, ResourceModel.Type type, String versionedId) {
return LwM2mValueConverterImpl.getInstance().convertValue(value,
STRING, type, new LwM2mPath(fromVersionedIdToObjectId(versionedId)));
public static Object convertValueByTypeResource(Object value, ResourceModel.Type type, String versionedId) {
Object valueCurrent = getJsonPrimitiveValue((JsonPrimitive) value);
return LwM2mValueConverterImpl.getInstance().convertValue(valueCurrent,
equalsResourceTypeGetSimpleName(valueCurrent), type, new LwM2mPath(fromVersionedIdToObjectId(versionedId)));
}
/**

View File

@ -25,6 +25,7 @@ import org.eclipse.leshan.core.util.Hex;
import org.thingsboard.server.common.data.StringUtils;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Base64;
@ -165,7 +166,19 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter {
}
break;
case OPAQUE:
if (currentType == Type.STRING) {
if (currentType == Type.INTEGER) {
if (value instanceof Integer) {
return ByteBuffer.allocate(4).putInt((Integer) value).array();
} else {
return ByteBuffer.allocate(8).putLong((Long) value).array();
}
} else if (currentType == Type.FLOAT) {
if (value instanceof Float) {
return ByteBuffer.allocate(4).putFloat((Float) value).array();
} else {
return ByteBuffer.allocate(8).putDouble((Double) value).array();
}
} else if (currentType == Type.STRING) {
/** let's assume we received an hexadecimal string */
log.debug("Trying to convert hexadecimal/base64 string [{}] to byte array", value);
try {
@ -178,6 +191,8 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter {
value, resourcePath);
}
}
} else if (currentType == Type.BOOLEAN) {
return new byte[] {(byte)((boolean)value ? 1 : 0)};
}
break;
case OBJLNK:

View File

@ -18,9 +18,11 @@ package org.thingsboard.server.common.transport.util;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
import java.util.List;
import java.util.Map;
public class JsonUtils {
@ -47,9 +49,30 @@ public class JsonUtils {
}
return json;
}
public static JsonElement parse(String params) {
return JsonParser.parseString(params);
public static JsonElement parse(Object value) {
if (value instanceof Integer) {
return new JsonPrimitive((Integer) value);
} else if (value instanceof Long) {
return new JsonPrimitive((Long) value);
} else if (value instanceof String) {
return JsonParser.parseString((String) value);
} else if (value instanceof Boolean) {
return new JsonPrimitive((Boolean) value);
} else if (value instanceof Double) {
return new JsonPrimitive((Double) value);
} else if (value instanceof Float) {
return new JsonPrimitive((Float) value);
} else {
throw new IllegalArgumentException("Unsupported type: " + value.getClass().getSimpleName());
}
}
public static JsonObject convertToJsonObject(Map<String,?> map) {
JsonObject jsonObject = new JsonObject();
for (Map.Entry<String, ?> entry : map.entrySet()) {
jsonObject.add(entry.getKey(), parse(entry.getValue()));
}
return jsonObject;
}
}