From 3d8f19c23e887a9563638b5549657ffa77269bfd Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 30 Jun 2021 16:08:59 +0300 Subject: [PATCH 1/9] 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. From 6083036f946c1bcaffd6b51dda9d83b4cfd531f5 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 30 Jun 2021 16:45:03 +0300 Subject: [PATCH 2/9] Lwm2m: opaque merge from master --- .../server/transport/lwm2m/server/client/LwM2mClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1028e1927d..a9af68160c 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 @@ -407,7 +407,7 @@ public class LwM2mClient implements Serializable { public boolean isComposite (LwM2mClientContext clientContext) { return LwM2mVersion.fromVersionStr(registration.getLwM2mVersion()).isComposite() & - clientContext.getProfile(this.profileId).getClientLwM2mSettings().isComposite(); + clientContext.getProfile(this.profileId).getClientLwM2mSettings().isCompositeOperationsSupport(); } } From 2d9717f910b4196700ee69caa1a8c49a06c88058 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 30 Jun 2021 19:10:36 +0300 Subject: [PATCH 3/9] Lwm2m: Opaque fix bug byte[] --- .../server/transport/lwm2m/server/client/LwM2mClient.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 a9af68160c..74edfed357 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 @@ -267,7 +267,7 @@ public class LwM2mClient implements Serializable { public String resourceToString(LwM2mResource resource, String key) { String value = null; StringBuilder builder = new StringBuilder(); - if (resource instanceof LwM2mSingleResource && ((byte[]) resource.getValue()).length > 0) { + if (resource instanceof LwM2mSingleResource) { builder.append("LwM2mSingleResource"); if (key == null) { builder.append(" id=").append(String.valueOf(resource.getId())); @@ -285,7 +285,9 @@ public class LwM2mClient implements Serializable { builder.append(" key=").append(key); } builder.append(" values={"); - builder.append(multiInstanceOpaqueToString((LwM2mMultipleResource) resource)); + if (resource.getInstances().size() > 0) { + builder.append(multiInstanceOpaqueToString((LwM2mMultipleResource) resource)); + } builder.append("}"); builder.append(" type=").append(OPAQUE.toString()); value = builder.toString(); From 385c60a6565b56b39bc62af7b440d5926c822eda Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 30 Jun 2021 18:52:15 +0300 Subject: [PATCH 4/9] UI: LwM2M device profile refactoring object list component models data --- ...ofile-transport-configuration.component.ts | 3 +- ...ofile-transport-configuration.component.ts | 42 +++++++------------ .../lwm2m/lwm2m-object-list.component.ts | 20 ++++----- .../lwm2m/lwm2m-profile-config.models.ts | 5 --- 4 files changed, 24 insertions(+), 46 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts index f5eb23e9e3..7d74cbc387 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts @@ -89,9 +89,8 @@ export class DeviceProfileTransportConfigurationComponent implements ControlValu if (configuration) { delete configuration.type; } - this.deviceProfileTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false}); setTimeout(() => { - this.deviceProfileTransportConfigurationFormGroup.updateValueAndValidity(); + this.deviceProfileTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false}); }, 0); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 777431ea3a..5edcf84345 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -38,7 +38,6 @@ import { INSTANCES, KEY_NAME, Lwm2mProfileConfigModels, - ModelValue, ObjectLwM2M, OBSERVE, OBSERVE_ATTR_TELEMETRY, @@ -49,7 +48,7 @@ import { TELEMETRY } from './lwm2m-profile-config.models'; import { DeviceProfileService } from '@core/http/device-profile.service'; -import { deepClone, isDefinedAndNotNull, isEmpty, isUndefined } from '@core/utils'; +import { deepClone, isDefinedAndNotNull, isEmpty } from '@core/utils'; import { JsonArray, JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; import { Direction } from '@shared/models/page/sort-order'; import _ from 'lodash'; @@ -226,32 +225,30 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private initWriteValue = (): void => { - const modelValue = {objectIds: [], objectsList: []} as ModelValue; - modelValue.objectIds = this.getObjectsFromJsonAllConfig(); - if (modelValue.objectIds.length > 0) { + const objectIds = this.getObjectsFromJsonAllConfig(); + if (objectIds.length > 0) { const sortOrder = { property: 'id', direction: Direction.ASC }; - this.deviceProfileService.getLwm2mObjects(sortOrder, modelValue.objectIds, null).subscribe( + this.deviceProfileService.getLwm2mObjects(sortOrder, objectIds, null).subscribe( (objectsList) => { - modelValue.objectsList = objectsList; - this.updateWriteValue(modelValue); + this.updateWriteValue(objectsList); } ); } else { - this.updateWriteValue(modelValue); + this.updateWriteValue([]); } } - private updateWriteValue = (value: ModelValue): void => { + private updateWriteValue = (value: ObjectLwM2M[]): void => { const fwResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateResource) ? this.configurationValue.clientLwM2mSettings.fwUpdateResource : ''; const swResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.swUpdateResource) ? this.configurationValue.clientLwM2mSettings.swUpdateResource : ''; this.lwm2mDeviceProfileFormGroup.patchValue({ objectIds: value, - observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value.objectsList), + observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value), bootstrap: this.configurationValue.bootstrap, clientLwM2mSettings: { clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, @@ -334,6 +331,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const isNotZeroInstanceId = (instance) => !instance.includes('/0/'); return attribute.some(isNotZeroInstanceId) || telemetry.some(isNotZeroInstanceId); } + private addInstances = (attribute: string[], telemetry: string[], clientObserveAttrTelemetry: ObjectLwM2M[]): void => { const instancesPath = attribute.concat(telemetry) .filter(instance => !instance.includes('/0/')) @@ -454,21 +452,13 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro }); } }); - if (isUndefined(this.configurationValue.observeAttr)) { - this.configurationValue.observeAttr = { - observe: observeArray, - attribute: attributeArray, - telemetry: telemetryArray, - keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew), - attributeLwm2m: attributes - }; - } else { - this.configurationValue.observeAttr.observe = observeArray; - this.configurationValue.observeAttr.attribute = attributeArray; - this.configurationValue.observeAttr.telemetry = telemetryArray; - this.configurationValue.observeAttr.keyName = this.sortObjectKeyPathJson(KEY_NAME, keyNameNew); - this.configurationValue.observeAttr.attributeLwm2m = attributes; - } + this.configurationValue.observeAttr = { + observe: observeArray, + attribute: attributeArray, + telemetry: telemetryArray, + keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew), + attributeLwm2m: attributes + }; } sortObjectKeyPathJson = (key: string, value: object): object => { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts index 62d96b69e0..c3a0e1e4b3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts @@ -28,7 +28,7 @@ import { import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Observable } from 'rxjs'; import { distinctUntilChanged, filter, mergeMap, share, tap } from 'rxjs/operators'; -import { ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT } from './lwm2m-profile-config.models'; +import { ObjectLwM2M, PAGE_SIZE_LIMIT } from './lwm2m-profile-config.models'; import { DeviceProfileService } from '@core/http/device-profile.service'; import { Direction } from '@shared/models/page/sort-order'; import { isDefined, isDefinedAndNotNull, isString } from '@core/utils'; @@ -54,7 +54,6 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V private requiredValue: boolean; private dirty = false; - private modelValue: Array = []; lwm2mListFormGroup: FormGroup; objectsList: Array = []; @@ -92,7 +91,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V this.lwm2mListFormGroup.valueChanges.subscribe((value) => { let formValue = null; if (this.lwm2mListFormGroup.valid) { - formValue = value; + formValue = value.objectsList; } this.propagateChange(formValue); }); @@ -139,15 +138,13 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V } } - writeValue(value: ModelValue): void { + writeValue(value: ObjectLwM2M[]): void { this.searchText = ''; if (isDefinedAndNotNull(value)) { - if (Array.isArray(value.objectIds)) { - this.modelValue = value.objectIds; - this.objectsList = value.objectsList; + if (Array.isArray(value)) { + this.objectsList = value; } else { this.objectsList = []; - this.modelValue = []; } this.lwm2mListFormGroup.patchValue({objectsList: this.objectsList}, {emitEvent: false}); this.dirty = false; @@ -161,8 +158,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V } private add(object: ObjectLwM2M): void { - if (isDefinedAndNotNull(this.modelValue) && this.modelValue.indexOf(object.keyId) === -1) { - this.modelValue.push(object.keyId); + if (isDefinedAndNotNull(this.objectsList) && this.objectsList.findIndex(item => item.keyId === object.keyId) === -1) { this.objectsList.push(object); this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList); this.addList.next(this.objectsList); @@ -171,12 +167,10 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V } remove = (object: ObjectLwM2M): void => { - let index = this.objectsList.indexOf(object); + const index = this.objectsList.indexOf(object); if (index >= 0) { this.objectsList.splice(index, 1); this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList); - index = this.modelValue.indexOf(object.keyId); - this.modelValue.splice(index, 1); this.removeList.next(object); this.clear(); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index b550e4f5ae..a6bfd39eb6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -127,11 +127,6 @@ export const PowerModeTranslationMap = new Map( ] ); -export interface ModelValue { - objectIds: string[]; - objectsList: ObjectLwM2M[]; -} - export interface BootstrapServersSecurityConfig { shortId: number; lifetime: number; From 8e78b65e772afa36e8ce46b1e9ff6ac31612af90 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 30 Jun 2021 19:07:27 +0300 Subject: [PATCH 5/9] UI: LwM2M device profile typo data model --- .../lwm2m-device-profile-transport-configuration.component.ts | 3 +-- .../profile/device/lwm2m/lwm2m-profile-config.models.ts | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 5edcf84345..bb9c3c87f6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -40,7 +40,6 @@ import { Lwm2mProfileConfigModels, ObjectLwM2M, OBSERVE, - OBSERVE_ATTR_TELEMETRY, PowerMode, PowerModeTranslationMap, RESOURCES, @@ -517,7 +516,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } removeObjectsList = (value: ObjectLwM2M): void => { - const objectsOld = this.lwm2mDeviceProfileFormGroup.get(OBSERVE_ATTR_TELEMETRY).value.clientLwM2M; + const objectsOld = this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value; const isIdIndex = (element) => element.keyId === value.keyId; const index = objectsOld.findIndex(isIdIndex); if (index >= 0) { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index a6bfd39eb6..d9c2eb87ca 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -20,7 +20,6 @@ export const PAGE_SIZE_LIMIT = 50; export const INSTANCES = 'instances'; export const INSTANCE = 'instance'; export const RESOURCES = 'resources'; -export const OBSERVE_ATTR_TELEMETRY = 'observeAttrTelemetry'; export const OBSERVE = 'observe'; export const ATTRIBUTE = 'attribute'; export const TELEMETRY = 'telemetry'; From bd558ed4c68cc63f62266ae107ffc0afca8aebc5 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 30 Jun 2021 19:41:22 +0300 Subject: [PATCH 6/9] UI: LwM2M device profile refactoring select attributes and telemetry --- ...serve-attr-telemetry-instances.component.html | 5 ++++- ...observe-attr-telemetry-instances.component.ts | 16 ++++++++++++++++ ...observe-attr-telemetry-resources.component.ts | 4 ++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.html index e9e805177f..2ccfd45616 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.html @@ -32,6 +32,7 @@ @@ -40,14 +41,16 @@
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.ts index 04528e4abc..adf0d60db0 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.ts @@ -161,6 +161,13 @@ export class Lwm2mObserveAttrTelemetryInstancesComponent implements ControlValue const resources = deepClone(instance.get('resources').value as ResourceLwM2M[]); if (value && type === 'observe') { resources.forEach(resource => resource[type] = resource.telemetry || resource.attribute); + } else if (!value && type !== 'observe') { + resources.forEach(resource => { + resource[type] = value; + if (resource.observe && !(resource.telemetry || resource.attribute)) { + resource.observe = false; + } + }); } else { resources.forEach(resource => resource[type] = value); } @@ -190,6 +197,15 @@ export class Lwm2mObserveAttrTelemetryInstancesComponent implements ControlValue return isDefinedAndNotNull(resources) && resources.every(resource => resource[type]); } + disableObserve(instance: AbstractControl): boolean { + return this.disabled || !( + this.getIndeterminate(instance, 'telemetry') || + this.getIndeterminate(instance, 'attribute') || + this.getChecked(instance, 'telemetry') || + this.getChecked(instance, 'attribute') + ); + } + get isExpend(): boolean { return this.instancesFormArray.length === 1; } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts index bced631596..8af08d8f36 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts @@ -129,7 +129,7 @@ export class Lwm2mObserveAttrTelemetryResourcesComponent implements ControlValue private updatedResources(resources: ResourceLwM2M[]): void { if (resources.length === this.resourcesFormArray.length) { - this.resourcesFormArray.patchValue(resources, {emitEvent: false}); + this.resourcesFormArray.patchValue(resources, {onlySelf: true}); } else { if (this.valueChange$) { this.valueChange$.unsubscribe(); @@ -144,7 +144,7 @@ export class Lwm2mObserveAttrTelemetryResourcesComponent implements ControlValue if (this.disabled) { this.resourcesFormGroup.disable({emitEvent: false}); } - this.valueChange$ = this.resourcesFormGroup.valueChanges.subscribe(value => { + this.valueChange$ = this.resourcesFormGroup.valueChanges.subscribe(() => { this.updateModel(this.resourcesFormGroup.getRawValue().resources); }); } From 194200da49d08cf4ad3d370716d8b969df4fe6e1 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 1 Jul 2021 10:44:48 +0300 Subject: [PATCH 7/9] UI: LwM2M device profile resource fixed disabled observe checkbox --- ...m-observe-attr-telemetry-resources.component.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts index 8af08d8f36..bca6d927d9 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.ts @@ -164,12 +164,14 @@ export class Lwm2mObserveAttrTelemetryResourcesComponent implements ControlValue form.get('attribute').valueChanges.pipe(startWith(resource.attribute), takeUntil(this.destroy$)), form.get('telemetry').valueChanges.pipe(startWith(resource.telemetry), takeUntil(this.destroy$)) ]).subscribe(([attribute, telemetry]) => { - if (attribute || telemetry) { - form.get('observe').enable({emitEvent: false}); - } else { - form.get('observe').disable({emitEvent: false}); - form.get('observe').patchValue(false, {emitEvent: false}); - form.get('attributes').patchValue({}, {emitEvent: false}); + if (!this.disabled) { + if (attribute || telemetry) { + form.get('observe').enable({emitEvent: false}); + } else { + form.get('observe').disable({emitEvent: false}); + form.get('observe').patchValue(false, {emitEvent: false}); + form.get('attributes').patchValue({}, {emitEvent: false}); + } } }); return form; From 31a86d0555d31fbd4a674dee24ec4b8d73c40ace Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 30 Jun 2021 18:45:20 +0300 Subject: [PATCH 8/9] Lwm2m: Write Composite --- .../DefaultLwM2mDownlinkMsgHandler.java | 35 ++++++++++--------- .../downlink/LwM2mDownlinkMsgHandler.java | 7 ++-- .../rpc/DefaultLwM2MRpcRequestHandler.java | 25 ++++++------- 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index 1599d73104..e0dea149e5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -61,9 +61,8 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService; import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MReadCompositeRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MWriteResponseCompositeCallback; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.rpc.composite.RpcWriteCompositeRequest; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; @@ -73,7 +72,6 @@ import java.util.Collection; import java.util.Date; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; @@ -136,19 +134,6 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im sendCompositeRequest(client, downlink, this.config.getTimeout(), callback); } - @Override - public void sendWriteCompositeRequest(LwM2mClient client, Map nodes, DefaultLwM2MUplinkMsgHandler handler) { -// ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); - TbLwM2MWriteResponseCompositeCallback callback = new TbLwM2MWriteResponseCompositeCallback(handler, logService, client, null); - ContentFormat contentFormat = ContentFormat.SENML_JSON; - try { - WriteCompositeRequest downlink = new WriteCompositeRequest(contentFormat, nodes); - sendWriteCompositeRequest(client, downlink, this.config.getTimeout(), callback); - } catch (Exception e) { - callback.onError(JacksonUtil.toString(nodes), e); - } - } - @Override public void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback callback) { validateVersionedId(client, request); @@ -258,6 +243,17 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im } } + @Override + public void sendWriteCompositeRequest(LwM2mClient client, RpcWriteCompositeRequest rpcWriteCompositeRequest, DownlinkRequestCallback callback) { + ContentFormat contentFormat = ContentFormat.SENML_JSON; + try { + WriteCompositeRequest downlink = new WriteCompositeRequest(contentFormat, rpcWriteCompositeRequest.getNodes()); + sendWriteCompositeRequest(client, downlink, this.config.getTimeout(), callback); + } catch (Exception e) { + callback.onError(JacksonUtil.toString(rpcWriteCompositeRequest), e); + } + } + @Override public void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback callback) { LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); @@ -330,7 +326,12 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im context.getServer().send(registration, request, timeoutInMs, response -> { executor.submit(() -> { try { - callback.onSuccess(request, response); + if (response.isSuccess()) { + callback.onSuccess(request, response); + } + else { + callback.onValidationError(request.getNodes().values().toString(), response.getErrorMessage()); + } } catch (Exception e) { log.error("[{}] failed to process successful response [{}] ", registration.getEndpoint(), response, e); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java index 04c63aec17..871a59a63b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java @@ -23,6 +23,7 @@ import org.eclipse.leshan.core.request.ObserveRequest; import org.eclipse.leshan.core.request.ReadCompositeRequest; import org.eclipse.leshan.core.request.ReadRequest; import org.eclipse.leshan.core.request.WriteAttributesRequest; +import org.eclipse.leshan.core.request.WriteCompositeRequest; import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.response.DeleteResponse; import org.eclipse.leshan.core.response.DiscoverResponse; @@ -31,13 +32,13 @@ import org.eclipse.leshan.core.response.ObserveResponse; import org.eclipse.leshan.core.response.ReadCompositeResponse; import org.eclipse.leshan.core.response.ReadResponse; import org.eclipse.leshan.core.response.WriteAttributesResponse; +import org.eclipse.leshan.core.response.WriteCompositeResponse; import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MReadCompositeRequest; -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.rpc.composite.RpcWriteCompositeRequest; import java.util.List; -import java.util.Map; import java.util.Set; public interface LwM2mDownlinkMsgHandler { @@ -66,7 +67,7 @@ public interface LwM2mDownlinkMsgHandler { void sendWriteReplaceRequest(LwM2mClient client, TbLwM2MWriteReplaceRequest request, DownlinkRequestCallback callback); - void sendWriteCompositeRequest(LwM2mClient client, Map nodes, DefaultLwM2MUplinkMsgHandler handler); + void sendWriteCompositeRequest(LwM2mClient client, RpcWriteCompositeRequest nodes, DownlinkRequestCallback callback); void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback callback); 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 ee195dbbe3..147a8a5131 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 @@ -52,6 +52,7 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteRespon import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MReadCompositeCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MReadCompositeRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MWriteResponseCompositeCallback; import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; import org.thingsboard.server.transport.lwm2m.server.rpc.composite.RpcReadCompositeRequest; import org.thingsboard.server.transport.lwm2m.server.rpc.composite.RpcReadResponseCompositeCallback; @@ -247,22 +248,18 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { } /** - * 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 + * WriteComposite {"nodes":{"/3/0/14":"+04", "/1/0/2":100, "/5/0/1":"coap://localhost:5685"}} + * {"result":"CHANGED"} + * Map nodes = new HashMap<>(); + * nodes.put("/3/0/14", "+02"); + * nodes.put("/1/0/2", 100); + * nodes.put("/5/0/1", "coap://localhost:5685"); */ private void sendWriteCompositeRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { - RpcWriteCompositeRequest nodes = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteCompositeRequest.class); -// TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId) -// .value(requestBody.getValue()) -// .timeout(this.config.getTimeout()).build(); -// var mainCallback = new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionedId); -// var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); -// downlinkHandler.sendWriteReplaceRequest(client, request, rpcCallback); + RpcWriteCompositeRequest rpcWriteCompositeRequest = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteCompositeRequest.class); + var mainCallback = new TbLwM2MWriteResponseCompositeCallback(uplinkHandler, logService, client, null); + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendWriteCompositeRequest(client, rpcWriteCompositeRequest, rpcCallback); } private void sendCancelObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { From a482a30198cd9a5e37f8de590e301bb9b7005f58 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 1 Jul 2021 13:42:03 +0300 Subject: [PATCH 9/9] Merge Opaque resource conversion to String --- .../lwm2m/server/LwM2mTransportUtil.java | 79 +++++++++++++++++++ .../lwm2m/server/client/LwM2mClient.java | 71 ----------------- .../server/client/LwM2mClientContext.java | 2 + .../server/client/LwM2mClientContextImpl.java | 7 ++ .../DefaultLwM2mDownlinkMsgHandler.java | 3 +- .../rpc/DefaultLwM2MRpcRequestHandler.java | 34 ++++---- .../server/rpc/RpcReadResponseCallback.java | 4 +- .../RpcReadResponseCompositeCallback.java | 4 +- 8 files changed, 111 insertions(+), 93 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index 5930825894..6fb5fd195e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -22,8 +22,10 @@ import org.eclipse.leshan.core.attributes.Attribute; import org.eclipse.leshan.core.attributes.AttributeSet; import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ResourceModel; +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.CodecException; import org.eclipse.leshan.core.request.SimpleDownlinkRequest; import org.eclipse.leshan.core.request.WriteAttributesRequest; @@ -48,6 +50,7 @@ import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION; @@ -371,4 +374,80 @@ public class LwM2mTransportUtil { return lwm2mResourceValue; } + public static Optional contentToString(Object content) { + try { + String value = null; + LwM2mResource resource = null; + String key = null; + if (content instanceof Map) { + Map contentAsMap = (Map) content; + if (contentAsMap.size() == 1) { + for (Map.Entry kv : contentAsMap.entrySet()) { + if (kv.getValue() instanceof LwM2mResource) { + key = kv.getKey().toString(); + resource = (LwM2mResource) kv.getValue(); + } + } + } + } else if (content instanceof LwM2mResource) { + resource = (LwM2mResource) content; + } + if (resource != null && resource.getType() == OPAQUE) { + value = opaqueResourceToString(resource, key); + } + value = value == null ? content.toString() : value; + return Optional.of(value); + } catch (Exception e) { + log.debug("Failed to convert content " + content + " to string", e); + return Optional.ofNullable(content != null ? content.toString() : null); + } + } + + private static String opaqueResourceToString(LwM2mResource resource, String key) { + String value = null; + StringBuilder builder = new StringBuilder(); + if (resource instanceof LwM2mSingleResource) { + 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={"); + if (resource.getInstances().size() > 0) { + builder.append(multiInstanceOpaqueToString((LwM2mMultipleResource) resource)); + } + builder.append("}"); + builder.append(" type=").append(OPAQUE.toString()); + value = builder.toString(); + } + return value; + } + + private static String multiInstanceOpaqueToString(LwM2mMultipleResource resource) { + StringBuilder builder = new StringBuilder(); + resource.getInstances().values() + .forEach(v -> 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); + } + return builder.toString(); + } + + private static String opaqueToString(byte[] value) { + String opaque = Hex.encodeHexString(value); + return opaque.length() > 1024 ? opaque.substring(0, 1024) : opaque; + } + } 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 74edfed357..d88e0131b1 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 @@ -243,74 +243,7 @@ public class LwM2mClient implements Serializable { .getObjectModel(pathIds.getObjectId()) : null; } - 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(); - if (resource instanceof LwM2mSingleResource) { - 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={"); - if (resource.getInstances().size() > 0) { - builder.append(multiInstanceOpaqueToString((LwM2mMultipleResource) resource)); - } - builder.append("}"); - builder.append(" type=").append(OPAQUE.toString()); - value = builder.toString(); - } - return value; - } - private String multiInstanceOpaqueToString(LwM2mMultipleResource resource) { - StringBuilder builder = new StringBuilder(); - 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); - } - return builder.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, LwM2mValueConverter converter) { @@ -407,9 +340,5 @@ 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().isCompositeOperationsSupport(); - } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java index a7305177e1..643ab61737 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java @@ -60,4 +60,6 @@ public interface LwM2mClientContext { void removeCredentials(TransportProtos.SessionInfoProto sessionInfo); void sendMsgsAfterSleeping(LwM2mClient lwM2MClient); + + boolean isComposite(LwM2mClient client); } 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 04c4cc6bcd..b861ce4cd0 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 @@ -35,6 +35,7 @@ import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; +import org.thingsboard.server.transport.lwm2m.config.LwM2mVersion; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; @@ -339,6 +340,12 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { return lwM2mClientsByRegistrationId.values().stream().filter(e -> deviceId.equals(e.getDeviceId())).findFirst().orElse(null); } + @Override + public boolean isComposite(LwM2mClient client) { + return LwM2mVersion.fromVersionStr(client.getRegistration().getLwM2mVersion()).isComposite() & + getProfile(client.getProfileId()).getClientLwM2mSettings().isCompositeOperationsSupport(); + } + private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config .getModelProvider()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index e0dea149e5..619cbdd7a0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -328,8 +328,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im try { if (response.isSuccess()) { callback.onSuccess(request, response); - } - else { + } else { callback.onValidationError(request.getNodes().values().toString(), response.getErrorMessage()); } } catch (Exception e) { 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 147a8a5131..7954cdda98 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 @@ -98,7 +98,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { return; } try { - if (operationType.isHasObjectId()) { + if (operationType.isHasObjectId()) { String objectId = getIdFromParameters(client, rpcRequst); switch (operationType) { case READ: @@ -132,23 +132,21 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); } } else if (operationType.isComposite()) { - 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; - } + if (clientContext.isComposite(client)) { + 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"); + } } else { switch (operationType) { case OBSERVE_CANCEL_ALL: 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 3a53ef5d5d..0a5fa70b5b 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 @@ -24,6 +24,8 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCal import java.util.Optional; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.contentToString; + public class RpcReadResponseCallback, T extends ReadResponse> extends RpcLwM2MDownlinkCallback { public RpcReadResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { @@ -32,6 +34,6 @@ public class RpcReadResponseCallback, T extends ReadRe @Override protected Optional serializeSuccessfulResponse(T response) { - return client.contentToString (response.getContent()); + return 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 e73fb54ca7..11f6849410 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 @@ -25,6 +25,8 @@ import org.thingsboard.server.transport.lwm2m.server.rpc.RpcLwM2MDownlinkCallbac import java.util.Optional; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.contentToString; + public class RpcReadResponseCompositeCallback, T extends ReadCompositeResponse> extends RpcLwM2MDownlinkCallback { public RpcReadResponseCompositeCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { @@ -33,6 +35,6 @@ public class RpcReadResponseCompositeCallback, T exten @Override protected Optional serializeSuccessfulResponse(T response) { - return client.contentToString (response.getContent()); + return contentToString(response.getContent()); } }