From 3d8f19c23e887a9563638b5549657ffa77269bfd Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 30 Jun 2021 16:08:59 +0300 Subject: [PATCH] Lwm2m: opaque --- .../transport/lwm2m/config/LwM2mVersion.java | 9 ++- .../lwm2m/server/client/LwM2mClient.java | 79 ++++++++++++++----- .../server/client/LwM2mClientContextImpl.java | 1 - .../lwm2m/server/client/ResourceValue.java | 12 ++- .../rpc/DefaultLwM2MRpcRequestHandler.java | 38 ++++++--- .../server/rpc/RpcReadResponseCallback.java | 13 +-- .../RpcReadResponseCompositeCallback.java | 3 +- .../uplink/DefaultLwM2MUplinkMsgHandler.java | 12 --- 8 files changed, 104 insertions(+), 63 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2mVersion.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2mVersion.java index 079dc296d9..18288246e4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2mVersion.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2mVersion.java @@ -20,8 +20,8 @@ import org.eclipse.leshan.core.LwM2m.Version; import org.eclipse.leshan.core.request.ContentFormat; public enum LwM2mVersion { - VERSION_1_0(0, Version.V1_0, ContentFormat.TLV), - VERSION_1_1(1, Version.V1_1, ContentFormat.TEXT); + VERSION_1_0(0, Version.V1_0, ContentFormat.TLV, false), + VERSION_1_1(1, Version.V1_1, ContentFormat.TEXT, true); @Getter private final int code; @@ -29,11 +29,14 @@ public enum LwM2mVersion { private final Version version; @Getter private final ContentFormat contentFormat; + @Getter + private final boolean composite; - LwM2mVersion(int code, Version version, ContentFormat contentFormat) { + LwM2mVersion(int code, Version version, ContentFormat contentFormat, boolean composite) { this.code = code; this.version = version; this.contentFormat = contentFormat; + this.composite = composite; } public static LwM2mVersion fromVersion(Version version) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 8cf6f20f36..588c8e77f4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -20,13 +20,13 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ResourceModel; -import org.eclipse.leshan.core.node.LwM2mObject; -import org.eclipse.leshan.core.node.LwM2mObjectInstance; +import org.eclipse.leshan.core.node.LwM2mMultipleResource; 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.node.codec.LwM2mValueConverter; import org.eclipse.leshan.core.request.ContentFormat; +import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.server.model.LwM2mModelProvider; import org.eclipse.leshan.server.registration.Registration; import org.thingsboard.server.common.data.Device; @@ -43,6 +43,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Collection; +import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -52,6 +53,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; +import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_OBJECT_VERSION_DEFAULT; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; @@ -239,36 +241,71 @@ public class LwM2mClient implements Serializable { .getObjectModel(pathIds.getObjectId()) : null; } - public String objectToString(LwM2mObject lwM2mObject) { + public Optional contentToString(Object content) { + String value = null; + LwM2mResource resource = null; + String key = null; + if (content instanceof HashMap && ((HashMap) content).size() == 1) { + key = ((HashMap) content).keySet().toArray()[0].toString(); + if (((HashMap) content).values().toArray()[0] != null) { + if (((HashMap) content).values().toArray()[0] instanceof LwM2mResource) { + resource = (LwM2mResource) ((HashMap) content).values().toArray()[0]; + } + } + } else if (content instanceof LwM2mResource) { + resource = (LwM2mResource) content; + } + if (resource != null && resource.getType() == OPAQUE) { + value = this.resourceToString(resource, key); + } + value = value == null ? content.toString() : value; + return Optional.of(String.format("%s", value)); + } + + public String resourceToString(LwM2mResource resource, String key) { + String value = null; StringBuilder builder = new StringBuilder(); - builder.append("LwM2mObject [id=").append(lwM2mObject.getId()).append(", instances={"); - lwM2mObject.getInstances().forEach((instId, inst) -> { - builder.append(instId).append("=").append(this.instanceToString(inst)).append(", "); - }); - int startInd = builder.lastIndexOf(", "); - if (startInd > 0) { - builder.delete(startInd, startInd + 2); + if (resource instanceof LwM2mSingleResource && ((byte[]) resource.getValue()).length > 0) { + builder.append("LwM2mSingleResource"); + if (key == null) { + builder.append(" id=").append(String.valueOf(resource.getId())); + } else { + builder.append(" key=").append(key); + } + builder.append(" value=").append(opaqueToString((byte[]) resource.getValue())); + builder.append(" type=").append(OPAQUE.toString()); + value = builder.toString(); + } else if (resource instanceof LwM2mMultipleResource) { + builder.append("LwM2mMultipleResource"); + if (key == null) { + builder.append(" id=").append(String.valueOf(resource.getId())); + } else { + builder.append(" key=").append(key); + } + builder.append(" values={"); + builder.append(multiInstanceOpaqueToString((LwM2mMultipleResource) resource)); + builder.append("}"); + builder.append(" type=").append(OPAQUE.toString()); + value = builder.toString(); } - builder.append("}]"); - return builder.toString(); + return value; } - public String instanceToString(LwM2mObjectInstance objectInstance) { + private String multiInstanceOpaqueToString(LwM2mMultipleResource resource) { StringBuilder builder = new StringBuilder(); - builder.append("LwM2mObjectInstance [id=").append(objectInstance.getId()).append(", resources={"); - objectInstance.getResources().forEach((resId, res) -> { - builder.append(resId).append("=").append(this.resourceToString(res)).append(", "); + resource.getInstances().values().stream().map(v -> { + return builder.append(" id=").append(v.getId()).append(" value=").append(Hex.encodeHexString((byte[]) v.getValue())).append(", "); }); int startInd = builder.lastIndexOf(", "); if (startInd > 0) { builder.delete(startInd, startInd + 2); } - builder.append("}]"); return builder.toString(); } - public String resourceToString(LwM2mResource lwM2mResource) { - return lwM2mResource.isMultiInstances() ? lwM2mResource.getInstances().toString() : lwM2mResource.getValue().toString(); + private String opaqueToString(byte[] value) { + String opaque = Hex.encodeHexString(value); + return opaque.length() > 1024 ? opaque.substring(0, 1024) : opaque; } public Collection getNewResourceForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider, @@ -366,5 +403,9 @@ public class LwM2mClient implements Serializable { this.lock = new ReentrantLock(); } + public boolean isComposite (LwM2mClientContext clientContext) { + return LwM2mVersion.fromVersionStr(registration.getLwM2mVersion()).isComposite() & + clientContext.getProfile(this.profileId).getClientLwM2mSettings().isComposite(); + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index 261220f013..178602a16b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -26,7 +26,6 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.transport.TransportDeviceProfileCache; -import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java index 38a1815b06..7f61caf58a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResourceValue.java @@ -21,6 +21,7 @@ import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResourceInstance; import java.io.Serializable; +import java.util.stream.Collectors; @Data public class ResourceValue implements Serializable { @@ -45,9 +46,14 @@ public class ResourceValue implements Serializable { private static TbLwM2MResource toTbLwM2MResource(LwM2mResource lwM2mResource) { if (lwM2mResource.isMultiInstances()) { - TbLwM2MResourceInstance[] instances = (TbLwM2MResourceInstance[]) lwM2mResource.getInstances().values().stream().map(ResourceValue::toTbLwM2MResourceInstance).toArray(); - return new TbLwM2MMultipleResource(lwM2mResource.getId(), lwM2mResource.getType(), instances); - } else { + if ( lwM2mResource.getInstances().values().size() > 0) { + TbLwM2MResourceInstance [] instances = lwM2mResource.getInstances().values().stream().map(ResourceValue::toTbLwM2MResourceInstance).collect(Collectors.toSet()).toArray(new TbLwM2MResourceInstance[0]); + return new TbLwM2MMultipleResource(lwM2mResource.getId(), lwM2mResource.getType(), instances); + } + else { + return new TbLwM2MMultipleResource(lwM2mResource.getId(), lwM2mResource.getType(), new TbLwM2MResourceInstance[0]); + } + } else { return new TbLwM2MSingleResource(lwM2mResource.getId(), lwM2mResource.getValue(), lwM2mResource.getType()); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java index f300e00675..dd263b33e0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java @@ -97,7 +97,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { return; } try { - if (operationType.isHasObjectId()) { + if (operationType.isHasObjectId()) { String objectId = getIdFromParameters(client, rpcRequst); switch (operationType) { case READ: @@ -131,16 +131,23 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); } } else if (operationType.isComposite()) { - switch (operationType) { - case READ_COMPOSITE: - sendReadCompositeRequest(client, rpcRequst); - break; - case WRITE_COMPOSITE: - sendWriteCompositeRequest(client, rpcRequst); - break; - default: - throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); - } + if (client.isComposite(clientContext)) { + switch (operationType) { + case READ_COMPOSITE: + sendReadCompositeRequest(client, rpcRequst); + break; + case WRITE_COMPOSITE: + sendWriteCompositeRequest(client, rpcRequst); + break; + default: + throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); + } + } + else { + this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), + ResponseCode.INTERNAL_SERVER_ERROR.getName(), "This device does not support Composite Operation"); + return; + } } else { switch (operationType) { case OBSERVE_CANCEL_ALL: @@ -239,6 +246,15 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { downlinkHandler.sendWriteReplaceRequest(client, request, rpcCallback); } + /** + * Map nodes = new HashMap<>(); + * nodes.put("/3/0/14", "+02"); + * nodes.put("/1/0/2", 100); + * nodes.put("/5/0/1", "coap://localhost:5685"); + * // defaultLwM2MDownlinkMsgHandler.sendWriteCompositeRequest(lwM2MClient, nodes, this); + * @param client + * @param requestMsg + */ private void sendWriteCompositeRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { RpcWriteCompositeRequest nodes = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteCompositeRequest.class); // TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java index 6f7eb4b2b7..3a53ef5d5d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java @@ -15,9 +15,6 @@ */ package org.thingsboard.server.transport.lwm2m.server.rpc; -import org.eclipse.leshan.core.node.LwM2mObject; -import org.eclipse.leshan.core.node.LwM2mObjectInstance; -import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.request.LwM2mRequest; import org.eclipse.leshan.core.response.ReadResponse; import org.thingsboard.server.common.transport.TransportService; @@ -35,14 +32,6 @@ public class RpcReadResponseCallback, T extends ReadRe @Override protected Optional serializeSuccessfulResponse(T response) { - Object value = null; - if (response.getContent() instanceof LwM2mObject) { - value = client.objectToString((LwM2mObject) response.getContent()); - } else if (response.getContent() instanceof LwM2mObjectInstance) { - value = client.instanceToString((LwM2mObjectInstance) response.getContent()); - } else if (response.getContent() instanceof LwM2mResource) { - value = client.resourceToString((LwM2mResource) response.getContent()); - } - return Optional.of(String.format("%s", value)); + return client.contentToString (response.getContent()); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/composite/RpcReadResponseCompositeCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/composite/RpcReadResponseCompositeCallback.java index 8ce3a0a672..e73fb54ca7 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/composite/RpcReadResponseCompositeCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/composite/RpcReadResponseCompositeCallback.java @@ -16,7 +16,6 @@ package org.thingsboard.server.transport.lwm2m.server.rpc.composite; import org.eclipse.leshan.core.request.LwM2mRequest; -import org.eclipse.leshan.core.request.ReadCompositeRequest; import org.eclipse.leshan.core.response.ReadCompositeResponse; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.gen.transport.TransportProtos; @@ -34,6 +33,6 @@ public class RpcReadResponseCompositeCallback, T exten @Override protected Optional serializeSuccessfulResponse(T response) { - return Optional.of(String.format("%s", response.getContent().toString())); + return client.contentToString (response.getContent()); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index cd8b19c60a..ed2a1802ff 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -91,7 +91,6 @@ import javax.annotation.PreDestroy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -427,17 +426,6 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl if (supportedObjects != null && supportedObjects.size() > 0) { // #1 this.sendReadRequests(lwM2MClient, profile, supportedObjects); - // test composite - String[] paths = new String[]{"/3/0", "/1/0", "/5/0"}; -// String [] paths = new String[] {"/5"}; -// String [] paths = new String[] {"/"}; -// String [] paths = new String[] {"/9"}; -// defaultLwM2MDownlinkMsgHandler.sendReadCompositeRequest(lwM2MClient, paths, this); - Map nodes = new HashMap<>(); - nodes.put("/3/0/14", "+02"); - nodes.put("/1/0/2", 100); - nodes.put("/5/0/1", "coap://localhost:5685"); -// defaultLwM2MDownlinkMsgHandler.sendWriteCompositeRequest(lwM2MClient, nodes, this); this.sendObserveRequests(lwM2MClient, profile, supportedObjects); this.sendWriteAttributeRequests(lwM2MClient, profile, supportedObjects); // Removed. Used only for debug.