From 08939e23e76605a270972b0ed6046d9fb308d108 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Wed, 16 Jun 2021 19:00:45 +0300 Subject: [PATCH] Refactoring of the Firmware Update --- .../device/data/lwm2m/OtherConfiguration.java | 2 + .../server/LwM2MFirmwareUpdateStrategy.java | 48 ++++ .../server/LwM2MSoftwareUpdateStrategy.java | 48 ++++ .../lwm2m/server/LwM2mServerListener.java | 5 +- .../lwm2m/server/LwM2mSessionMsgListener.java | 3 +- .../server/LwM2mTransportServerHelper.java | 6 +- .../lwm2m/server/LwM2mTransportUtil.java | 215 +++------------ .../lwm2m/server/UpdateResultFw.java | 70 +++++ .../transport/lwm2m/server/UpdateStateFw.java | 56 ++++ .../DefaultLwM2MAttributesService.java | 183 +++++++++---- .../attributes/LwM2MAttributesService.java | 4 +- .../lwm2m/server/client/LwM2mClient.java | 9 +- .../server/client/LwM2mClientContext.java | 5 +- .../server/client/LwM2mClientContextImpl.java | 41 ++- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 65 ++--- .../downlink/TbLwM2MWriteReplaceRequest.java | 6 +- .../ota/DefaultLwM2MOtaUpdateService.java | 248 ++++++++++++++++-- .../lwm2m/server/ota/LwM2MClientOtaInfo.java | 99 +++++++ .../lwm2m/server/ota/LwM2MClientOtaState.java | 5 +- .../server/ota/LwM2MOtaUpdateService.java | 13 + .../rpc/DefaultLwM2MRpcRequestHandler.java | 2 +- .../uplink/DefaultLwM2MUplinkMsgHandler.java | 159 ++--------- .../server/uplink/LwM2mUplinkMsgHandler.java | 4 - 23 files changed, 851 insertions(+), 445 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MFirmwareUpdateStrategy.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSoftwareUpdateStrategy.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateResultFw.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateStateFw.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java index 2bfef74391..829180983b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/OtherConfiguration.java @@ -23,5 +23,7 @@ public class OtherConfiguration { private Integer fwUpdateStrategy; private Integer swUpdateStrategy; private Integer clientOnlyObserveAfterConnect; + private String fwUpdateRecourse; + private String swUpdateRecourse; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MFirmwareUpdateStrategy.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MFirmwareUpdateStrategy.java new file mode 100644 index 0000000000..f111152b76 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MFirmwareUpdateStrategy.java @@ -0,0 +1,48 @@ +/** + * 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; + +public enum LwM2MFirmwareUpdateStrategy { + OBJ_5_BINARY(1, "ObjectId 5, Binary"), + OBJ_5_TEMP_URL(2, "ObjectId 5, URI"), + OBJ_19_BINARY(3, "ObjectId 19, Binary"); + + public int code; + public String type; + + LwM2MFirmwareUpdateStrategy(int code, String type) { + this.code = code; + this.type = type; + } + + public static LwM2MFirmwareUpdateStrategy fromStrategyFwByType(String type) { + for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) { + if (to.type.equals(type)) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type)); + } + + public static LwM2MFirmwareUpdateStrategy fromStrategyFwByCode(int code) { + for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) { + if (to.code == code) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported FW Strategy code : %s", code)); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSoftwareUpdateStrategy.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSoftwareUpdateStrategy.java new file mode 100644 index 0000000000..0df3f5ca2d --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSoftwareUpdateStrategy.java @@ -0,0 +1,48 @@ +/** + * 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; + +public enum LwM2MSoftwareUpdateStrategy { + BINARY(1, "ObjectId 9, Binary"), + TEMP_URL(2, "ObjectId 9, URI"); + + public int code; + public String type; + + LwM2MSoftwareUpdateStrategy(int code, String type) { + this.code = code; + this.type = type; + } + + public static LwM2MSoftwareUpdateStrategy fromStrategySwByType(String type) { + for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) { + if (to.type.equals(type)) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported SW Strategy type : %s", type)); + } + + public static LwM2MSoftwareUpdateStrategy fromStrategySwByCode(int code) { + for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) { + if (to.code == code) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported SW Strategy code : %s", code)); + } + +} 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 36f1e8a903..1c2bb70145 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 @@ -27,8 +27,7 @@ import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandle import java.util.Collection; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; @Slf4j public class LwM2mServerListener { @@ -93,7 +92,7 @@ public class LwM2mServerListener { @Override public void onResponse(Observation observation, Registration registration, ObserveResponse response) { if (registration != null) { - service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), response); + service.onUpdateValueAfterReadResponse(registration, convertObjectIdToVersionedId(observation.getPath().toString(), registration), response); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java index ebf0a48aba..f39ced6dfc 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java @@ -33,7 +33,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseM import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import java.util.Optional; @@ -54,7 +53,7 @@ public class LwM2mSessionMsgListener implements GenericFutureListener>> futures; private final TransportService transportService; + private final LwM2mTransportServerHelper helper; + private final LwM2mClientContext clientContext; + private final LwM2MTransportServerConfig config; + private final LwM2mUplinkMsgHandler uplinkHandler; + private final LwM2mDownlinkMsgHandler downlinkHandler; + private final LwM2MOtaUpdateService otaUpdateService; @Override public ListenableFuture> getSharedAttributes(LwM2mClient client, Collection keys) { @@ -96,55 +116,114 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService { * @param msg - */ @Override - public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { -// LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo); -// if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) { -// log.warn("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList()); -// msg.getSharedUpdatedList().forEach(tsKvProto -> { -// String pathName = tsKvProto.getKv().getKey(); -// String pathIdVer = this.getObjectIdByKeyNameFromProfile(sessionInfo, pathName); -// Object valueNew = getValueFromKvProto(tsKvProto.getKv()); -// if ((OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) -// && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion()))) -// || (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) -// && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentTitle())))) { -// this.getInfoFirmwareUpdate(lwM2MClient, null); -// } else if ((OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName) -// && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentVersion()))) -// || (OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName) -// && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentTitle())))) { -// this.getInfoSoftwareUpdate(lwM2MClient, null); -// } -// if (pathIdVer != null) { -// ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.config -// .getModelProvider()); -// if (resourceModel != null && resourceModel.operations.isWritable()) { -// this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), valueNew, pathIdVer); -// } else { -// 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_LWM2M_ERROR, pathIdVer, valueNew); -// this.logToTelemetry(lwM2MClient, logMsg); -// } -// } else if (!isFwSwWords(pathName)) { -// 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_LWM2M_ERROR, pathName, valueNew); -// this.logToTelemetry(lwM2MClient, logMsg); -// } -// -// }); -// } else if (msg.getSharedDeletedCount() > 0 && lwM2MClient != null) { -// msg.getSharedUpdatedList().forEach(tsKvProto -> { -// String pathName = tsKvProto.getKv().getKey(); -// Object valueNew = getValueFromKvProto(tsKvProto.getKv()); -// if (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) { -// lwM2MClient.getFwUpdate().setCurrentVersion((String) valueNew); -// } -// }); -// log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); -// } else if (lwM2MClient == null) { -// log.error("OnAttributeUpdate, lwM2MClient is null"); -// } + public void onAttributesUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { + LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo); + if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) { + String newFirmwareTitle = null; + String newFirmwareVersion = null; + String newFirmwareUrl = null; + String newSoftwareTitle = null; + String newSoftwareVersion = null; + List otherAttributes = new ArrayList<>(); + for (TransportProtos.TsKvProto tsKvProto : msg.getSharedUpdatedList()) { + String attrName = tsKvProto.getKv().getKey(); + if (DefaultLwM2MOtaUpdateService.FIRMWARE_TITLE.equals(attrName)) { + newFirmwareTitle = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_VERSION.equals(attrName)) { + newFirmwareVersion = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_URL.equals(attrName)) { + newFirmwareUrl = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TITLE.equals(attrName)) { + newSoftwareTitle = getStrValue(tsKvProto); + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_VERSION.equals(attrName)) { + newSoftwareVersion = getStrValue(tsKvProto); + } else { + otherAttributes.add(tsKvProto); + } + } + if (newFirmwareTitle != null || newFirmwareVersion != null) { + otaUpdateService.onTargetFirmwareUpdate(lwM2MClient, newFirmwareTitle, newFirmwareVersion, Optional.ofNullable(newFirmwareUrl)); + } + if (newSoftwareTitle != null || newSoftwareVersion != null) { + otaUpdateService.onTargetSoftwareUpdate(lwM2MClient, newSoftwareTitle, newSoftwareVersion); + } + if (!otherAttributes.isEmpty()) { + onAttributesUpdate(lwM2MClient, otherAttributes); + } + } else if (msg.getSharedDeletedCount() > 0 && lwM2MClient != null) { + msg.getSharedUpdatedList().forEach(tsKvProto -> { + String pathName = tsKvProto.getKv().getKey(); + Object valueNew = getValueFromKvProto(tsKvProto.getKv()); + if (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) { + lwM2MClient.getFwUpdate().setCurrentVersion((String) valueNew); + } + }); + } else if (lwM2MClient == null) { + log.error("OnAttributeUpdate, lwM2MClient is null"); + } + } + + /** + * #1.1 If two names have equal path => last time attribute + * #2.1 if there is a difference in values between the current resource values and the shared attribute values + * => send to client Request Update of value (new value from shared attribute) + * and LwM2MClient.delayedRequests.add(path) + * #2.1 if there is not a difference in values between the current resource values and the shared attribute values + * + */ + @Override + public void onAttributesUpdate(LwM2mClient lwM2MClient, List tsKvProtos) { + log.trace("[{}] onAttributesUpdate [{}]", lwM2MClient.getEndpoint(), tsKvProtos); + tsKvProtos.forEach(tsKvProto -> { + String pathIdVer = clientContext.getObjectIdByKeyNameFromProfile(lwM2MClient, tsKvProto.getKv().getKey()); + if (pathIdVer != null) { + // #1.1 + if (lwM2MClient.getSharedAttributes().containsKey(pathIdVer)) { + if (tsKvProto.getTs() > lwM2MClient.getSharedAttributes().get(pathIdVer).getTs()) { + lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto); + } + } else { + lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto); + } + } + }); + // #2.1 + lwM2MClient.getSharedAttributes().forEach((pathIdVer, tsKvProto) -> { + this.pushUpdateToClientIfNeeded(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), + getValueFromKvProto(tsKvProto.getKv()), pathIdVer); + }); + } + + private void pushUpdateToClientIfNeeded(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String versionedId) { + if (newValue != null && (valueOld == null || !newValue.toString().equals(valueOld.toString()))) { + TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValue).timeout(this.config.getTimeout()).build(); + downlinkHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteResponseCallback(uplinkHandler, lwM2MClient, versionedId)); + } else { + log.error("Failed update resource [{}] [{}]", versionedId, newValue); + String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed or bad", + LOG_LWM2M_ERROR, versionedId, newValue); + uplinkHandler.logToTelemetry(lwM2MClient, logMsg); + log.info("Failed update resource [{}] [{}]", versionedId, newValue); + } + } + + /** + * @param pathIdVer - path resource + * @return - value of Resource into format KvProto or null + */ + private Object getResourceValueFormatKv(LwM2mClient lwM2MClient, String pathIdVer) { + LwM2mResource resourceValue = LwM2mTransportUtil.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); + if (resourceValue != null) { + ResourceModel.Type currentType = resourceValue.getType(); + ResourceModel.Type expectedType = helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); + return LwM2mValueConverterImpl.getInstance().convertValue(resourceValue.getValue(), currentType, expectedType, + new LwM2mPath(fromVersionedIdToObjectId(pathIdVer))); + } else { + return null; + } + } + + private String getStrValue(TransportProtos.TsKvProto tsKvProto) { + return tsKvProto.getKv().getStringV(); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/LwM2MAttributesService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/LwM2MAttributesService.java index 008788cb33..b9d7fa9e21 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/LwM2MAttributesService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/LwM2MAttributesService.java @@ -28,5 +28,7 @@ public interface LwM2MAttributesService { void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg getAttributesResponse, TransportProtos.SessionInfoProto sessionInfo); - void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification, TransportProtos.SessionInfoProto sessionInfo); + void onAttributesUpdate(TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification, TransportProtos.SessionInfoProto sessionInfo); + + void onAttributesUpdate(LwM2mClient lwM2MClient, List tsKvProtos); } 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 ec187549c8..21d6ec3d48 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 @@ -38,11 +38,9 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Queue; @@ -50,14 +48,13 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TRANSPORT_DEFAULT_LWM2M_VERSION; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsResourceTypeGetSimpleName; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getVerFromPathIdVerOrId; @@ -202,7 +199,7 @@ public class LwM2mClient implements Cloneable { } public Object getResourceValue(String pathRezIdVer, String pathRezId) { - String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer; + String pathRez = pathRezIdVer == null ? convertObjectIdToVersionedId(pathRezId, this.registration) : pathRezIdVer; if (this.resources.get(pathRez) != null) { return this.resources.get(pathRez).getLwM2mResource().getValue(); } @@ -210,7 +207,7 @@ public class LwM2mClient implements Cloneable { } public Object getResourceNameByRezId(String pathRezIdVer, String pathRezId) { - String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer; + String pathRez = pathRezIdVer == null ? convertObjectIdToVersionedId(pathRezId, this.registration) : pathRezIdVer; if (this.resources.get(pathRez) != null) { return this.resources.get(pathRez).getResourceModel().name; } 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 0f2f208b2b..006953af58 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 @@ -53,7 +53,10 @@ public interface LwM2mClientContext { LwM2mClient getClientByDeviceId(UUID deviceId); - void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials); + String getObjectIdByKeyNameFromProfile(TransportProtos.SessionInfoProto sessionInfo, String keyName); + + String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName); + void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials); } 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 b9206ca4df..d60482a0c1 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 @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.client; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.server.registration.Registration; import org.springframework.stereotype.Service; @@ -25,6 +26,7 @@ import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTrans import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; @@ -40,7 +42,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import static org.eclipse.leshan.core.SecurityMode.NO_SEC; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateObjectVerFromKey; @Slf4j @Service @@ -49,6 +53,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.c public class LwM2mClientContextImpl implements LwM2mClientContext { private final LwM2mTransportContext context; + private final LwM2MTransportServerConfig config; private final TbEditableSecurityStore securityStore; private final Map lwM2mClientsByEndpoint = new ConcurrentHashMap<>(); private final Map lwM2mClientsByRegistrationId = new ConcurrentHashMap<>(); @@ -160,6 +165,28 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { return lwM2mClient; } + /** + * Get path to resource from profile equal keyName + * + * @param sessionInfo - + * @param keyName - + * @return - + */ + @Override + public String getObjectIdByKeyNameFromProfile(TransportProtos.SessionInfoProto sessionInfo, String keyName) { + return getObjectIdByKeyNameFromProfile(getClientBySessionInfo(sessionInfo), keyName); + } + + @Override + public String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName) { + Lwm2mDeviceProfileTransportConfiguration profile = getProfile(lwM2mClient.getProfileId()); + + return profile.getObserveAttr().getKeyName().entrySet().stream() + .filter(e -> e.getValue().equals(keyName) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().orElseThrow( + () -> new IllegalArgumentException(keyName + " is not configured in the device profile!") + ).getKey(); + } + public Registration getRegistration(String registrationId) { return this.lwM2mClientsByRegistrationId.get(registrationId).getRegistration(); } @@ -200,7 +227,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { Arrays.stream(client.getRegistration().getObjectLinks()).forEach(link -> { LwM2mPath pathIds = new LwM2mPath(link.getUrl()); if (!pathIds.isRoot()) { - clientObjects.add(convertPathFromObjectIdToIdVer(link.getUrl(), client.getRegistration())); + clientObjects.add(convertObjectIdToVersionedId(link.getUrl(), client.getRegistration())); } }); return (clientObjects.size() > 0) ? clientObjects : null; @@ -211,4 +238,14 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { return lwM2mClientsByRegistrationId.values().stream().filter(e -> deviceId.equals(e.getDeviceId())).findFirst().orElse(null); } + private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { + ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config + .getModelProvider()); + Integer objectId = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)).getObjectId(); + String objectVer = validateObjectVerFromKey(pathIdVer); + return resourceModel != null && (isWritableNotOptional ? + objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() : + objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId))); + } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index b19fa4ac55..b39f6084cd 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -23,7 +23,10 @@ import org.eclipse.leshan.server.registration.Registration; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; +import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; +import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; @@ -62,9 +65,9 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.F import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; @@ -75,8 +78,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.S import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_VER_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsFwSateToFirmwareUpdateStatus; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.toOtaPackageUpdateStatus; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.splitCamelCaseString; @Slf4j @@ -140,8 +143,8 @@ public class LwM2mFwSwUpdate { private void initPathId() { if (FIRMWARE.equals(this.type)) { - this.pathPackageId = LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy ? - FW_PACKAGE_5_ID : LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy ? + this.pathPackageId = LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy ? + FW_PACKAGE_5_ID : LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy ? FW_PACKAGE_URI_ID : FW_PACKAGE_19_ID; this.pathStateId = FW_STATE_ID; this.pathResultId = FW_RESULT_ID; @@ -185,26 +188,26 @@ public class LwM2mFwSwUpdate { if (this.currentId != null) { this.stateUpdate = OtaPackageUpdateStatus.INITIATED.name(); this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LWM2M_INFO, null); - String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration()); + String targetIdVer = convertObjectIdToVersionedId(this.pathPackageId, this.lwM2MClient.getRegistration()); String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LWM2M_INFO, LwM2mOperationType.FW_UPDATE.name(), this.pathPackageId); handler.logToTelemetry(fwMsg, lwM2MClient.getRegistration().getId()); log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer); - if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy) { + if (LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy) { int chunkSize = 0; int chunk = 0; byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk); TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(firmwareChunk).timeout(handler.config.getTimeout()).build(); request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer)); - } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { + } else if (LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { String apiFont = "coap://176.36.143.9:5685"; String uri = apiFont + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); log.warn("89) coapUri: [{}]", uri); //TODO: user this.rpcRequest??? TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(uri).timeout(handler.config.getTimeout()).build(); request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer)); - } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { + } else if (LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { } } else { @@ -269,7 +272,7 @@ public class LwM2mFwSwUpdate { (this.currentTitle != null && pathName != null && this.currentTitle.equals(pathName))) { fwMsg = String.format("%s: The update was interrupted. The device has the same version: %s.", LOG_LWM2M_ERROR, this.currentVersion); - } else if (updateResultFw != null && updateResultFw > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { + } else if (updateResultFw != null && updateResultFw > UpdateResultFw.UPDATE_SUCCESSFULLY.code) { fwMsg = String.format("%s: The update was interrupted. The device has the status UpdateResult: error (%d).", LOG_LWM2M_ERROR, updateResultFw); } @@ -288,7 +291,7 @@ public class LwM2mFwSwUpdate { */ public boolean conditionalFwExecuteStart() { Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - return LwM2mTransportUtil.UpdateResultFw.INITIAL.code == updateResult; + return UpdateResultFw.INITIAL.code == updateResult; } /** @@ -297,7 +300,7 @@ public class LwM2mFwSwUpdate { */ public boolean conditionalFwExecuteAfterSuccess() { Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code == updateResult; + return UpdateResultFw.UPDATE_SUCCESSFULLY.code == updateResult; } /** @@ -306,7 +309,7 @@ public class LwM2mFwSwUpdate { */ public boolean conditionalFwExecuteAfterError() { Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code < updateResult; + return UpdateResultFw.UPDATE_SUCCESSFULLY.code < updateResult; } /** @@ -354,7 +357,7 @@ public class LwM2mFwSwUpdate { */ public void finishFwSwUpdate(DefaultLwM2MUplinkMsgHandler handler, boolean success) { Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); - String value = FIRMWARE.equals(this.type) ? LwM2mTransportUtil.UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type : + String value = FIRMWARE.equals(this.type) ? UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type : LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type; String key = splitCamelCaseString((String) this.lwM2MClient.getResourceNameByRezId(null, this.pathResultId)); if (success) { @@ -408,18 +411,18 @@ public class LwM2mFwSwUpdate { public void sendReadObserveInfo(LwM2mDownlinkMsgHandler request) { this.infoFwSwUpdate = true; - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( + this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( this.pathStateId, this.lwM2MClient.getRegistration())); - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( + this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( this.pathResultId, this.lwM2MClient.getRegistration())); - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( + this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( FW_3_VER_ID, this.lwM2MClient.getRegistration())); - if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy || - LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy || + if (LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy || + LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy || SOFTWARE.equals(this.type)) { - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( + this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( this.pathVerId, this.lwM2MClient.getRegistration())); - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( + this.pendingInfoRequestsStart.add(convertObjectIdToVersionedId( this.pathNameId, this.lwM2MClient.getRegistration())); } this.pendingInfoRequestsStart.forEach(versionedId -> { @@ -442,7 +445,7 @@ public class LwM2mFwSwUpdate { public void updateStateOta(DefaultLwM2MUplinkMsgHandler handler, LwM2mDownlinkMsgHandler request, Registration registration, String path, int value) { if (OBJ_5_BINARY.code == this.getUpdateStrategy()) { - if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { + if ((convertObjectIdToVersionedId(FW_RESULT_ID, registration).equals(path))) { if (DOWNLOADED.name().equals(this.getStateUpdate()) && this.conditionalFwExecuteStart()) { this.executeFwSwWare(handler, request); @@ -455,23 +458,23 @@ public class LwM2mFwSwUpdate { } } } else if (OBJ_5_TEMP_URL.code == this.getUpdateStrategy()) { - if (this.currentId != null && (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { - String state = equalsFwSateToFirmwareUpdateStatus(LwM2mTransportUtil.StateFw.fromStateFwByCode(value)).name(); + if (this.currentId != null && (convertObjectIdToVersionedId(FW_STATE_ID, registration).equals(path))) { + String state = toOtaPackageUpdateStatus(UpdateStateFw.fromStateFwByCode(value)).name(); if (StringUtils.isNotEmpty(state) && !FAILED.name().equals(this.stateUpdate) && !state.equals(this.stateUpdate)) { this.stateUpdate = state; this.sendSateOnThingsBoard(handler); } - if (value == LwM2mTransportUtil.StateFw.DOWNLOADED.code) { + if (value == UpdateStateFw.DOWNLOADED.code) { this.executeFwSwWare(handler, request); } handler.firmwareUpdateState.put(lwM2MClient.getEndpoint(), value); } - if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { - if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.INITIAL.code) { + if ((convertObjectIdToVersionedId(FW_RESULT_ID, registration).equals(path))) { + if (this.currentId != null && value == UpdateResultFw.INITIAL.code) { this.setStateUpdate(INITIATED.name()); - } else if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { + } else if (this.currentId != null && value == UpdateResultFw.UPDATE_SUCCESSFULLY.code) { this.setStateUpdate(UPDATED.name()); - } else if (value > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { + } else if (value > UpdateResultFw.UPDATE_SUCCESSFULLY.code) { this.setStateUpdate(FAILED.name()); } this.sendSateOnThingsBoard(handler); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java index 007aa1d3f8..f4b1059944 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceRequest.java @@ -17,17 +17,21 @@ package org.thingsboard.server.transport.lwm2m.server.downlink; import lombok.Builder; import lombok.Getter; +import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; public class TbLwM2MWriteReplaceRequest extends AbstractTbLwM2MTargetedDownlinkRequest { + @Getter + private final ContentFormat contentFormat; @Getter private final Object value; @Builder - private TbLwM2MWriteReplaceRequest(String versionedId, long timeout, Object value) { + private TbLwM2MWriteReplaceRequest(String versionedId, long timeout, ContentFormat contentFormat, Object value) { super(versionedId, timeout); + this.contentFormat = contentFormat; this.value = value; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 0b862ff725..68d7d76b73 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -17,21 +17,50 @@ package org.thingsboard.server.transport.lwm2m.server.ota; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.request.ContentFormat; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.DonAsynchron; +import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.ota.OtaPackageKey; import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.common.transport.TransportServiceCallback; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; +import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; +import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; +import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest; +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; +import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; @Slf4j @Service @@ -39,45 +68,228 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttribute @RequiredArgsConstructor public class DefaultLwM2MOtaUpdateService implements LwM2MOtaUpdateService { + public static final String FIRMWARE_VERSION = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION); + public static final String FIRMWARE_TITLE = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE); + public static final String FIRMWARE_URL = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.URL); + public static final String SOFTWARE_VERSION = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION); + public static final String SOFTWARE_TITLE = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE); + public static final String SOFTWARE_URL = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.URL); + + private static final String FW_PACKAGE_5_ID = "/5/0/0"; + private static final String FW_URL_ID = "/5/0/1"; + private static final String FW_EXECUTE_ID = "/5/0/2"; private static final String FW_NAME_ID = "/5/0/6"; private static final String FW_VER_ID = "/5/0/7"; private static final String SW_NAME_ID = "/9/0/0"; private static final String SW_VER_ID = "/9/0/1"; - private final Map fwStates = new ConcurrentHashMap<>(); - private final Map swStates = new ConcurrentHashMap<>(); + private final Map fwStates = new ConcurrentHashMap<>(); + private final Map swStates = new ConcurrentHashMap<>(); - private final LwM2MAttributesService attributesService; private final TransportService transportService; private final LwM2mClientContext clientContext; + private final LwM2MTransportServerConfig config; + private final LwM2mUplinkMsgHandler uplinkHandler; + private final LwM2mDownlinkMsgHandler downlinkHandler; + private final OtaPackageDataCache otaPackageDataCache; + private final LwM2mTransportServerHelper helper; + private ExecutorService executor; + + @Autowired + @Lazy + private LwM2MAttributesService attributesService; + + @PostConstruct + public void init() { + //TODO: define parallelism in constant + executor = ThingsBoardExecutors.newWorkStealingPool(4, "LwM2M OTA Updates"); + } @Override public void init(LwM2mClient client) { + //TODO: add locks by client fwInfo. //TODO: check that the client supports FW and SW by checking the supported objects in the model. List attributesToFetch = new ArrayList<>(); - if (client.isValidObjectVersion(FW_NAME_ID) || client.isValidObjectVersion(FW_VER_ID)) { - LwM2MClientOtaState fwState = getOrInitFwSate(client); - attributesToFetch.add(getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE)); - attributesToFetch.add(getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION)); + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + fwInfo.setSupported(client.isValidObjectVersion(FW_NAME_ID) || client.isValidObjectVersion(FW_VER_ID)); + if (fwInfo.isSupported()) { + attributesToFetch.add(FIRMWARE_TITLE); + attributesToFetch.add(FIRMWARE_VERSION); + attributesToFetch.add(FIRMWARE_URL); + } + + if (!attributesToFetch.isEmpty()) { + var future = attributesService.getSharedAttributes(client, attributesToFetch); + DonAsynchron.withCallback(future, attrs -> { + if (fwInfo.isSupported()) { + Optional newFirmwareTitle = getAttributeValue(attrs, FIRMWARE_TITLE); + Optional newFirmwareVersion = getAttributeValue(attrs, FIRMWARE_VERSION); + Optional newFirmwareUrl = getAttributeValue(attrs, FIRMWARE_URL); + if (newFirmwareTitle.isPresent() && newFirmwareVersion.isPresent()) { + onTargetFirmwareUpdate(client, newFirmwareTitle.get(), newFirmwareVersion.get(), newFirmwareUrl); + } + } + }, throwable -> { + if (fwInfo.isSupported()) { + fwInfo.setTargetFetchFailure(true); + } + }, executor); + } + } + + @Override + public void onTargetFirmwareUpdate(LwM2mClient client, String newFirmwareTitle, String newFirmwareVersion, Optional newFirmwareUrl) { + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + fwInfo.updateTarget(newFirmwareTitle, newFirmwareVersion, newFirmwareUrl); + startFirmwareUpdateIfNeeded(client, fwInfo); + } + + @Override + public void onCurrentFirmwareNameUpdate(LwM2mClient client, String name) { + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + fwInfo.setCurrentName(name); + } + + @Override + public void onCurrentFirmwareVersionUpdate(LwM2mClient client, String version) { + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + fwInfo.setCurrentVersion(version); + } + + @Override + public void onCurrentFirmwareStateUpdate(LwM2mClient client, Long state) { + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + UpdateStateFw newState = UpdateStateFw.fromStateFwByCode(state.intValue()); + if (UpdateStateFw.DOWNLOADED.equals(newState)) { + executeFwUpdate(client); + } + fwInfo.setUpdateState(newState); + sendStateUpdateToTelemetry(client, fwInfo, LwM2mTransportUtil.toOtaPackageUpdateStatus(newState)); + } + + @Override + public void onCurrentFirmwareResultUpdate(LwM2mClient client, Long code) { + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); + UpdateResultFw result = UpdateResultFw.fromUpdateResultFwByCode(code.intValue()); + sendStateUpdateToTelemetry(client, fwInfo, LwM2mTransportUtil.toOtaPackageUpdateStatus(result)); + if (result.isAgain && fwInfo.getRetryAttempts() <= 2) { + fwInfo.setRetryAttempts(fwInfo.getRetryAttempts() + 1); + startFirmwareUpdateIfNeeded(client, fwInfo); + } else { + fwInfo.setUpdateResult(result); } + } - if (client.isValidObjectVersion(SW_NAME_ID) || client.isValidObjectVersion(SW_VER_ID)) { - LwM2MClientOtaState swState = getOrInitSwSate(client); - attributesToFetch.add(getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE)); - attributesToFetch.add(getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION)); + @Override + public void onTargetSoftwareUpdate(LwM2mClient client, String newSoftwareTitle, String newSoftwareVersion) { + + } + + private void startFirmwareUpdateIfNeeded(LwM2mClient client, LwM2MClientOtaInfo fwInfo) { + if (fwInfo.isUpdateRequired()) { + if (StringUtils.isNotEmpty(fwInfo.getTargetUrl())) { + log.info("[{}] Starting update to [{}{}] using URL: {}", client.getEndpoint(), fwInfo.getTargetName(), fwInfo.getTargetVersion(), fwInfo.getTargetUrl()); + startFirmwareUpdateUsingUrl(client, fwInfo.getTargetUrl()); + } else { + startFirmwareUpdateUsingBinary(client, fwInfo); + } } + } + + private void startFirmwareUpdateUsingUrl(LwM2mClient client, String url) { + String targetIdVer = convertObjectIdToVersionedId(FW_URL_ID, client.getRegistration()); + TbLwM2MWriteUpdateRequest request = TbLwM2MWriteUpdateRequest.builder().versionedId(targetIdVer).value(url).timeout(config.getTimeout()).build(); + downlinkHandler.sendWriteUpdateRequest(client, request, new TbLwM2MWriteResponseCallback(uplinkHandler, client, targetIdVer)); + } + + public void startFirmwareUpdateUsingBinary(LwM2mClient client, LwM2MClientOtaInfo fwInfo) { + String versionedId = convertObjectIdToVersionedId(FW_PACKAGE_5_ID, client.getRegistration()); + this.transportService.process(client.getSession(), createOtaPackageRequestMsg(client.getSession(), OtaPackageType.FIRMWARE.name()), + new TransportServiceCallback<>() { + @Override + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { + if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) + && response.getType().equals(OtaPackageType.FIRMWARE.name())) { + UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); + var strategy = fwInfo.getStrategy(); + switch (strategy) { + case OBJ_5_BINARY: + byte[] firmwareChunk = otaPackageDataCache.get(otaPackageId.toString(), 0, 0); + TbLwM2MWriteReplaceRequest writeRequest = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId) + .value(firmwareChunk).contentFormat(ContentFormat.OPAQUE) + .timeout(config.getTimeout()).build(); + downlinkHandler.sendWriteReplaceRequest(client, writeRequest, new TbLwM2MWriteResponseCallback(uplinkHandler, client, versionedId)); + break; + case OBJ_5_TEMP_URL: + startFirmwareUpdateUsingUrl(client, fwInfo.getBaseUrl() + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + otaPackageId.toString()); + break; + default: + //TODO: send log to telemetry + } + } else { + //TODO: send log to telemetry + } + } - var future = attributesService.getSharedAttributes(client, attributesToFetch); + @Override + public void onError(Throwable e) { + log.trace("Failed to process firmwareUpdate ", e); + } + }); } - private LwM2MClientOtaState getOrInitFwSate(LwM2mClient client) { - //TODO: fetch state from the cache. - return fwStates.computeIfAbsent(client.getEndpoint(), endpoint -> new LwM2MClientOtaState()); + private TransportProtos.GetOtaPackageRequestMsg createOtaPackageRequestMsg(TransportProtos.SessionInfoProto sessionInfo, String nameFwSW) { + return TransportProtos.GetOtaPackageRequestMsg.newBuilder() + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) + .setTenantIdMSB(sessionInfo.getTenantIdMSB()) + .setTenantIdLSB(sessionInfo.getTenantIdLSB()) + .setType(nameFwSW) + .build(); + } + + private void executeFwUpdate(LwM2mClient client) { + TbLwM2MExecuteRequest request = TbLwM2MExecuteRequest.builder().versionedId(FW_EXECUTE_ID).timeout(config.getTimeout()).build(); + downlinkHandler.sendExecuteRequest(client, request, new TbLwM2MExecuteCallback(uplinkHandler, client, FW_EXECUTE_ID)); + } + + private Optional getAttributeValue(List attrs, String keyName) { + for (TransportProtos.TsKvProto attr : attrs) { + if (keyName.equals(attr.getKv().getKey())) { + if (attr.getKv().getType().equals(TransportProtos.KeyValueType.STRING_V)) { + return Optional.of(attr.getKv().getStringV()); + } else { + return Optional.empty(); + } + } + } + return Optional.empty(); + } + + private LwM2MClientOtaInfo getOrInitFwInfo(LwM2mClient client) { + //TODO: fetch state from the cache or DB. + return fwStates.computeIfAbsent(client.getEndpoint(), endpoint -> { + var profile = clientContext.getProfile(client.getProfileId()); + return new LwM2MClientOtaInfo(endpoint, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy(), + profile.getClientLwM2mSettings().getFwUpdateRecourse()); + }); + } + + private LwM2MClientOtaInfo getOrInitSwInfo(LwM2mClient client) { + //TODO: fetch state from the cache or DB. + return swStates.computeIfAbsent(client.getEndpoint(), endpoint -> { + var profile = clientContext.getProfile(client.getProfileId()); + return new LwM2MClientOtaInfo(endpoint, OtaPackageType.SOFTWARE, profile.getClientLwM2mSettings().getSwUpdateStrategy(), profile.getClientLwM2mSettings().getSwUpdateRecourse()); + }); + } - private LwM2MClientOtaState getOrInitSwSate(LwM2mClient client) { - //TODO: fetch state from the cache. - return swStates.computeIfAbsent(client.getEndpoint(), endpoint -> new LwM2MClientOtaState()); + private void sendStateUpdateToTelemetry(LwM2mClient client, LwM2MClientOtaInfo fwInfo, OtaPackageUpdateStatus status) { + List result = new ArrayList<>(); + TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(getAttributeKey(fwInfo.getType(), STATE)); + kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(status.name()); + result.add(kvProto.build()); + helper.sendParametersOnThingsboardTelemetry(result, client.getSession()); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java new file mode 100644 index 0000000000..26f7abed4f --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaInfo.java @@ -0,0 +1,99 @@ +/** + * 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.ota; + +import lombok.Data; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; +import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; +import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; + +import java.util.Optional; + +@Data +public class LwM2MClientOtaInfo { + + private final String endpoint; + private final OtaPackageType type; + + private String baseUrl; + private boolean supported; + + private boolean targetFetchFailure; + private String targetName; + private String targetVersion; + private String targetUrl; + + private boolean currentFetchFailure; + private String currentName; + private String currentVersion; + + //TODO: use value from device if applicable; + private LwM2MFirmwareUpdateStrategy strategy; + private UpdateStateFw updateState; + private UpdateResultFw updateResult; + + private String failedPackageId; + private int retryAttempts; + + public LwM2MClientOtaInfo(String endpoint, OtaPackageType type, Integer strategyCode, String baseUrl) { + this.endpoint = endpoint; + this.type = type; + this.strategy = LwM2MFirmwareUpdateStrategy.fromStrategyFwByCode(strategyCode); + this.baseUrl = baseUrl; + } + + public void updateTarget(String targetName, String targetVersion, Optional newFirmwareUrl) { + this.targetName = targetName; + this.targetVersion = targetVersion; + this.targetUrl = newFirmwareUrl.orElse(null); + } + + public boolean isUpdateRequired() { + if (StringUtils.isEmpty(targetName) || + StringUtils.isEmpty(targetVersion) || + (StringUtils.isEmpty(currentName) && StringUtils.isEmpty(currentVersion))) { + return false; + } else { + String targetPackageId = getPackageId(targetName, targetVersion); + String currentPackageId = getPackageId(currentName, currentVersion); + if (StringUtils.isNotEmpty(failedPackageId) && failedPackageId.equals(targetPackageId)) { + return false; + } else { + return !targetPackageId.equals(currentPackageId); + } + } + } + + public void setUpdateResult(UpdateResultFw updateResult) { + this.updateResult = updateResult; + switch (updateResult) { + case INITIAL: + break; + case UPDATE_SUCCESSFULLY: + retryAttempts = 0; + break; + default: + failedPackageId = getPackageId(targetName, targetVersion); + break; + } + } + + private static String getPackageId(String name, String version) { + return (StringUtils.isNotEmpty(name) ? name : "") + (StringUtils.isNotEmpty(version) ? version : ""); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaState.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaState.java index 43dd874a09..b4201f3abd 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaState.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MClientOtaState.java @@ -15,5 +15,8 @@ */ package org.thingsboard.server.transport.lwm2m.server.ota; -public class LwM2MClientOtaState { +public enum LwM2MClientOtaState { + + IDLE, IN_PROGRESS, SUCCESS, FAILED + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java index 9dcbab5d9e..e1bde7df8b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/LwM2MOtaUpdateService.java @@ -17,8 +17,21 @@ package org.thingsboard.server.transport.lwm2m.server.ota; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import java.util.Optional; + public interface LwM2MOtaUpdateService { void init(LwM2mClient client); + void onTargetFirmwareUpdate(LwM2mClient client, String newFirmwareTitle, String newFirmwareVersion, Optional newFirmwareUrl); + + void onTargetSoftwareUpdate(LwM2mClient client, String newSoftwareTitle, String newSoftwareVersion); + + void onCurrentFirmwareNameUpdate(LwM2mClient client, String name); + + void onCurrentFirmwareVersionUpdate(LwM2mClient client, String version); + + void onCurrentFirmwareStateUpdate(LwM2mClient client, Long state); + + void onCurrentFirmwareResultUpdate(LwM2mClient client, Long result); } 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 b2c8f73ac8..533458ce76 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 @@ -238,7 +238,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { IdOrKeyRequest requestParams = JacksonUtil.fromString(rpcRequst.getParams(), IdOrKeyRequest.class); String targetId; if (StringUtils.isNotEmpty(requestParams.getKey())) { - targetId = uplinkHandler.getObjectIdByKeyNameFromProfile(client, requestParams.getKey()); + targetId = clientContext.getObjectIdByKeyNameFromProfile(client, requestParams.getKey()); } else if (StringUtils.isNotEmpty(requestParams.getId())) { targetId = requestParams.getId(); } else { 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 ec5fbbb6dd..97fe273857 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 @@ -68,23 +68,20 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mFwSwUpdate; import org.thingsboard.server.transport.lwm2m.server.client.ParametersAnalyzeResult; -import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MLatchCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MLatchCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; @@ -109,8 +106,10 @@ import java.util.stream.Collectors; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; @@ -119,9 +118,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateObjectVerFromKey; @Slf4j @@ -148,9 +146,11 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { public final Map firmwareUpdateState; - public DefaultLwM2MUplinkMsgHandler(TransportService transportService, LwM2MAttributesService attributesService, LwM2MOtaUpdateService otaService, + public DefaultLwM2MUplinkMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, LwM2mClientContext clientContext, + @Lazy LwM2MOtaUpdateService otaService, + @Lazy LwM2MAttributesService attributesService, @Lazy LwM2MRpcRequestHandler rpcHandler, @Lazy LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler, OtaPackageDataCache otaPackageDataCache, @@ -561,21 +561,16 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private void updateResourcesValue(LwM2mClient lwM2MClient, LwM2mResource lwM2mResource, String path) { Registration registration = lwM2MClient.getRegistration(); if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider())) { - /** version != null - * set setClient_fw_info... = value - **/ - if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getFwUpdate().initReadValue(this, this.defaultLwM2MDownlinkMsgHandler, path); - } - if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { - lwM2MClient.getSwUpdate().initReadValue(this, this.defaultLwM2MDownlinkMsgHandler, path); - } - - if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path)) || - (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { - LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(this, clientContext); - log.warn("93) path: [{}] value: [{}]", path, lwM2mResource.getValue()); - fwUpdate.updateStateOta(this, defaultLwM2MDownlinkMsgHandler, registration, path, ((Long) lwM2mResource.getValue()).intValue()); + if (path.equals(convertObjectIdToVersionedId(FW_NAME_ID, registration))) { + otaService.onCurrentFirmwareNameUpdate(lwM2MClient, (String) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_3_VER_ID, registration))) { + otaService.onCurrentFirmwareVersionUpdate(lwM2MClient, (String) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_5_VER_ID, registration))) { + otaService.onCurrentFirmwareVersionUpdate(lwM2MClient, (String) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_STATE_ID, registration))) { + otaService.onCurrentFirmwareStateUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); + } else if (path.equals(convertObjectIdToVersionedId(FW_RESULT_ID, registration))) { + otaService.onCurrentFirmwareResultUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); } this.updateAttrTelemetry(registration, Collections.singleton(path)); } else { @@ -684,7 +679,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { String resourceName = names.get(pathIdVer); if (resourceName != null && !resourceName.isEmpty()) { try { - LwM2mResource resourceValue = getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); + LwM2mResource resourceValue = LwM2mTransportUtil.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); if (resourceValue != null) { ResourceModel.Type currentType = resourceValue.getType(); ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); @@ -720,38 +715,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { return null; } - /** - * @param pathIdVer - path resource - * @return - value of Resource into format KvProto or null - */ - private Object getResourceValueFormatKv(LwM2mClient lwM2MClient, String pathIdVer) { - LwM2mResource resourceValue = this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); - if (resourceValue != null) { - ResourceModel.Type currentType = resourceValue.getType(); - ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); - return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, - new LwM2mPath(fromVersionedIdToObjectId(pathIdVer))); - } else { - return null; - } - } - - /** - * @param lwM2MClient - - * @param path - - * @return - return value of Resource by idPath - */ - private LwM2mResource getResourceValueFromLwM2MClient(LwM2mClient lwM2MClient, String path) { - LwM2mResource lwm2mResourceValue = null; - ResourceValue resourceValue = lwM2MClient.getResources().get(path); - if (resourceValue != null) { - if (new LwM2mPath(fromVersionedIdToObjectId(path)).isResource()) { - lwm2mResourceValue = lwM2MClient.getResources().get(path).getLwM2mResource(); - } - } - return lwm2mResourceValue; - } - @Override public void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request) { if (request.getNode() instanceof LwM2mResource) { @@ -890,19 +853,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { } } - private void pushUpdateToClientIfNeeded(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String versionedId) { - if (newValue != null && (valueOld == null || !newValue.toString().equals(valueOld.toString()))) { - TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValue).timeout(this.config.getTimeout()).build(); - defaultLwM2MDownlinkMsgHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteResponseCallback(this, lwM2MClient, versionedId)); - } else { - log.error("Failed update resource [{}] [{}]", versionedId, newValue); - String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed or bad", - LOG_LWM2M_ERROR, versionedId, newValue); - this.logToTelemetry(lwM2MClient, logMsg); - log.info("Failed update resource [{}] [{}]", versionedId, newValue); - } - } - /** * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...) * config attr/telemetry... in profile @@ -912,59 +862,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList()); } - /** - * Get path to resource from profile equal keyName - * - * @param sessionInfo - - * @param keyName - - * @return - - */ - @Override - public String getObjectIdByKeyNameFromProfile(TransportProtos.SessionInfoProto sessionInfo, String keyName) { - return getObjectIdByKeyNameFromProfile(clientContext.getClientBySessionInfo(sessionInfo), keyName); - } - - @Override - public String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName) { - Lwm2mDeviceProfileTransportConfiguration profile = clientContext.getProfile(lwM2mClient.getProfileId()); - - return profile.getObserveAttr().getKeyName().entrySet().stream() - .filter(e -> e.getValue().equals(keyName) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().orElseThrow( - () -> new IllegalArgumentException(keyName + " is not configured in the device profile!") - ).getKey(); - } - - /** - * #1.1 If two names have equal path => last time attribute - * #2.1 if there is a difference in values between the current resource values and the shared attribute values - * => send to client Request Update of value (new value from shared attribute) - * and LwM2MClient.delayedRequests.add(path) - * #2.1 if there is not a difference in values between the current resource values and the shared attribute values - * - * @param tsKvProtos - */ - public void onAttributesUpdate(LwM2mClient lwM2MClient, List tsKvProtos) { - log.trace("[{}] onAttributesUpdate [{}]", lwM2MClient.getEndpoint(), tsKvProtos); - tsKvProtos.forEach(tsKvProto -> { - String pathIdVer = this.getObjectIdByKeyNameFromProfile(lwM2MClient, tsKvProto.getKv().getKey()); - if (pathIdVer != null) { - // #1.1 - if (lwM2MClient.getSharedAttributes().containsKey(pathIdVer)) { - if (tsKvProto.getTs() > lwM2MClient.getSharedAttributes().get(pathIdVer).getTs()) { - lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto); - } - } else { - lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto); - } - } - }); - // #2.1 - lwM2MClient.getSharedAttributes().forEach((pathIdVer, tsKvProto) -> { - this.pushUpdateToClientIfNeeded(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), - getValueFromKvProto(tsKvProto.getKv()), pathIdVer); - }); - } - /** * @param lwM2MClient - * @return SessionInfoProto - @@ -1017,7 +914,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { keysToFetch.removeAll(OtaPackageUtil.ALL_FW_ATTRIBUTE_KEYS); keysToFetch.removeAll(OtaPackageUtil.ALL_SW_ATTRIBUTE_KEYS); DonAsynchron.withCallback(attributesService.getSharedAttributes(lwM2MClient, keysToFetch), - v -> onAttributesUpdate(lwM2MClient, v), + v -> attributesService.onAttributesUpdate(lwM2MClient, v), t -> log.error("[{}] Failed to get attributes", lwM2MClient.getEndpoint(), t), registrationExecutor); } @@ -1045,7 +942,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { fwUpdate.setCurrentTitle(response.getTitle()); fwUpdate.setCurrentId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())); // if (rpcRequest == null) { - fwUpdate.sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); + fwUpdate.sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); // } else { // fwUpdate.writeFwSwWare(handler, defaultLwM2MDownlinkMsgHandler); // } @@ -1059,7 +956,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); log.trace(msgError); // if (rpcRequest != null) { - //TODO: refactor + //TODO: refactor // sendErrorRpcResponse(rpcRequest, msgError, sessionInfo); // } } @@ -1091,7 +988,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { lwM2MClient.getSwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); lwM2MClient.getSwUpdate().sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); // if (rpcRequest == null) { - lwM2MClient.getSwUpdate().sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); + lwM2MClient.getSwUpdate().sendReadObserveInfo(defaultLwM2MDownlinkMsgHandler); // } else { // lwM2MClient.getSwUpdate().writeFwSwWare(handler, defaultLwM2MDownlinkMsgHandler); // } @@ -1124,16 +1021,6 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { return profile.getObserveAttr().getKeyName(); } - private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { - ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config - .getModelProvider()); - Integer objectId = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)).getObjectId(); - String objectVer = validateObjectVerFromKey(pathIdVer); - return resourceModel != null && (isWritableNotOptional ? - objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() : - objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId))); - } - public LwM2MTransportServerConfig getConfig() { return this.config; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java index 041af97474..517db388e4 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java @@ -60,9 +60,5 @@ public interface LwM2mUplinkMsgHandler { void onToTransportUpdateCredentials(TransportProtos.ToTransportUpdateCredentialsProto updateCredentials); - String getObjectIdByKeyNameFromProfile(TransportProtos.SessionInfoProto sessionInfo, String name); - - String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName); - LwM2MTransportServerConfig getConfig(); }