From e33420d3a89264b3df099654eea8b3671ca0fbec Mon Sep 17 00:00:00 2001 From: nickAS21 <44275303+nickAS21@users.noreply.github.com> Date: Fri, 30 Apr 2021 08:39:06 +0300 Subject: [PATCH] Lwm2m: firmwareUpdate (#4516) * Lwm2m: firmwareUpdate * Lwm2m: firmwareUpdate with merge master * Lwm2m: firmwareUpdate cleaned * Lwm2m: delete Californium.properties * Lwm2m: merge with master --- .../resource/DefaultTbResourceService.java | 2 +- .../server/common/data/DataConstants.java | 4 +- ...TransportBootstrapServerConfiguration.java | 3 +- .../lwm2m/server/LwM2mNetworkConfig.java | 100 +++++++ .../lwm2m/server/LwM2mServerListener.java | 14 +- .../lwm2m/server/LwM2mTransportHandler.java | 33 +-- .../lwm2m/server/LwM2mTransportRequest.java | 123 +++++---- .../LwM2mTransportServerConfiguration.java | 8 +- .../lwm2m/server/LwM2mTransportService.java | 4 +- .../server/LwM2mTransportServiceImpl.java | 255 ++++++++++++------ .../server/adaptors/LwM2MJsonAdaptor.java | 21 +- .../adaptors/LwM2MTransportAdaptor.java | 4 +- .../lwm2m/server/client/LwM2mClient.java | 18 +- .../server/client/LwM2mClientContextImpl.java | 10 +- .../server/client/LwM2mFirmwareUpdate.java | 27 ++ .../lwm2m-observe-attr-telemetry.component.ts | 15 +- .../assets/locale/locale.constant-en_US.json | 2 +- 17 files changed, 435 insertions(+), 208 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFirmwareUpdate.java diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index 81c59609ff..63c587405a 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -185,7 +185,7 @@ public class DefaultTbResourceService implements TbResourceService { instance.setId(0); List resources = new ArrayList<>(); obj.resources.forEach((k, v) -> { - if (!v.operations.isExecutable()) { + if (v.operations.isReadable()) { LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false); resources.add(lwM2MResourceObserve); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java index d0fdf169fc..094069dafb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java @@ -95,8 +95,8 @@ public class DataConstants { //firmware //telemetry - public static final String CURRENT_FIRMWARE_TITLE = "cur_fw_title"; - public static final String CURRENT_FIRMWARE_VERSION = "cur_fw_version"; + public static final String CURRENT_FIRMWARE_TITLE = "current_fw_title"; + public static final String CURRENT_FIRMWARE_VERSION = "current_fw_version"; public static final String TARGET_FIRMWARE_TITLE = "target_fw_title"; public static final String TARGET_FIRMWARE_VERSION = "target_fw_version"; public static final String TARGET_FIRMWARE_TS = "target_fw_ts"; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java index e531de3628..9e40c736df 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java @@ -55,7 +55,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getCoapConfig; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig; @Slf4j @Component @@ -93,7 +93,6 @@ public class LwM2MTransportBootstrapServerConfiguration { builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort)); /** Define model provider (Create Models )*/ -// builder.setModel(new StaticModel(contextS.getLwM2MTransportConfigServer().getModelsValueCommon())); /** Create credentials */ this.setServerWithCredentials(builder); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java new file mode 100644 index 0000000000..22f822bae6 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java @@ -0,0 +1,100 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server; + +import org.eclipse.californium.core.network.config.NetworkConfig; + +public class LwM2mNetworkConfig { + + public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) { + NetworkConfig coapConfig = new NetworkConfig(); + coapConfig.setInt(NetworkConfig.Keys.COAP_PORT,serverPortNoSec); + coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT,serverSecurePort); + /** + * Example:Property for large packet: + * #NetworkConfig config = new NetworkConfig(); + * #config.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE,32); + * #config.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE,32); + * #config.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE,2048); + * #config.setInt(NetworkConfig.Keys.MAX_RETRANSMIT,3); + * #config.setInt(NetworkConfig.Keys.MAX_TRANSMIT_WAIT,120000); + */ + + /** + * Property to indicate if the response should always include the Block2 option \ + * when client request early blockwise negociation but the response can be sent on one packet. + * - value of false indicate that the server will respond without block2 option if no further blocks are required. + * - value of true indicate that the server will response with block2 option event if no further blocks are required. + * CoAP client will try to use block mode + * or adapt the block size when receiving a 4.13 Entity too large response code + */ + coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true); + /*** + * Property to indicate if the response should always include the Block2 option \ + * when client request early blockwise negociation but the response can be sent on one packet. + * - value of false indicate that the server will respond without block2 option if no further blocks are required. + * - value of true indicate that the server will response with block2 option event if no further blocks are required. + */ + coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true); + + coapConfig.setInt(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, 300000); + /** + * !!! REQUEST_ENTITY_TOO_LARGE CODE=4.13 + * The maximum size of a resource body (in bytes) that will be accepted + * as the payload of a POST/PUT or the response to a GET request in a + * transparent> blockwise transfer. + * This option serves as a safeguard against excessive memory + * consumption when many resources contain large bodies that cannot be + * transferred in a single CoAP message. This option has no impact on + * *manually* managed blockwise transfers in which the blocks are handled individually. + * Note that this option does not prevent local clients or resource + * implementations from sending large bodies as part of a request or response to a peer. + * The default value of this property is DEFAULT_MAX_RESOURCE_BODY_SIZE = 8192 + * A value of {@code 0} turns off transparent handling of blockwise transfers altogether. + */ +// coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 8192); + coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 16384); + /** + * The default DTLS response matcher. + * Supported values are STRICT, RELAXED, or PRINCIPAL. + * The default value is STRICT. + * Create new instance of udp endpoint context matcher. + * Params: + * checkAddress + * – true with address check, (STRICT, UDP) + * - false, without + */ + coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "STRICT"); + /** + * https://tools.ietf.org/html/rfc7959#section-2.9.3 + * The block size (number of bytes) to use when doing a blockwise transfer. \ + * This value serves as the upper limit for block size in blockwise transfers + */ + coapConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 512); + /** + * The maximum payload size (in bytes) that can be transferred in a + * single message, i.e. without requiring a blockwise transfer. + * NB: this value MUST be adapted to the maximum message size supported by the transport layer. + * In particular, this value cannot exceed the network's MTU if UDP is used as the transport protocol + * DEFAULT_VALUE = 1024 + */ + coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 512); + + coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4); + + return coapConfig; + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java index c17e37fd4a..72626f1103 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java @@ -26,6 +26,7 @@ import org.eclipse.leshan.server.registration.RegistrationUpdate; import java.util.Collection; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer; @Slf4j @@ -85,17 +86,19 @@ public class LwM2mServerListener { @Override public void cancelled(Observation observation) { - log.info("Received notification cancelled from [{}] ", observation.getPath()); + String msg = String.format("%s: Cancel Observation %s.", LOG_LW2M_INFO, observation.getPath()); + service.sendLogsToThingsboard(msg, observation.getRegistrationId()); + log.trace(msg); } @Override public void onResponse(Observation observation, Registration registration, ObserveResponse response) { if (registration != null) { try { - service.onObservationResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), + service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), response, null); } catch (Exception e) { - log.error("[{}] onResponse", e.toString()); + log.error("Observation/Read onResponse", e); } } @@ -108,7 +111,10 @@ public class LwM2mServerListener { @Override public void newObservation(Observation observation, Registration registration) { - log.info("Received newObservation from [{}] endpoint [{}] ", observation.getPath(), registration.getEndpoint()); + String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO, + observation.getPath()); + service.sendLogsToThingsboard(msg, registration.getId()); + log.trace(msg); } }; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandler.java index ac5adcb7a3..08e46eb288 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandler.java @@ -25,7 +25,6 @@ import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.leshan.core.attributes.Attribute; import org.eclipse.leshan.core.attributes.AttributeSet; import org.eclipse.leshan.core.model.ObjectModel; @@ -40,7 +39,6 @@ import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.request.DownlinkRequest; import org.eclipse.leshan.core.request.WriteAttributesRequest; import org.eclipse.leshan.core.util.Hex; -import org.eclipse.leshan.server.californium.LeshanServerBuilder; import org.eclipse.leshan.server.registration.Registration; import org.nustaq.serialization.FSTConfiguration; import org.thingsboard.server.common.data.DeviceProfile; @@ -50,7 +48,6 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -72,7 +69,6 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA @Slf4j public class LwM2mTransportHandler { - // public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me"; public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0"; public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings"; public static final String BOOTSTRAP = "bootstrap"; @@ -85,19 +81,12 @@ public class LwM2mTransportHandler { public static final String KEY_NAME = "keyName"; public static final String OBSERVE_LWM2M = "observe"; public static final String ATTRIBUTE_LWM2M = "attributeLwm2m"; -// public static final String RESOURCE_VALUE = "resValue"; -// public static final String RESOURCE_TYPE = "resType"; private static final String REQUEST = "/request"; - // private static final String RESPONSE = "/response"; private static final String ATTRIBUTES = "/" + ATTRIBUTE; public static final String TELEMETRIES = "/" + TELEMETRY; - // public static final String ATTRIBUTES_RESPONSE = ATTRIBUTES + RESPONSE; public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST; - // public static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/"; public static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/"; -// public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + ATTRIBUTES; -// public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRIES; public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms @@ -112,6 +101,11 @@ public class LwM2mTransportHandler { public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized"; + public static final Integer FR_OBJECT_ID = 5; + public static final Integer FR_RESOURCE_VER_ID = 7; + public static final String FR_PATH_RESOURCE_VER_ID = LWM2M_SEPARATOR_PATH + FR_OBJECT_ID + LWM2M_SEPARATOR_PATH + + "0" + LWM2M_SEPARATOR_PATH + FR_RESOURCE_VER_ID; + public enum LwM2mTypeServer { BOOTSTRAP(0, "bootstrap"), CLIENT(1, "client"); @@ -168,6 +162,8 @@ public class LwM2mTransportHandler { WRITE_ATTRIBUTES(8, "WriteAttributes"), DELETE(9, "Delete"); +// READ_INFO_FW(10, "ReadInfoFirmware"); + public int code; public String type; @@ -190,21 +186,6 @@ public class LwM2mTransportHandler { public static final String SERVICE_CHANNEL = "SERVICE"; public static final String RESPONSE_CHANNEL = "RESP"; - public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) { - NetworkConfig coapConfig; - File configFile = new File(NetworkConfig.DEFAULT_FILE_NAME); - if (configFile.isFile()) { - coapConfig = new NetworkConfig(); - coapConfig.load(configFile); - } else { - coapConfig = LeshanServerBuilder.createDefaultNetworkConfig(); - coapConfig.store(configFile); - } - coapConfig.setString("COAP_PORT", Integer.toString(serverPortNoSec)); - coapConfig.setString("COAP_SECURE_PORT", Integer.toString(serverSecurePort)); - return coapConfig; - } - public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException { switch (type) { case BOOLEAN: diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java index 9d7c22a112..370abaa9cd 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java @@ -67,6 +67,7 @@ import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEFAULT_TIMEOUT; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.FR_PATH_RESOURCE_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE; @@ -125,7 +126,6 @@ public class LwM2mTransportRequest { public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper, String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { try { - String target = convertPathFromIdVerToObjectId(targetIdVer); DownlinkRequest request = null; ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT; @@ -145,11 +145,11 @@ public class LwM2mTransportRequest { break; case OBSERVE: if (resultIds.isResource()) { - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); } else if (resultIds.isObjectInstance()) { - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId()); + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId()); } else if (resultIds.getObjectId() >= 0) { - request = new ObserveRequest(resultIds.getObjectId()); + request = new ObserveRequest(contentFormat, resultIds.getObjectId()); } break; case OBSERVE_CANCEL: @@ -171,8 +171,6 @@ public class LwM2mTransportRequest { break; case WRITE_REPLACE: // Request to write a String Single-Instance Resource using the TLV content format. -// resource = lwM2MClient.getResourceModel(targetIdVer); -// if (contentFormat.equals(ContentFormat.TLV) && !resource.multiple) { resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() .getModelProvider()); if (contentFormat.equals(ContentFormat.TLV)) { @@ -181,7 +179,6 @@ public class LwM2mTransportRequest { registration, rpcRequest); } // Mode.REPLACE && Request to write a String Single-Instance Resource using the given content format (TEXT, TLV, JSON) -// else if (!contentFormat.equals(ContentFormat.TLV) && !resource.multiple) { else if (!contentFormat.equals(ContentFormat.TLV)) { request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type, @@ -215,13 +212,16 @@ public class LwM2mTransportRequest { long finalTimeoutInMs = timeoutInMs; lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, rpcRequest)); } catch (Exception e) { - log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper, e); + log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e); + } + } else if (OBSERVE_CANCEL == typeOper) { + log.trace("[{}], [{}] - [{}] SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer); + if (rpcRequest != null) { + rpcRequest.setInfoMsg(null); + serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null); } - } else if (OBSERVE_CANCEL == typeOper && rpcRequest != null) { - rpcRequest.setInfoMsg(null); - serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null); } else { - log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper, targetIdVer); + log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer); if (rpcRequest != null) { String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); @@ -236,8 +236,8 @@ public class LwM2mTransportRequest { Set observationPaths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); String msg = String.format("%s: type operation %s observation paths - %s", LOG_LW2M_INFO, OBSERVE_READ_ALL.type, observationPaths); - serviceImpl.sendLogsToThingsboard(msg, registration); - log.info("[{}], [{}]", registration.getEndpoint(), msg); + serviceImpl.sendLogsToThingsboard(msg, registration.getId()); + log.trace("[{}] [{}], [{}]", typeOper.name(), registration.getEndpoint(), msg); if (rpcRequest != null) { String valueMsg = String.format("Observation paths - %s", observationPaths); serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); @@ -246,7 +246,7 @@ public class LwM2mTransportRequest { } catch (Exception e) { String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, typeOper.name(), e.getMessage()); - serviceImpl.sendLogsToThingsboard(msg, registration); + serviceImpl.sendLogsToThingsboard(msg, registration.getId()); throw new Exception(e); } } @@ -261,45 +261,53 @@ public class LwM2mTransportRequest { private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { leshanServer.send(registration, request, timeoutInMs, (ResponseCallback) response -> { if (!lwM2MClient.isInit()) { - lwM2MClient.initValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); + lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); } if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) { this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest); - if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest()) { - LwM2mNode node = ((WriteRequest) request).getNode(); - Object value = this.converter.convertValue(((LwM2mSingleResource) node).getValue(), - ((LwM2mSingleResource) node).getType(), ResourceModel.Type.STRING, request.getPath()); - String msg = String.format("%s: sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client", - LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), - response.getCode().getName(), request.getPath().toString(), value); - serviceImpl.sendLogsToThingsboard(msg, registration); - log.info("[{}] [{}] - [{}] [{}] Update SendRequest[{}]", registration.getEndpoint(), - ((Response) response.getCoapResponse()).getCode(), response.getCode(), - request.getPath().toString(), value); - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO); - } } else { - String msg = String.format("%s: sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", LOG_LW2M_ERROR, + String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(), ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); - serviceImpl.sendLogsToThingsboard(msg, registration); - log.error("[{}], [{}] - [{}] [{}] error SendRequest", registration.getEndpoint(), ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); + serviceImpl.sendLogsToThingsboard(msg, registration.getId()); + log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(), + ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); + if (!lwM2MClient.isInit()) { + lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); + } if (rpcRequest != null) { serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR); } + /** Not Found + * set setClient_fw_version = empty + **/ + if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) { + lwM2MClient.setUpdateFw(false); + lwM2MClient.getFrUpdate().setClientFwVersion(""); + log.warn("updateFirmwareClient1"); + serviceImpl.updateFirmwareClient(lwM2MClient); + } } }, e -> { + /** version == null + * set setClient_fw_version = empty + **/ + if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) { + lwM2MClient.setUpdateFw(false); + lwM2MClient.getFrUpdate().setClientFwVersion(""); + log.warn("updateFirmwareClient2"); + serviceImpl.updateFirmwareClient(lwM2MClient); + } if (!lwM2MClient.isInit()) { - lwM2MClient.initValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); + lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); } - String msg = String.format("%s: sendRequest: Resource path - %s msg error - %s SendRequest to Client", - LOG_LW2M_ERROR, request.getPath().toString(), e.getMessage()); - serviceImpl.sendLogsToThingsboard(msg, registration); - log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString()); + String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s", + LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage()); + serviceImpl.sendLogsToThingsboard(msg, registration.getId()); + log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString()); if (rpcRequest != null) { serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); } }); - } private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, @@ -323,7 +331,9 @@ public class LwM2mTransportRequest { Date date = new Date(Long.decode(value.toString())); return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date); case OPAQUE: // byte[] value, base64 - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())); + byte[] valueRequest = value instanceof byte[] ? (byte[]) value : Hex.decodeHex(value.toString().toCharArray()); + return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, valueRequest) : + new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueRequest); default: } } @@ -337,7 +347,7 @@ public class LwM2mTransportRequest { String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client", patn, type, value, e.toString()); - serviceImpl.sendLogsToThingsboard(msg, registration); + serviceImpl.sendLogsToThingsboard(msg, registration.getId()); log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); if (rpcRequest != null) { String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value); @@ -369,7 +379,7 @@ public class LwM2mTransportRequest { DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { String pathIdVer = convertPathFromObjectIdToIdVer(path, registration); if (response instanceof ReadResponse) { - serviceImpl.onObservationResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest); + serviceImpl.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest); } else if (response instanceof CancelObservationResponse) { log.info("[{}] Path [{}] CancelObservationResponse 3_Send", pathIdVer, response); @@ -389,14 +399,33 @@ public class LwM2mTransportRequest { } else if (response instanceof WriteAttributesResponse) { log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response); } else if (response instanceof WriteResponse) { - log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", pathIdVer, response); + log.info("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response); + this.infoWriteResponse(registration, response, request); serviceImpl.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request); } - if (rpcRequest != null && (response instanceof ExecuteResponse - || response instanceof WriteAttributesResponse - || response instanceof DeleteResponse)) { - rpcRequest.setInfoMsg(null); - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null); + if (rpcRequest != null) { + if (response instanceof ExecuteResponse + || response instanceof WriteAttributesResponse + || response instanceof DeleteResponse) { + rpcRequest.setInfoMsg(null); + serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null); + } else if (response instanceof WriteResponse) { + serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO); + } } } + + private void infoWriteResponse(Registration registration, LwM2mResponse response, + DownlinkRequest request) { + LwM2mNode node = ((WriteRequest) request).getNode(); + Object value = this.converter.convertValue(((LwM2mSingleResource) node).getValue(), + ((LwM2mSingleResource) node).getType(), ResourceModel.Type.STRING, request.getPath()); + String msg = String.format("%s: Update finished successfully: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s", + LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), + response.getCode().getName(), request.getPath().toString(), value); + serviceImpl.sendLogsToThingsboard(msg, registration.getId()); + log.warn("[{}] [{}] [{}] - [{}] [{}] Update finished successfully: [{}]", request.getClass().getName().toString(), registration.getEndpoint(), + ((Response) response.getCoapResponse()).getCode(), response.getCode(), + request.getPath().toString(), value); + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java index 0af20b3e51..86bec24984 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java @@ -16,6 +16,8 @@ package org.thingsboard.server.transport.lwm2m.server; import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.network.config.NetworkConfig; +import org.eclipse.californium.core.network.stack.BlockwiseLayer; import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; @@ -57,7 +59,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getCoapConfig; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig; @Slf4j @Component @@ -92,7 +94,10 @@ public class LwM2mTransportServerConfiguration { /** Use a magic converter to support bad type send by the UI. */ builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); + /** Create CoAP Config */ + NetworkConfig networkConfig = getCoapConfig(serverPortNoSec, serverSecurePort); + BlockwiseLayer blockwiseLayer = new BlockwiseLayer(networkConfig); builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort)); /** Define model provider (Create Models )*/ @@ -110,6 +115,7 @@ public class LwM2mTransportServerConfiguration { /** Create DTLS Config */ DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); + dtlsConfig.setServerOnly(true); dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportConfigServer().isRecommendedSupportedGroups()); dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportConfigServer().isRecommendedCiphers()); if (this.pskMode) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java index 91588c0490..dec1e7889d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java @@ -39,7 +39,7 @@ public interface LwM2mTransportService extends TbTransportService { void setCancelObservations(Registration registration); - void onObservationResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest); + void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest); void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo); @@ -60,6 +60,4 @@ public interface LwM2mTransportService extends TbTransportService { void doTrigger(Registration registration, String path); void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); - - } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java index 31ae4bdf83..f52919ea3c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java @@ -38,10 +38,12 @@ import org.eclipse.leshan.server.registration.Registration; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.cache.firmware.FirmwareDataCache; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.service.DefaultTransportService; import org.thingsboard.server.gen.transport.TransportProtos; @@ -77,9 +79,12 @@ import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; +import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_VERSION; +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.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_REQUEST; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.FR_PATH_RESOURCE_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE; @@ -110,6 +115,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { private ExecutorService executorUpdateRegistered; private ExecutorService executorUnRegistered; private LwM2mValueConverterImpl converter; + private FirmwareDataCache firmwareDataCache; + private final TransportService transportService; @@ -121,12 +128,15 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { private final LwM2mTransportRequest lwM2mTransportRequest; - public LwM2mTransportServiceImpl(TransportService transportService, LwM2mTransportContextServer lwM2mTransportContextServer, LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer, @Lazy LwM2mTransportRequest lwM2mTransportRequest) { + public LwM2mTransportServiceImpl(TransportService transportService, LwM2mTransportContextServer lwM2mTransportContextServer, + LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer, + @Lazy LwM2mTransportRequest lwM2mTransportRequest, FirmwareDataCache firmwareDataCache) { this.transportService = transportService; this.lwM2mTransportContextServer = lwM2mTransportContextServer; this.lwM2mClientContext = lwM2mClientContext; this.leshanServer = leshanServer; this.lwM2mTransportRequest = lwM2mTransportRequest; + this.firmwareDataCache = firmwareDataCache; } @PostConstruct @@ -168,8 +178,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null); transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null); + this.getInfoFirmwareUpdate(lwM2MClient); this.initLwM2mFromClientValue(registration, lwM2MClient); - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration); + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId()); } else { log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); } @@ -224,7 +235,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { executorUnRegistered.submit(() -> { try { this.setCancelObservations(registration); - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration); + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId()); this.closeClientSession(registration); } catch (Throwable t) { log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); @@ -257,7 +268,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { @Override public void onSleepingDev(Registration registration) { log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration); + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration.getId()); //TODO: associate endpointId with device information. } @@ -280,7 +291,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { * @param response - observe */ @Override - public void onObservationResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) { + public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) { if (response.getContent() != null) { if (response.getContent() instanceof LwM2mObject) { LwM2mObject lwM2mObject = (LwM2mObject) response.getContent(); @@ -304,20 +315,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { /** * Update - send request in change value resources in Client - * Path to resources from profile equal keyName or from ModelObject equal name - * Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase) - * Delete - nothing * + * 1. FirmwareUpdate: + * - If msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0 + * 2. Shared Other AttributeUpdate + * -- Path to resources from profile equal keyName or from ModelObject equal name + * -- Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase) + * 3. Delete - nothing * * @param msg - */ @Override public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { + LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); if (msg.getSharedUpdatedCount() > 0) { msg.getSharedUpdatedList().forEach(tsKvProto -> { String pathName = tsKvProto.getKv().getKey(); String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()); - LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); + if (FIRMWARE_VERSION.equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) { + this.getInfoFirmwareUpdate(lwM2MClient); + } if (pathIdVer != null) { ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() .getModelProvider()); @@ -327,19 +344,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew); String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", LOG_LW2M_ERROR, pathIdVer, valueNew); - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); } } else { log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew); String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", LOG_LW2M_ERROR, pathName, valueNew); - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); } + }); } else if (msg.getSharedDeletedCount() > 0) { + msg.getSharedUpdatedList().forEach(tsKvProto -> { + String pathName = tsKvProto.getKv().getKey(); + Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()); + if (FIRMWARE_VERSION.equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) { + lwM2MClient.getFrUpdate().setCurrentFwVersion((String) valueNew); + } + }); log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); } - } /** @@ -455,23 +479,21 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { } if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) { lwm2mClientRpcRequest.setParams(new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey) - .getAsJsonObject().toString(), new TypeToken>() { - }.getType())); + .getAsJsonObject().toString(), new TypeToken>() { + }.getType())); } lwm2mClientRpcRequest.setSessionInfo(sessionInfo); if (OBSERVE_READ_ALL != lwm2mClientRpcRequest.getTypeOper() && lwm2mClientRpcRequest.getTargetIdVer() == null) { lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " + lwm2mClientRpcRequest.keyNameKey + " is null or bad format"); - } - else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper() + } else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper() || WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper()) - && lwm2mClientRpcRequest.getTargetIdVer() !=null + && lwm2mClientRpcRequest.getTargetIdVer() != null && !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource() || new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) { - lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey + lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey + ". Only Resource or ResourceInstance can be this operation"); - } - else if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()){ + } else if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()) { lwm2mClientRpcRequest.setErrorMsg("Procedures In Development..."); } } else { @@ -483,23 +505,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { return lwm2mClientRpcRequest; } - public void sentRpcRequest (Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { + public void sentRpcRequest(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { rpcRequest.setResponseCode(requestCode); - if (LOG_LW2M_ERROR.equals(typeMsg)) { - rpcRequest.setInfoMsg(null); - rpcRequest.setValueMsg(null); - if (rpcRequest.getErrorMsg() == null) { - msg = msg.isEmpty() ? null : msg; - rpcRequest.setErrorMsg(msg); - } - } else if (LOG_LW2M_INFO.equals(typeMsg)) { - if (rpcRequest.getInfoMsg() == null) { - rpcRequest.setInfoMsg(msg); - } - } else if (LOG_LW2M_VALUE.equals(typeMsg)) { - if (rpcRequest.getValueMsg() == null) { - rpcRequest.setValueMsg(msg); - } + if (LOG_LW2M_ERROR.equals(typeMsg)) { + rpcRequest.setInfoMsg(null); + rpcRequest.setValueMsg(null); + if (rpcRequest.getErrorMsg() == null) { + msg = msg.isEmpty() ? null : msg; + rpcRequest.setErrorMsg(msg); + } + } else if (LOG_LW2M_INFO.equals(typeMsg)) { + if (rpcRequest.getInfoMsg() == null) { + rpcRequest.setInfoMsg(msg); + } + } else if (LOG_LW2M_VALUE.equals(typeMsg)) { + if (rpcRequest.getValueMsg() == null) { + rpcRequest.setValueMsg(msg); + } } this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo()); } @@ -556,7 +578,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { */ protected void onAwakeDev(Registration registration) { log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration); + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration.getId()); //TODO: associate endpointId with device information. } @@ -583,10 +605,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { /** * @param logMsg - text msg - * @param registration - Id of Registration LwM2M Client + * @param registrationId - Id of Registration LwM2M Client */ - public void sendLogsToThingsboard(String logMsg, Registration registration) { - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); + public void sendLogsToThingsboard(String logMsg, String registrationId) { + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registrationId); if (logMsg != null && sessionInfo != null) { this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(this.lwM2mTransportContextServer.getKvLogyToThingsboard(logMsg), sessionInfo); } @@ -610,7 +632,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { if (clientObjects != null && clientObjects.size() > 0) { if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { // #2 - lwM2MClient.getPendingRequests().addAll(clientObjects); + lwM2MClient.getPendingReadRequests().addAll(clientObjects); clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(), null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); } @@ -652,6 +674,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { * Sending observe value of resources to thingsboard * #1 Return old Value Resource from LwM2MClient * #2 Update new Resources (replace old Resource Value on new Resource Value) + * #3 If fr_update -> UpdateFirmware + * #4 updateAttrTelemetry * * @param registration - Registration LwM2M Client * @param lwM2mResource - LwM2mSingleResource response.getContent() @@ -661,6 +685,19 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() .getModelProvider())) { + if (FR_PATH_RESOURCE_VER_ID.equals(convertPathFromIdVerToObjectId(path)) && + lwM2MClient.getFrUpdate().getCurrentFwVersion() != null + && !lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion()) + && lwM2MClient.isUpdateFw()) { + + /** version != null + * set setClient_fw_version = value + **/ + lwM2MClient.setUpdateFw(false); + lwM2MClient.getFrUpdate().setClientFwVersion(lwM2mResource.getValue().toString()); + log.warn("updateFirmwareClient3"); + this.updateFirmwareClient(lwM2MClient); + } Set paths = new HashSet<>(); paths.add(path); this.updateAttrTelemetry(registration, paths); @@ -669,6 +706,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { } } + /** * send Attribute and Telemetry to Thingsboard * #1 - get AttrName/TelemetryName with value from LwM2MClient: @@ -723,22 +761,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()); result = params.keySet(); } - if (!result.isEmpty()) { + if (result != null && !result.isEmpty()) { // #1 Set pathSend = result.stream().filter(target -> { return target.split(LWM2M_SEPARATOR_PATH).length < 3 ? clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) : clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]); } - ) - .collect(Collectors.toUnmodifiableSet()); + ).collect(Collectors.toUnmodifiableSet()); if (!pathSend.isEmpty()) { - lwM2MClient.getPendingRequests().addAll(pathSend); + lwM2MClient.getPendingReadRequests().addAll(pathSend); ConcurrentHashMap finalParams = params; - pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(), - finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); + pathSend.forEach(target -> { + lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(), + finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); + }); if (OBSERVE.equals(typeOper)) { - lwM2MClient.initValue(this, null); + lwM2MClient.initReadValue(this, null); } } } @@ -969,7 +1008,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { // update value in Resources registrationIds.forEach(registrationId -> { Registration registration = lwM2mClientContext.getRegistration(registrationId); - this.readResourceValueObserve(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ); + this.readObserveFromProfile(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ); // send attr/telemetry to tingsboard for new path this.updateAttrTelemetry(registration, sendAttrToThingsboard.getPathPostParametersAdd()); }); @@ -998,12 +1037,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { registrationIds.forEach(registrationId -> { Registration registration = lwM2mClientContext.getRegistration(registrationId); if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) { - this.readResourceValueObserve(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE); + this.readObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE); } // 5.3 del // send Request cancel observe to Client if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) { - this.cancelObserveIsValue(registration, postObserveAnalyzer.getPathPostParametersDel()); + this.cancelObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersDel()); } }); } @@ -1043,7 +1082,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { * @param registration - Registration LwM2M Client * @param targets - path Resources == [ "/2/0/0", "/2/0/1"] */ - private void readResourceValueObserve(Registration registration, Set targets, LwM2mTypeOper typeOper) { + private void readObserveFromProfile(Registration registration, Set targets, LwM2mTypeOper typeOper) { targets.forEach(target -> { LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target)); if (pathIds.isResource()) { @@ -1133,7 +1172,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { } - private void cancelObserveIsValue(Registration registration, Set paramAnallyzer) { + private void cancelObserveFromProfile(Registration registration, Set paramAnallyzer) { LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); paramAnallyzer.forEach(pathIdVer -> { if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) { @@ -1153,7 +1192,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { log.error("Failed update resource [{}] [{}]", path, valueNew); String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad", LOG_LW2M_ERROR, path, valueNew); - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); log.info("Failed update resource [{}] [{}]", path, valueNew); } } @@ -1182,8 +1221,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { } /** - * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values - * #1 Get path resource by result attributesResponse + * 1. FirmwareUpdate: + * - msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0 + * 2. Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values + * - Get path resource by result attributesResponse * * @param attributesResponse - * @param sessionInfo - @@ -1191,6 +1232,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) { try { List tsKvProtos = attributesResponse.getSharedAttributeListList(); + this.updateAttriuteFromThingsboard(tsKvProtos, sessionInfo); } catch (Exception e) { log.error(String.valueOf(e)); @@ -1274,7 +1316,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { */ private SessionInfoProto getValidateSessionInfo(String registrationId) { LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(null, registrationId); - return getNewSessionInfoProto(lwM2MClient); + return lwM2MClient != null ? this.getNewSessionInfoProto(lwM2MClient) : null; } /** @@ -1293,28 +1335,88 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { } /** - * !!! sharedAttr === profileAttr !!! - * If there is a difference in values between the current resource values and the shared attribute values - * when the client connects to the server - * #1 get attributes name from profile include name resources in ModelObject if resource isWritable - * #2.1 #1 size > 0 => send Request getAttributes to thingsboard + * #1. !!! sharedAttr === profileAttr !!! + * - If there is a difference in values between the current resource values and the shared attribute values + * - when the client connects to the server + * #1.1 get attributes name from profile include name resources in ModelObject if resource isWritable + * #1.2 #1 size > 0 => send Request getAttributes to thingsboard + * #2. FirmwareAttribute subscribe: * * @param lwM2MClient - LwM2M Client */ public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) { SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration()); if (sessionInfo != null) { - //#1.1 + #1.2 - List attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient); - if (attrSharedNames.size() > 0) { - //#2.1 + //#1.1 + ConcurrentMap keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient); + if (keyNamesMap.values().size() > 0) { try { - TransportProtos.GetAttributeRequestMsg getAttributeMsg = lwM2mTransportContextServer.getAdaptor().convertToGetAttributes(null, attrSharedNames); + //#1.2 + TransportProtos.GetAttributeRequestMsg getAttributeMsg = lwM2mTransportContextServer.getAdaptor().convertToGetAttributes(null, keyNamesMap.values()); transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST)); } catch (AdaptorException e) { log.warn("Failed to decode get attributes request", e); } } + + } + } + + public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient) { + SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration()); + if (sessionInfo != null) { + TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) + .setTenantIdMSB(sessionInfo.getTenantIdMSB()) + .setTenantIdLSB(sessionInfo.getTenantIdLSB()) + .build(); + transportService.process(sessionInfo, getFirmwareRequestMsg, + new TransportServiceCallback<>() { + @Override + public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) { + if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { + lwM2MClient.getFrUpdate().setCurrentFwVersion(response.getVersion()); + lwM2MClient.getFrUpdate().setCurrentFwId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId()); + lwM2MClient.setUpdateFw(true); + readRequestToClientFirmwareVer(lwM2MClient.getRegistration()); + } else { + log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); + } + } + + @Override + public void onError(Throwable e) { + log.trace("Failed to process credentials ", e); + } + }); + } + } + + /** + * @param registration + */ + public void readRequestToClientFirmwareVer(Registration registration) { + String pathIdVer = convertPathFromObjectIdToIdVer(FR_PATH_RESOURCE_VER_ID, registration); + lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, READ, ContentFormat.TLV.getName(), + null, lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); + } + + /** + * + * @param lwM2MClient - + */ + public void updateFirmwareClient(LwM2mClient lwM2MClient) { + if (!lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())) { + int chunkSize = 0; + int chunk = 0; + byte[] firmwareChunk = firmwareDataCache.get(lwM2MClient.getFrUpdate().getCurrentFwId().toString(), chunkSize, chunk); + Integer objectId = 5; + String verSupportedObject = lwM2MClient.getRegistration().getSupportedObject().get(objectId); + String targetIdVer = LWM2M_SEPARATOR_PATH + objectId + LWM2M_SEPARATOR_KEY + verSupportedObject + LWM2M_SEPARATOR_PATH + 0 + LWM2M_SEPARATOR_PATH + 0; + lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.TLV.getName(), + firmwareChunk, lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); + log.warn("updateFirmwareClient [{}] [{}]", lwM2MClient.getFrUpdate().getCurrentFwVersion(), lwM2MClient.getFrUpdate().getClientFwVersion()); } } @@ -1326,23 +1428,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { * @param lwM2MClient - * @return ArrayList keyNames from profile profileAttr && IsWritable */ - private List getNamesAttrFromProfileIsWritable(LwM2mClient lwM2MClient) { + private ConcurrentMap getNamesFromProfileForSharedAttributes(LwM2mClient lwM2MClient) { + LwM2mClientProfile profile = lwM2mClientContext.getProfile(lwM2MClient.getProfileId()); - Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), - new TypeToken>() { - }.getType()); - ConcurrentMap keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), + return new Gson().fromJson(profile.getPostKeyNameProfile().toString(), new TypeToken>() { }.getType()); - - ConcurrentMap keyNamesIsWritable = keyNamesMap.entrySet() - .stream() - .filter(e -> (attrSet.contains(e.getKey()) && validateResourceInModel(lwM2MClient, e.getKey(), true))) - .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue)); - - Set namesIsWritable = ConcurrentHashMap.newKeySet(); - namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values())); - return new ArrayList<>(namesIsWritable); } private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java index b228a52b94..65a1e14a4c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java @@ -24,11 +24,8 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; +import java.util.Collection; import java.util.Random; -import java.util.Set; @Slf4j @Component("LwM2MJsonAdaptor") @@ -54,11 +51,7 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor { } @Override - public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List clientKeys, List sharedKeys) throws AdaptorException { - return processGetAttributeRequestMsg(clientKeys, sharedKeys); - } - - protected TransportProtos.GetAttributeRequestMsg processGetAttributeRequestMsg(List clientKeys, List sharedKeys) throws AdaptorException { + public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(Collection clientKeys, Collection sharedKeys) throws AdaptorException { try { TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); Random random = new Random(); @@ -75,14 +68,4 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor { throw new AdaptorException(e); } } - - private Set toStringSet(JsonElement requestBody, String name) { - JsonElement element = requestBody.getAsJsonObject().get(name); - if (element != null) { - return new HashSet<>(Arrays.asList(element.getAsString().split(","))); - } else { - return null; - } - } - } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java index 4b6d54c874..d654475f03 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java @@ -19,7 +19,7 @@ import com.google.gson.JsonElement; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.TransportProtos; -import java.util.List; +import java.util.Collection; public interface LwM2MTransportAdaptor { @@ -27,5 +27,5 @@ public interface LwM2MTransportAdaptor { TransportProtos.PostAttributeMsg convertToPostAttributes(JsonElement jsonElement) throws AdaptorException; - TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List clientKeys, List sharedKeys) throws AdaptorException; + TransportProtos.GetAttributeRequestMsg convertToGetAttributes(Collection clientKeys, Collection sharedKeys) throws AdaptorException; } 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 ac1738e101..0617847246 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 @@ -57,13 +57,15 @@ public class LwM2mClient implements Cloneable { private UUID deviceId; private UUID sessionId; private UUID profileId; + private volatile LwM2mFirmwareUpdate frUpdate; private Registration registration; private ValidateDeviceCredentialsResponseMsg credentialsResponse; private final Map resources; private final Map delayedRequests; - private final List pendingRequests; + private final List pendingReadRequests; private final Queue queuedRequests; private boolean init; + private volatile boolean updateFw; public Object clone() throws CloneNotSupportedException { return super.clone(); @@ -75,12 +77,14 @@ public class LwM2mClient implements Cloneable { this.securityInfo = securityInfo; this.credentialsResponse = credentialsResponse; this.delayedRequests = new ConcurrentHashMap<>(); - this.pendingRequests = new CopyOnWriteArrayList<>(); + this.pendingReadRequests = new CopyOnWriteArrayList<>(); this.resources = new ConcurrentHashMap<>(); this.profileId = profileId; this.sessionId = sessionId; this.init = false; + this.updateFw = false; this.queuedRequests = new ConcurrentLinkedQueue<>(); + this.frUpdate = new LwM2mFirmwareUpdate(); } public boolean saveResourceValue(String pathRez, LwM2mResource rez, LwM2mModelProvider modelProvider) { @@ -103,15 +107,13 @@ public class LwM2mClient implements Cloneable { LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez)); String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); String verRez = getVerFromPathIdVerOrId(pathRez); - return (verRez == null || verSupportedObject.equals(verRez)) ? modelProvider.getObjectModel(registration) + return verRez == null || verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration) .getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null; } public Collection getNewResourcesForInstance(String pathRezIdVer, LwM2mModelProvider modelProvider, LwM2mValueConverterImpl converter) { LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer)); - String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); - String verRez = getVerFromPathIdVerOrId(pathRezIdVer); Collection resources = ConcurrentHashMap.newKeySet(); Map resourceModels = modelProvider.getObjectModel(registration) .getObjectModel(pathIds.getObjectId()).resources; @@ -170,11 +172,11 @@ public class LwM2mClient implements Cloneable { .collect(Collectors.toSet()); } - public void initValue(LwM2mTransportServiceImpl serviceImpl, String path) { + public void initReadValue(LwM2mTransportServiceImpl serviceImpl, String path) { if (path != null) { - this.pendingRequests.remove(path); + this.pendingReadRequests.remove(path); } - if (this.pendingRequests.size() == 0) { + if (this.pendingReadRequests.size() == 0) { this.init = true; serviceImpl.putDelayedUpdateResourcesThingsboard(this); } 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 f496ed01b4..fe17ac3819 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 @@ -82,12 +82,12 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @Override public LwM2mClient getLwM2mClientWithReg(Registration registration, String registrationId) { - LwM2mClient client = registrationId != null ? + LwM2mClient client = registrationId != null && this.lwM2mClients.containsKey(registrationId) ? this.lwM2mClients.get(registrationId) : - this.lwM2mClients.containsKey(registration.getId()) ? - this.lwM2mClients.get(registration.getId()) : - this.lwM2mClients.get(registration.getEndpoint()); - return client != null ? client : updateInSessionsLwM2MClient(registration); + registration !=null && this.lwM2mClients.containsKey(registration.getId()) ? + this.lwM2mClients.get(registration.getId()) : registration !=null && this.lwM2mClients.containsKey(registration) ? + this.lwM2mClients.get(registration.getEndpoint()) : null; + return client != null ? client : registration!= null ? updateInSessionsLwM2MClient(registration) : null; } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFirmwareUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFirmwareUpdate.java new file mode 100644 index 0000000000..9eb6321445 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFirmwareUpdate.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.client; + +import lombok.Data; + +import java.util.UUID; + +@Data +public class LwM2mFirmwareUpdate { + private volatile String clientFwVersion; + private volatile String currentFwVersion; + private volatile UUID currentFwId; +} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts index 77a5956855..3d6b93b84c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts @@ -24,6 +24,8 @@ import { NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ATTRIBUTE, @@ -38,6 +40,7 @@ import { } from './lwm2m-profile-config.models'; import { deepClone, isDefinedAndNotNull, isEqual, isUndefined } from '@core/utils'; import { MatDialog } from '@angular/material/dialog'; +import { TranslateService } from '@ngx-translate/core'; import { Lwm2mObjectAddInstancesData, Lwm2mObjectAddInstancesDialogComponent @@ -80,8 +83,10 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor @Input() disabled: boolean; - constructor(private fb: FormBuilder, - private dialog: MatDialog) { + constructor(private store: Store, + private fb: FormBuilder, + private dialog: MatDialog, + public translate: TranslateService) { this.observeAttrTelemetryFormGroup = this.fb.group({ [CLIENT_LWM2M]: this.fb.array([]) }); @@ -93,7 +98,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor } private propagateChange = (v: any) => { - } + }; registerOnChange(fn: any): void { this.propagateChange = fn; @@ -184,7 +189,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M).updateValueAndValidity(); } - trackByParams = (index: number): number => { + trackByParams = (index: number, element: any): number => { return index; } @@ -312,7 +317,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor return objectName + ' <' + idVerObj + '>'; } getNameInstanceLwm2m = (instance: Instance, idVerObj: string): string => { - return ` instance <${idVerObj}/${instance.id}>`; + return ' instance <' + idVerObj + '/' + instance.id +'>'; } updateAttributeLwm2mObject = (event: Event, objectKeyId: number): void => { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index bd7ec5e2a2..9c2253d0ab 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1205,7 +1205,7 @@ "telemetry-label": "Telemetry", "key-name-label": "Key Name", "attribute-lwm2m-label": "AttrLwm2m", - "resource-tip": "ID & Original Name of the Resource", + "resource-tip": "ID & Original Name of the Resource (only Operations isReadable)", "is-observe-tip": "Is Observe", "not-observe-tip": "To observe select telemetry or attributes first", "is-attr-tip": "Is Attribute",