rpcSubscriptionsToRemove = rpcSubscriptions.entrySet().stream().filter(kv -> System.currentTimeMillis() > kv.getValue()).map(Map.Entry::getKey).collect(Collectors.toSet());
- rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove);
- }
-
- public void sentRpcResponse(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);
- }
- }
- this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo());
- }
-
- @Override
- public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, SessionInfoProto sessionInfo) {
- log.warn ("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
- transportService.process(sessionInfo, toDeviceResponse, null);
- }
-
- public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) {
- log.info("[{}] toServerRpcResponse", toServerResponse);
- }
-
- /**
- * Trigger Server path = "/1/0/8"
- *
- * Trigger bootStrap path = "/1/0/9" - have to implemented on client
- */
- @Override
- public void doTrigger(Registration registration, String path) {
- lwM2mTransportRequest.sendAllRequest(registration, path, EXECUTE,
- ContentFormat.TLV.getName(), null, this.config.getTimeout(), null);
- }
-
- /**
- * Deregister session in transport
- *
- * @param sessionInfo - lwm2m client
- */
- @Override
- public void doDisconnect(SessionInfoProto sessionInfo) {
- transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null);
- transportService.deregisterSession(sessionInfo);
- }
-
- /**
- * Session device in thingsboard is closed
- *
- * @param sessionInfo - lwm2m client
- */
- private void doCloseSession(SessionInfoProto sessionInfo) {
- TransportProtos.SessionEvent event = SessionEvent.CLOSED;
- TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder()
- .setSessionType(TransportProtos.SessionType.ASYNC)
- .setEvent(event).build();
- transportService.process(sessionInfo, msg, null);
- }
-
- /**
- * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay,
- * * if you need to do long time processing use a dedicated thread pool.
- *
- * @param registration -
- */
- @Override
- public void onAwakeDev(Registration registration) {
- log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
- this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration.getId());
- //TODO: associate endpointId with device information.
- }
-
- /**
- * @param logMsg - text msg
- * @param registrationId - Id of Registration LwM2M Client
- */
- @Override
- public void sendLogsToThingsboard(String logMsg, String registrationId) {
- SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registrationId);
- if (logMsg != null && sessionInfo != null) {
- if (logMsg.length() > 1024) {
- logMsg = logMsg.substring(0, 1024);
- }
- this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo);
- }
- }
-
- /**
- * #1 clientOnlyObserveAfterConnect == true
- * - Only Observe Request to the client marked as observe from the profile configuration.
- * #2. clientOnlyObserveAfterConnect == false
- * После регистрации отправляю запрос на read всех ресурсов, которые после регистрации есть у клиента,
- * а затем запрос на observe (edited)
- * - Read Request to the client after registration to read all resource values for all objects
- * - then Observe Request to the client marked as observe from the profile configuration.
- *
- * @param registration - Registration LwM2M Client
- * @param lwM2MClient - object with All parameters off client
- */
- private void initLwM2mFromClientValue(Registration registration, LwM2mClient lwM2MClient) {
- LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(registration);
- Set clientObjects = clientContext.getSupportedIdVerInClient(registration);
- if (clientObjects != null && clientObjects.size() > 0) {
- if (LWM2M_STRATEGY_2 == LwM2mTransportUtil.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
- // #2
- lwM2MClient.getPendingReadRequests().addAll(clientObjects);
- clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(),
- null, this.config.getTimeout(), null));
- }
- // #1
- this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, READ, clientObjects);
- this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, OBSERVE, clientObjects);
- this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, WRITE_ATTRIBUTES, clientObjects);
- this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, DISCOVER, clientObjects);
- }
- }
-
- /**
- * @param registration -
- * @param lwM2mObject -
- * @param pathIdVer -
- */
- private void updateObjectResourceValue(Registration registration, LwM2mObject lwM2mObject, String pathIdVer) {
- LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer));
- lwM2mObject.getInstances().forEach((instanceId, instance) -> {
- String pathInstance = pathIds.toString() + "/" + instanceId;
- this.updateObjectInstanceResourceValue(registration, instance, pathInstance);
- });
- }
-
- /**
- * @param registration -
- * @param lwM2mObjectInstance -
- * @param pathIdVer -
- */
- private void updateObjectInstanceResourceValue(Registration registration, LwM2mObjectInstance lwM2mObjectInstance, String pathIdVer) {
- LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer));
- lwM2mObjectInstance.getResources().forEach((resourceId, resource) -> {
- String pathRez = pathIds.toString() + "/" + resourceId;
- this.updateResourcesValue(registration, resource, pathRez);
- });
- }
-
- /**
- * 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()
- * @param path - resource
- */
- private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) {
- LwM2mClient lwM2MClient = clientContext.getOrRegister(registration);
- if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider())) {
- /** version != null
- * set setClient_fw_info... = value
- **/
- if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
- lwM2MClient.getFwUpdate().initReadValue(this, this.lwM2mTransportRequest, path);
- }
- if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
- lwM2MClient.getSwUpdate().initReadValue(this, this.lwM2mTransportRequest, path);
- }
-
- /**
- * Before operation Execute (FwUpdate) inspection Update Result :
- * - after finished operation Write result: success (FwUpdate): fw_state = DOWNLOADED
- * - before start operation Execute (FwUpdate) Update Result = 0 - Initial value
- * - start Execute (FwUpdate)
- * After finished operation Execute (FwUpdate) inspection Update Result :
- * - after start operation Execute (FwUpdate): fw_state = UPDATING
- * - after success finished operation Execute (FwUpdate) Update Result == 1 ("Firmware updated successfully")
- * - finished operation Execute (FwUpdate)
- */
- if (lwM2MClient.getFwUpdate() != null
- && (convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) {
- if (DOWNLOADED.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
- && lwM2MClient.getFwUpdate().conditionalFwExecuteStart()) {
- lwM2MClient.getFwUpdate().executeFwSwWare(this, this.lwM2mTransportRequest);
- } else if (UPDATING.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
- && lwM2MClient.getFwUpdate().conditionalFwExecuteAfterSuccess()) {
- lwM2MClient.getFwUpdate().finishFwSwUpdate(this, true);
- } else if (UPDATING.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
- && lwM2MClient.getFwUpdate().conditionalFwExecuteAfterError()) {
- lwM2MClient.getFwUpdate().finishFwSwUpdate(this, false);
- }
- }
-
- /**
- * Before operation Execute (SwUpdate) inspection Update Result :
- * - after finished operation Write result: success (SwUpdate): fw_state = DOWNLOADED
- * - before operation Execute (SwUpdate) Update Result = 3 - Successfully Downloaded and package integrity verified
- * - start Execute (SwUpdate)
- * After finished operation Execute (SwUpdate) inspection Update Result :
- * - after start operation Execute (SwUpdate): fw_state = UPDATING
- * - after success finished operation Execute (SwUpdate) Update Result == 2 "Software successfully installed.""
- * - after success finished operation Execute (SwUpdate) Update Result == 2 "Software successfully installed.""
- * - finished operation Execute (SwUpdate)
- */
- if (lwM2MClient.getSwUpdate() != null
- && (convertPathFromObjectIdToIdVer(SW_RESULT_ID, registration).equals(path))) {
- if (DOWNLOADED.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
- && lwM2MClient.getSwUpdate().conditionalSwUpdateExecute()) {
- lwM2MClient.getSwUpdate().executeFwSwWare(this, this.lwM2mTransportRequest);
- } else if (UPDATING.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
- && lwM2MClient.getSwUpdate().conditionalSwExecuteAfterSuccess()) {
- lwM2MClient.getSwUpdate().finishFwSwUpdate(this, true);
- } else if (UPDATING.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
- && lwM2MClient.getSwUpdate().conditionalSwExecuteAfterError()) {
- lwM2MClient.getSwUpdate().finishFwSwUpdate(this, false);
- }
- }
- Set paths = new HashSet<>();
- paths.add(path);
- this.updateAttrTelemetry(registration, paths);
- } else {
- log.error("Fail update Resource [{}]", lwM2mResource);
- }
- }
-
-
- /**
- * send Attribute and Telemetry to Thingsboard
- * #1 - get AttrName/TelemetryName with value from LwM2MClient:
- * -- resourceId == path from LwM2MClientProfile.postAttributeProfile/postTelemetryProfile/postObserveProfile
- * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId)
- * #2 - set Attribute/Telemetry
- *
- * @param registration - Registration LwM2M Client
- */
- private void updateAttrTelemetry(Registration registration, Set paths) {
- try {
- ResultsAddKeyValueProto results = this.getParametersFromProfile(registration, paths);
- SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration);
- if (results != null && sessionInfo != null) {
- if (results.getResultAttributes().size() > 0) {
- this.helper.sendParametersOnThingsboardAttribute(results.getResultAttributes(), sessionInfo);
- }
- if (results.getResultTelemetries().size() > 0) {
- this.helper.sendParametersOnThingsboardTelemetry(results.getResultTelemetries(), sessionInfo);
- }
- }
- } catch (Exception e) {
- log.error("UpdateAttrTelemetry", e);
- }
- }
-
- /**
- * Start observe/read: Attr/Telemetry
- * #1 - Analyze: path in resource profile == client resource
- *
- * @param registration -
- */
- private void initReadAttrTelemetryObserveToClient(Registration registration, LwM2mClient lwM2MClient,
- LwM2mTypeOper typeOper, Set clientObjects) {
- LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(registration);
- Set result = null;
- ConcurrentHashMap params = null;
- if (READ.equals(typeOper)) {
- result = JacksonUtil.fromString(lwM2MClientProfile.getPostAttributeProfile().toString(),
- new TypeReference<>() {
- });
- result.addAll(JacksonUtil.fromString(lwM2MClientProfile.getPostTelemetryProfile().toString(),
- new TypeReference<>() {
- }));
- } else if (OBSERVE.equals(typeOper)) {
- result = JacksonUtil.fromString(lwM2MClientProfile.getPostObserveProfile().toString(),
- new TypeReference<>() {
- });
- } else if (DISCOVER.equals(typeOper)) {
- result = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()).keySet();
- } else if (WRITE_ATTRIBUTES.equals(typeOper)) {
- params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile());
- result = params.keySet();
- }
- 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());
- if (!pathSend.isEmpty()) {
- 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.config.getTimeout(), null);
- });
- if (OBSERVE.equals(typeOper)) {
- lwM2MClient.initReadValue(this, null);
- }
- }
- }
- }
-
- private ConcurrentHashMap getPathForWriteAttributes(JsonObject objectJson) {
- ConcurrentHashMap pathAttributes = new Gson().fromJson(objectJson.toString(),
- new TypeToken>() {
- }.getType());
- return pathAttributes;
- }
-
- private void onDeviceUpdate(LwM2mClient lwM2MClient, Device device, Optional deviceProfileOpt) {
- deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceProfileUpdate(Collections.singleton(lwM2MClient.getRegistration().getId()), deviceProfile));
- lwM2MClient.onDeviceUpdate(device, deviceProfileOpt);
- }
-
- /**
- * // * @param attributes - new JsonObject
- * // * @param telemetry - new JsonObject
- *
- * @param registration - Registration LwM2M Client
- * @param path -
- */
- private ResultsAddKeyValueProto getParametersFromProfile(Registration registration, Set path) {
- if (path != null && path.size() > 0) {
- ResultsAddKeyValueProto results = new ResultsAddKeyValueProto();
- LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(registration);
- List resultAttributes = new ArrayList<>();
- lwM2MClientProfile.getPostAttributeProfile().forEach(pathIdVer -> {
- if (path.contains(pathIdVer.getAsString())) {
- TransportProtos.KeyValueProto kvAttr = this.getKvToThingsboard(pathIdVer.getAsString(), registration);
- if (kvAttr != null) {
- resultAttributes.add(kvAttr);
- }
- }
- });
- List resultTelemetries = new ArrayList<>();
- lwM2MClientProfile.getPostTelemetryProfile().forEach(pathIdVer -> {
- if (path.contains(pathIdVer.getAsString())) {
- TransportProtos.KeyValueProto kvAttr = this.getKvToThingsboard(pathIdVer.getAsString(), registration);
- if (kvAttr != null) {
- resultTelemetries.add(kvAttr);
- }
- }
- });
- if (resultAttributes.size() > 0) {
- results.setResultAttributes(resultAttributes);
- }
- if (resultTelemetries.size() > 0) {
- results.setResultTelemetries(resultTelemetries);
- }
- return results;
- }
- return null;
- }
-
- private TransportProtos.KeyValueProto getKvToThingsboard(String pathIdVer, Registration registration) {
- LwM2mClient lwM2MClient = this.clientContext.getClientByRegistrationId(registration.getId());
- JsonObject names = clientContext.getProfiles().get(lwM2MClient.getProfileId()).getPostKeyNameProfile();
- if (names != null && names.has(pathIdVer)) {
- String resourceName = names.get(pathIdVer).getAsString();
- if (resourceName != null && !resourceName.isEmpty()) {
- try {
- LwM2mResource resourceValue = lwM2MClient != null ? getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) : null;
- if (resourceValue != null) {
- ResourceModel.Type currentType = resourceValue.getType();
- ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer);
- Object valueKvProto = null;
- if (resourceValue.isMultiInstances()) {
- valueKvProto = new JsonObject();
- Object finalvalueKvProto = valueKvProto;
- Gson gson = new GsonBuilder().create();
- resourceValue.getValues().forEach((k, v) -> {
- Object val = this.converter.convertValue(v, currentType, expectedType,
- new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
- JsonElement element = gson.toJsonTree(val, val.getClass());
- ((JsonObject) finalvalueKvProto).add(String.valueOf(k), element);
- });
- valueKvProto = gson.toJson(valueKvProto);
- } else {
- valueKvProto = this.converter.convertValue(resourceValue.getValue(), currentType, expectedType,
- new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
- }
- return valueKvProto != null ? this.helper.getKvAttrTelemetryToThingsboard(currentType, resourceName, valueKvProto, resourceValue.isMultiInstances()) : null;
- }
- } catch (Exception e) {
- log.error("Failed to add parameters.", e);
- }
- }
- } else {
- log.error("Failed to add parameters. path: [{}], names: [{}]", pathIdVer, names);
- }
- 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(convertPathFromIdVerToObjectId(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(convertPathFromIdVerToObjectId(path)).isResource()) {
- lwm2mResourceValue = lwM2MClient.getResources().get(path).getLwM2mResource();
- }
- }
- return lwm2mResourceValue;
- }
-
- /**
- * Update resource (attribute) value on thingsboard after update value in client
- *
- * @param registration -
- * @param path -
- * @param request -
- */
- public void onWriteResponseOk(Registration registration, String path, WriteRequest request) {
- if (request.getNode() instanceof LwM2mResource) {
- this.updateResourcesValue(registration, ((LwM2mResource) request.getNode()), path);
- } else if (request.getNode() instanceof LwM2mObjectInstance) {
- ((LwM2mObjectInstance) request.getNode()).getResources().forEach((resId, resource) -> {
- this.updateResourcesValue(registration, resource, path + "/" + resId);
- });
- }
-
- }
-
- /**
- * #1 Read new, old Value (Attribute, Telemetry, Observe, KeyName)
- * #2 Update in lwM2MClient: ...Profile if changes from update device
- * #3 Equivalence test: old <> new Value (Attribute, Telemetry, Observe, KeyName)
- * #3.1 Attribute isChange (add&del)
- * #3.2 Telemetry isChange (add&del)
- * #3.3 KeyName isChange (add)
- * #3.4 attributeLwm2m isChange (update WrightAttribute: add/update/del)
- * #4 update
- * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and send Value to thingsboard
- * #4.2 del
- * -- if add attributes includes del telemetry - result del for observe
- * #5
- * #5.1 Observe isChange (add&del)
- * #5.2 Observe.add
- * -- path Attr/Telemetry includes newObserve and does not include oldObserve: send Request observe to Client
- * #5.3 Observe.del
- * -- different between newObserve and oldObserve: send Request cancel observe to client
- * #6
- * #6.1 - update WriteAttribute
- * #6.2 - del WriteAttribute
- *
- * @param registrationIds -
- * @param deviceProfile -
- */
- private void onDeviceProfileUpdate(Set registrationIds, DeviceProfile deviceProfile) {
- LwM2mClientProfile lwM2MClientProfileOld = clientContext.getProfiles().get(deviceProfile.getUuidId()).clone();
- if (clientContext.profileUpdate(deviceProfile) != null) {
- // #1
- JsonArray attributeOld = lwM2MClientProfileOld.getPostAttributeProfile();
- Set attributeSetOld = convertJsonArrayToSet(attributeOld);
- JsonArray telemetryOld = lwM2MClientProfileOld.getPostTelemetryProfile();
- Set telemetrySetOld = convertJsonArrayToSet(telemetryOld);
- JsonArray observeOld = lwM2MClientProfileOld.getPostObserveProfile();
- JsonObject keyNameOld = lwM2MClientProfileOld.getPostKeyNameProfile();
- JsonObject attributeLwm2mOld = lwM2MClientProfileOld.getPostAttributeLwm2mProfile();
-
- LwM2mClientProfile lwM2MClientProfileNew = clientContext.getProfiles().get(deviceProfile.getUuidId()).clone();
- JsonArray attributeNew = lwM2MClientProfileNew.getPostAttributeProfile();
- Set attributeSetNew = convertJsonArrayToSet(attributeNew);
- JsonArray telemetryNew = lwM2MClientProfileNew.getPostTelemetryProfile();
- Set telemetrySetNew = convertJsonArrayToSet(telemetryNew);
- JsonArray observeNew = lwM2MClientProfileNew.getPostObserveProfile();
- JsonObject keyNameNew = lwM2MClientProfileNew.getPostKeyNameProfile();
- JsonObject attributeLwm2mNew = lwM2MClientProfileNew.getPostAttributeLwm2mProfile();
-
- // #3
- ResultsAnalyzerParameters sendAttrToThingsboard = new ResultsAnalyzerParameters();
- // #3.1
- if (!attributeOld.equals(attributeNew)) {
- ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld,
- new TypeToken>() {
- }.getType()), attributeSetNew);
- sendAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd());
- sendAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel());
- }
- // #3.2
- if (!telemetryOld.equals(telemetryNew)) {
- ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld,
- new TypeToken>() {
- }.getType()), telemetrySetNew);
- sendAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd());
- sendAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel());
- }
- // #3.3
- if (!keyNameOld.equals(keyNameNew)) {
- ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(),
- new TypeToken>() {
- }.getType()),
- new Gson().fromJson(keyNameNew.toString(), new TypeToken>() {
- }.getType()));
- sendAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd());
- }
-
- // #3.4, #6
- if (!attributeLwm2mOld.equals(attributeLwm2mNew)) {
- this.getAnalyzerAttributeLwm2m(registrationIds, attributeLwm2mOld, attributeLwm2mNew);
- }
-
- // #4.1 add
- if (sendAttrToThingsboard.getPathPostParametersAdd().size() > 0) {
- // update value in Resources
- registrationIds.forEach(registrationId -> {
- Registration registration = clientContext.getRegistration(registrationId);
- this.readObserveFromProfile(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ);
- });
- }
- // #4.2 del
- if (sendAttrToThingsboard.getPathPostParametersDel().size() > 0) {
- ResultsAnalyzerParameters sendAttrToThingsboardDel = this.getAnalyzerParameters(sendAttrToThingsboard.getPathPostParametersAdd(), sendAttrToThingsboard.getPathPostParametersDel());
- sendAttrToThingsboard.setPathPostParametersDel(sendAttrToThingsboardDel.getPathPostParametersDel());
- }
-
- // #5.1
- if (!observeOld.equals(observeNew)) {
- Set observeSetOld = new Gson().fromJson(observeOld, new TypeToken>() {
- }.getType());
- Set observeSetNew = new Gson().fromJson(observeNew, new TypeToken>() {
- }.getType());
- //#5.2 add
- // path Attr/Telemetry includes newObserve
- attributeSetOld.addAll(telemetrySetOld);
- ResultsAnalyzerParameters sendObserveToClientOld = this.getAnalyzerParametersIn(attributeSetOld, observeSetOld); // add observe
- attributeSetNew.addAll(telemetrySetNew);
- ResultsAnalyzerParameters sendObserveToClientNew = this.getAnalyzerParametersIn(attributeSetNew, observeSetNew); // add observe
- // does not include oldObserve
- ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sendObserveToClientOld.getPathPostParametersAdd(), sendObserveToClientNew.getPathPostParametersAdd());
- // send Request observe to Client
- registrationIds.forEach(registrationId -> {
- Registration registration = clientContext.getRegistration(registrationId);
- if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) {
- this.readObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE);
- }
- // 5.3 del
- // send Request cancel observe to Client
- if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) {
- this.cancelObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersDel());
- }
- });
- }
- }
- }
-
- /**
- * Compare old list with new list after change AttrTelemetryObserve in config Profile
- *
- * @param parametersOld -
- * @param parametersNew -
- * @return ResultsAnalyzerParameters: add && new
- */
- private ResultsAnalyzerParameters getAnalyzerParameters(Set parametersOld, Set parametersNew) {
- ResultsAnalyzerParameters analyzerParameters = null;
- if (!parametersOld.equals(parametersNew)) {
- analyzerParameters = new ResultsAnalyzerParameters();
- analyzerParameters.setPathPostParametersAdd(parametersNew
- .stream().filter(p -> !parametersOld.contains(p)).collect(Collectors.toSet()));
- analyzerParameters.setPathPostParametersDel(parametersOld
- .stream().filter(p -> !parametersNew.contains(p)).collect(Collectors.toSet()));
- }
- return analyzerParameters;
- }
-
- private ResultsAnalyzerParameters getAnalyzerParametersIn(Set parametersObserve, Set parameters) {
- ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
- analyzerParameters.setPathPostParametersAdd(parametersObserve
- .stream().filter(parameters::contains).collect(Collectors.toSet()));
- return analyzerParameters;
- }
-
- /**
- * Update Resource value after change RezAttrTelemetry in config Profile
- * send response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue()
- *
- * @param registration - Registration LwM2M Client
- * @param targets - path Resources == [ "/2/0/0", "/2/0/1"]
- */
- private void readObserveFromProfile(Registration registration, Set targets, LwM2mTypeOper typeOper) {
- targets.forEach(target -> {
- LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target));
- if (pathIds.isResource()) {
- if (READ.equals(typeOper)) {
- lwM2mTransportRequest.sendAllRequest(registration, target, typeOper,
- ContentFormat.TLV.getName(), null, this.config.getTimeout(), null);
- } else if (OBSERVE.equals(typeOper)) {
- lwM2mTransportRequest.sendAllRequest(registration, target, typeOper,
- null, null, this.config.getTimeout(), null);
- }
- }
- });
- }
-
- private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentHashMap keyNameOld, ConcurrentHashMap keyNameNew) {
- ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
- Set paths = keyNameNew.entrySet()
- .stream()
- .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey())))
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet();
- analyzerParameters.setPathPostParametersAdd(paths);
- return analyzerParameters;
- }
-
- /**
- * #3.4, #6
- * #6
- * #6.1 - send update WriteAttribute
- * #6.2 - send empty WriteAttribute
- *
- * @param attributeLwm2mOld -
- * @param attributeLwm2mNew -
- * @return
- */
- private void getAnalyzerAttributeLwm2m(Set registrationIds, JsonObject attributeLwm2mOld, JsonObject attributeLwm2mNew) {
- ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
- ConcurrentHashMap lwm2mAttributesOld = new Gson().fromJson(attributeLwm2mOld.toString(),
- new TypeToken>() {
- }.getType());
- ConcurrentHashMap lwm2mAttributesNew = new Gson().fromJson(attributeLwm2mNew.toString(),
- new TypeToken>() {
- }.getType());
- Set pathOld = lwm2mAttributesOld.keySet();
- Set pathNew = lwm2mAttributesNew.keySet();
- analyzerParameters.setPathPostParametersAdd(pathNew
- .stream().filter(p -> !pathOld.contains(p)).collect(Collectors.toSet()));
- analyzerParameters.setPathPostParametersDel(pathOld
- .stream().filter(p -> !pathNew.contains(p)).collect(Collectors.toSet()));
- Set pathCommon = pathNew
- .stream().filter(p -> pathOld.contains(p)).collect(Collectors.toSet());
- Set pathCommonChange = pathCommon
- .stream().filter(p -> !lwm2mAttributesOld.get(p).equals(lwm2mAttributesNew.get(p))).collect(Collectors.toSet());
- analyzerParameters.getPathPostParametersAdd().addAll(pathCommonChange);
- // #6
- // #6.2
- if (analyzerParameters.getPathPostParametersAdd().size() > 0) {
- registrationIds.forEach(registrationId -> {
- Registration registration = this.clientContext.getRegistration(registrationId);
- Set clientObjects = clientContext.getSupportedIdVerInClient(registration);
- Set pathSend = analyzerParameters.getPathPostParametersAdd().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]))
- .collect(Collectors.toUnmodifiableSet());
- if (!pathSend.isEmpty()) {
- ConcurrentHashMap finalParams = lwm2mAttributesNew;
- pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(),
- finalParams.get(target), this.config.getTimeout(), null));
- }
- });
- }
- // #6.2
- if (analyzerParameters.getPathPostParametersDel().size() > 0) {
- registrationIds.forEach(registrationId -> {
- Registration registration = this.clientContext.getRegistration(registrationId);
- Set clientObjects = clientContext.getSupportedIdVerInClient(registration);
- Set pathSend = analyzerParameters.getPathPostParametersDel().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]))
- .collect(Collectors.toUnmodifiableSet());
- if (!pathSend.isEmpty()) {
- pathSend.forEach(target -> {
- Map params = (Map) lwm2mAttributesOld.get(target);
- params.clear();
- params.put(OBJECT_VERSION, "");
- lwM2mTransportRequest.sendAllRequest(registration, target, WRITE_ATTRIBUTES, ContentFormat.TLV.getName(),
- params, this.config.getTimeout(), null);
- });
- }
- });
- }
-
- }
-
- private void cancelObserveFromProfile(Registration registration, Set paramAnallyzer) {
- LwM2mClient lwM2MClient = clientContext.getOrRegister(registration);
- paramAnallyzer.forEach(pathIdVer -> {
- if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) {
- lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, OBSERVE_CANCEL, null,
- null, this.config.getTimeout(), null);
- }
- }
- );
- }
-
- private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object valueNew, String path) {
- if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) {
- lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), path, WRITE_REPLACE,
- ContentFormat.TLV.getName(), valueNew,
- this.config.getTimeout(), null);
- } else {
- 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().getId());
- log.info("Failed update resource [{}] [{}]", path, valueNew);
- }
- }
-
- /**
- * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...)
- * config attr/telemetry... in profile
- */
- public void onToTransportUpdateCredentials(TransportProtos.ToTransportUpdateCredentialsProto updateCredentials) {
- log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList());
- }
-
- /**
- * Get path to resource from profile equal keyName
- *
- * @param sessionInfo -
- * @param name -
- * @return -
- */
- public String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
- LwM2mClientProfile profile = clientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
- LwM2mClient lwM2mClient = clientContext.getClient(sessionInfo);
- return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
- .filter(e -> e.getValue().getAsString().equals(name) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().map(Map.Entry::getKey)
- .orElse(null);
- }
-
- /**
- * 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 -
- */
- public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
- try {
- List tsKvProtos = attributesResponse.getSharedAttributeListList();
- this.updateAttributeFromThingsboard(tsKvProtos, sessionInfo);
- } catch (Exception e) {
- log.error("", e);
- }
- }
-
- /**
- * #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
- * @param sessionInfo
- */
- public void updateAttributeFromThingsboard(List tsKvProtos, TransportProtos.SessionInfoProto sessionInfo) {
- LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo);
- if (lwM2MClient != null) {
- log.warn("1) UpdateAttributeFromThingsboard, tsKvProtos [{}]", tsKvProtos);
- tsKvProtos.forEach(tsKvProto -> {
- String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, tsKvProto.getKv().getKey());
- if (pathIdVer != null) {
- // #1.1
- if (lwM2MClient.getDelayedRequests().containsKey(pathIdVer) && tsKvProto.getTs() > lwM2MClient.getDelayedRequests().get(pathIdVer).getTs()) {
- lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto);
- } else if (!lwM2MClient.getDelayedRequests().containsKey(pathIdVer)) {
- lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto);
- }
- }
- });
- // #2.1
- lwM2MClient.getDelayedRequests().forEach((pathIdVer, tsKvProto) -> {
- this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer),
- getValueFromKvProto(tsKvProto.getKv()), pathIdVer);
- });
- }
- else {
- log.error("UpdateAttributeFromThingsboard, lwM2MClient is null");
- }
- }
-
- /**
- * @param lwM2MClient -
- * @return SessionInfoProto -
- */
- private SessionInfoProto getSessionInfoOrCloseSession(LwM2mClient lwM2MClient) {
- if (lwM2MClient != null) {
- SessionInfoProto sessionInfoProto = lwM2MClient.getSession();
- if (sessionInfoProto == null) {
- log.info("[{}] [{}]", lwM2MClient.getEndpoint(), CLIENT_NOT_AUTHORIZED);
- this.closeClientSession(lwM2MClient.getRegistration());
- }
- return sessionInfoProto;
- }
- return null;
- }
-
- /**
- * @param registration - Registration LwM2M Client
- * @return - sessionInfo after access connect client
- */
- public SessionInfoProto getSessionInfoOrCloseSession(Registration registration) {
- return getSessionInfoOrCloseSession(clientContext.getOrRegister(registration));
- }
-
- /**
- * @param registrationId -
- * @return -
- */
- private SessionInfoProto getSessionInfoOrCloseSession(String registrationId) {
- return getSessionInfoOrCloseSession(clientContext.getClientByRegistrationId(registrationId));
- }
-
- /**
- * if sessionInfo removed from sessions, then new registerAsyncSession
- *
- * @param sessionInfo -
- */
- private void reportActivityAndRegister(SessionInfoProto sessionInfo) {
- if (sessionInfo != null && transportService.reportActivity(sessionInfo) == null) {
- transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo));
- this.reportActivitySubscription(sessionInfo);
- }
- }
-
- private void reportActivity() {
- clientContext.getLwM2mClients().forEach(client -> reportActivityAndRegister(client.getSession()));
- }
-
- /**
- * #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.getSessionInfoOrCloseSession(lwM2MClient);
- if (sessionInfo != null) {
- //#1.1
- ConcurrentMap keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient);
- if (keyNamesMap.values().size() > 0) {
- try {
- //#1.2
- TransportProtos.GetAttributeRequestMsg getAttributeMsg = adaptor.convertToGetAttributes(null, keyNamesMap.values());
- transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
- } catch (AdaptorException e) {
- log.trace("Failed to decode get attributes request", e);
- }
- }
-
- }
- }
-
- public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) {
- if (lwM2MClient.getRegistration().getSupportedVersion(FW_ID) != null) {
- SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient);
- if (sessionInfo != null) {
- DefaultLwM2MTransportMsgHandler handler = this;
- this.transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, 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())) {
- log.warn ("7) firmware start with ver: [{}]", response.getVersion());
- lwM2MClient.getFwUpdate().setRpcRequest(rpcRequest);
- lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion());
- lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle());
- lwM2MClient.getFwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId());
- if (rpcRequest == null) {
- lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
- }
- else {
- lwM2MClient.getFwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
- }
- } else {
- log.trace("OtaPackage [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
- }
- }
-
- @Override
- public void onError(Throwable e) {
- log.trace("Failed to process firmwareUpdate ", e);
- }
- });
- }
- }
- }
-
- public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) {
- if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) {
- SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient);
- if (sessionInfo != null) {
- DefaultLwM2MTransportMsgHandler handler = this;
- transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.SOFTWARE.name()),
- new TransportServiceCallback<>() {
- @Override
- public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) {
- if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())
- && response.getType().equals(OtaPackageType.SOFTWARE.name())) {
- lwM2MClient.getSwUpdate().setRpcRequest(rpcRequest);
- lwM2MClient.getSwUpdate().setCurrentVersion(response.getVersion());
- lwM2MClient.getSwUpdate().setCurrentTitle(response.getTitle());
- lwM2MClient.getSwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId());
- lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
- if (rpcRequest == null) {
- lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
- }
- else {
- lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
- }
- } else {
- log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
- }
- }
-
- @Override
- public void onError(Throwable e) {
- log.trace("Failed to process softwareUpdate ", e);
- }
- });
- }
- }
- }
-
- private TransportProtos.GetOtaPackageRequestMsg createOtaPackageRequestMsg(SessionInfoProto sessionInfo, String nameFwSW) {
- return TransportProtos.GetOtaPackageRequestMsg.newBuilder()
- .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
- .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
- .setTenantIdMSB(sessionInfo.getTenantIdMSB())
- .setTenantIdLSB(sessionInfo.getTenantIdLSB())
- .setType(nameFwSW)
- .build();
- }
-
- /**
- * !!! sharedAttr === profileAttr !!!
- * Get names or keyNames from profile: resources IsWritable
- *
- * @param lwM2MClient -
- * @return ArrayList keyNames from profile profileAttr && IsWritable
- */
- private ConcurrentMap getNamesFromProfileForSharedAttributes(LwM2mClient lwM2MClient) {
-
- LwM2mClientProfile profile = clientContext.getProfile(lwM2MClient.getProfileId());
- return new Gson().fromJson(profile.getPostKeyNameProfile().toString(),
- new TypeToken>() {
- }.getType());
- }
-
- private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) {
- ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config
- .getModelProvider());
- Integer objectId = new LwM2mPath(convertPathFromIdVerToObjectId(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;
- }
-
- private void reportActivitySubscription(TransportProtos.SessionInfoProto sessionInfo) {
- transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
- .setAttributeSubscription(true)
- .setRpcSubscription(true)
- .setLastActivityTime(System.currentTimeMillis())
- .build(), TransportServiceCallback.EMPTY);
- }
-}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
index 90e3e13033..9425ef7891 100644
--- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
@@ -26,9 +26,8 @@ import org.eclipse.leshan.server.californium.LeshanServer;
import org.eclipse.leshan.server.californium.LeshanServerBuilder;
import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
import org.eclipse.leshan.server.model.LwM2mModelProvider;
-import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.springframework.stereotype.Component;
-import org.thingsboard.common.util.ThingsBoardThreadFactory;
+import org.thingsboard.server.cache.ota.OtaPackageDataCache;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
@@ -36,6 +35,8 @@ import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer;
import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
+import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore;
+import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import javax.annotation.PostConstruct;
@@ -58,14 +59,13 @@ import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256;
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.LwM2mNetworkConfig.getCoapConfig;
+import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE;
@Slf4j
@Component
@@ -81,9 +81,10 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
private final LwM2mTransportContext context;
private final LwM2MTransportServerConfig config;
private final LwM2mTransportServerHelper helper;
- private final LwM2mTransportMsgHandler handler;
+ private final OtaPackageDataCache otaPackageDataCache;
+ private final DefaultLwM2MUplinkMsgHandler handler;
private final CaliforniumRegistrationStore registrationStore;
- private final EditableSecurityStore securityStore;
+ private final TbSecurityStore securityStore;
private final LwM2mClientContext lwM2mClientContext;
private final TbLwM2MDtlsCertificateVerifier certificateVerifier;
private final TbLwM2MAuthorizer authorizer;
@@ -96,6 +97,16 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
new LWM2MGenerationPSkRPkECC();
}
this.server = getLhServer();
+ /**
+ * Add a resource to the server.
+ * CoapResource ->
+ * path = FW_PACKAGE or SW_PACKAGE
+ * nameFile = "BC68JAR01A09_TO_BC68JAR01A10.bin"
+ * "coap://host:port/{path}/{token}/{nameFile}"
+ */
+
+ LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RECOURSE);
+ this.server.coap().getServer().add(otaCoapResource);
this.startLhServer();
this.context.setServer(server);
}
@@ -126,7 +137,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
/* Create CoAP Config */
- builder.setCoapConfig(getCoapConfig(config.getPort(), config.getSecurePort()));
+ builder.setCoapConfig(getCoapConfig(config.getPort(), config.getSecurePort(), config));
/* Define model provider (Create Models )*/
LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.helper, this.context);
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/LwM2mNetworkConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java
index 280cb7dde4..4fc60aecfc 100644
--- 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
@@ -16,10 +16,14 @@
package org.thingsboard.server.transport.lwm2m.server;
import org.eclipse.californium.core.network.config.NetworkConfig;
+import org.eclipse.californium.core.network.config.NetworkConfigDefaults;
+import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
+
+import static org.eclipse.californium.core.network.config.NetworkConfigDefaults.DEFAULT_BLOCKWISE_STATUS_LIFETIME;
public class LwM2mNetworkConfig {
- public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) {
+ public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort, LwM2MTransportServerConfig config) {
NetworkConfig coapConfig = new NetworkConfig();
coapConfig.setInt(NetworkConfig.Keys.COAP_PORT,serverPortNoSec);
coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT,serverSecurePort);
@@ -49,8 +53,15 @@ public class LwM2mNetworkConfig {
- 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);
+ /**
+ * The maximum amount of time (in milliseconds) allowed between
+ * transfers of individual blocks in a blockwise transfer before the
+ * blockwise transfer state is discarded.
+ *
+ * The default value of this property is
+ * {@link NetworkConfigDefaults#DEFAULT_BLOCKWISE_STATUS_LIFETIME} = 5 * 60 * 1000; // 5 mins [ms].
+ */
+ coapConfig.setLong(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME);
/**
!!! REQUEST_ENTITY_TOO_LARGE CODE=4.13
The maximum size of a resource body (in bytes) that will be accepted
@@ -73,10 +84,10 @@ public class LwM2mNetworkConfig {
Create new instance of udp endpoint context matcher.
Params:
checkAddress
- – true with address check, (STRICT, UDP)
+ – true with address check, (STRICT, UDP) - if port Registration of client is changed - it is bad
- false, without
*/
- coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "STRICT");
+ coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "RELAXED");
/**
https://tools.ietf.org/html/rfc7959#section-2.9.3
The block size (number of bytes) to use when doing a blockwise transfer. \
@@ -92,7 +103,7 @@ public class LwM2mNetworkConfig {
*/
coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024);
- coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4);
+ coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 10);
return coapConfig;
}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java
new file mode 100644
index 0000000000..f9b3f93854
--- /dev/null
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java
@@ -0,0 +1,79 @@
+/**
+ * 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 lombok.Getter;
+
+/**
+ * Define the behavior of a write request.
+ */
+public enum LwM2mOperationType {
+
+ READ(0, "Read", true),
+ DISCOVER(1, "Discover", true),
+ DISCOVER_ALL(2, "DiscoverAll", false),
+ OBSERVE_READ_ALL(3, "ObserveReadAll", false),
+
+ OBSERVE(4, "Observe", true),
+ OBSERVE_CANCEL(5, "ObserveCancel", true),
+ OBSERVE_CANCEL_ALL(6, "ObserveCancelAll", false),
+ EXECUTE(7, "Execute", true),
+ /**
+ * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see
+ * section 5.3.3 of the LW M2M spec).
+ * if all resources are to be replaced
+ */
+ WRITE_REPLACE(8, "WriteReplace", true),
+
+ /**
+ * Adds or updates Resources provided in the new value and leaves other existing Resources unchanged. (see section
+ * 5.3.3 of the LW M2M spec).
+ * if this is a partial update request
+ */
+ WRITE_UPDATE(9, "WriteUpdate", true),
+ WRITE_ATTRIBUTES(10, "WriteAttributes", true),
+ DELETE(11, "Delete", true),
+
+ // only for RPC
+ FW_UPDATE(12, "FirmwareUpdate", false);
+
+// FW_READ_INFO(12, "FirmwareReadInfo"),
+// SW_READ_INFO(15, "SoftwareReadInfo"),
+// SW_UPDATE(16, "SoftwareUpdate"),
+// SW_UNINSTALL(18, "SoftwareUninstall");
+
+ @Getter
+ private final int code;
+ @Getter
+ private final String type;
+ @Getter
+ private final boolean hasObjectId;
+
+ LwM2mOperationType(int code, String type, boolean hasObjectId) {
+ this.code = code;
+ this.type = type;
+ this.hasObjectId = hasObjectId;
+ }
+
+ public static LwM2mOperationType fromType(String type) {
+ for (LwM2mOperationType to : LwM2mOperationType.values()) {
+ if (to.type.equals(type)) {
+ return to;
+ }
+ }
+ return null;
+ }
+}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOtaConvert.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOtaConvert.java
new file mode 100644
index 0000000000..7b7c56adb3
--- /dev/null
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOtaConvert.java
@@ -0,0 +1,25 @@
+/**
+ * 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 lombok.Data;
+import org.eclipse.leshan.core.model.ResourceModel;
+
+@Data
+public class LwM2mOtaConvert {
+ private ResourceModel.Type currentType;
+ private Object value;
+}
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 f0e11aceb4..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
@@ -23,18 +23,18 @@ import org.eclipse.leshan.server.queue.PresenceListener;
import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.registration.RegistrationListener;
import org.eclipse.leshan.server.registration.RegistrationUpdate;
+import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
import java.util.Collection;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_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 {
- private final LwM2mTransportMsgHandler service;
+ private final LwM2mUplinkMsgHandler service;
- public LwM2mServerListener(LwM2mTransportMsgHandler service) {
+ public LwM2mServerListener(LwM2mUplinkMsgHandler service) {
this.service = service;
}
@@ -86,30 +86,24 @@ public class LwM2mServerListener {
@Override
public void cancelled(Observation observation) {
- String msg = String.format("%s: Canceled Observation %s.", LOG_LW2M_INFO, observation.getPath());
- service.sendLogsToThingsboard(msg, observation.getRegistrationId());
- log.warn(msg);
+ log.trace("Canceled Observation {}.", observation.getPath());
}
@Override
public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
if (registration != null) {
- service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
- registration), response, null);
+ service.onUpdateValueAfterReadResponse(registration, convertObjectIdToVersionedId(observation.getPath().toString(), registration), response);
}
}
@Override
public void onError(Observation observation, Registration registration, Exception error) {
- log.error(String.format("Unable to handle notification of [%s:%s]", observation.getRegistrationId(), observation.getPath()), error);
+ log.error("Unable to handle notification of [{}:{}]", observation.getRegistrationId(), observation.getPath(), error);
}
@Override
public void newObservation(Observation observation, Registration registration) {
- String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO,
- observation.getPath());
- log.warn(msg);
- service.sendLogsToThingsboard(msg, registration.getId());
+ log.trace("Successful start newObservation {}.", observation.getPath());
}
};
}
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 b71de7db1b..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
@@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.thingsboard.server.common.data.Device;
@@ -30,28 +31,29 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotifica
import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
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.LwM2mUplinkMsgHandler;
import java.util.Optional;
import java.util.UUID;
@Slf4j
+@RequiredArgsConstructor
public class LwM2mSessionMsgListener implements GenericFutureListener>, SessionMsgListener {
- private DefaultLwM2MTransportMsgHandler handler;
- private TransportProtos.SessionInfoProto sessionInfo;
-
- public LwM2mSessionMsgListener(DefaultLwM2MTransportMsgHandler handler, TransportProtos.SessionInfoProto sessionInfo) {
- this.handler = handler;
- this.sessionInfo = sessionInfo;
- }
+ private final LwM2mUplinkMsgHandler handler;
+ private final LwM2MAttributesService attributesService;
+ private final LwM2MRpcRequestHandler rpcHandler;
+ private final TransportProtos.SessionInfoProto sessionInfo;
@Override
public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) {
- this.handler.onGetAttributesResponse(getAttributesResponse, this.sessionInfo);
+ this.attributesService.onGetAttributesResponse(getAttributesResponse, this.sessionInfo);
}
@Override
public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) {
- this.handler.onAttributeUpdate(attributeUpdateNotification, this.sessionInfo);
+ this.attributesService.onAttributesUpdate(attributeUpdateNotification, this.sessionInfo);
}
@Override
@@ -76,12 +78,12 @@ public class LwM2mSessionMsgListener implements GenericFutureListener tokenToObserveRelationMap = new ConcurrentHashMap<>();
+ private final ConcurrentMap tokenToObserveNotificationSeqMap = new ConcurrentHashMap<>();
+ private final OtaPackageDataCache otaPackageDataCache;
+
+ public LwM2mTransportCoapResource(OtaPackageDataCache otaPackageDataCache, String name) {
+ super(name);
+ this.otaPackageDataCache = otaPackageDataCache;
+ this.setObservable(true); // enable observing
+ this.addObserver(new CoapResourceObserver());
+ }
+
+
+ @Override
+ public void checkObserveRelation(Exchange exchange, Response response) {
+ String token = getTokenFromRequest(exchange.getRequest());
+ final ObserveRelation relation = exchange.getRelation();
+ if (relation == null || relation.isCanceled()) {
+ return; // because request did not try to establish a relation
+ }
+ if (CoAP.ResponseCode.isSuccess(response.getCode())) {
+
+ if (!relation.isEstablished()) {
+ relation.setEstablished();
+ addObserveRelation(relation);
+ }
+ AtomicInteger notificationCounter = tokenToObserveNotificationSeqMap.computeIfAbsent(token, s -> new AtomicInteger(0));
+ response.getOptions().setObserve(notificationCounter.getAndIncrement());
+ } // ObserveLayer takes care of the else case
+ }
+
+
+ @Override
+ protected void processHandleGet(CoapExchange exchange) {
+ log.warn("90) processHandleGet [{}]", exchange);
+ if (exchange.getRequestOptions().getUriPath().size() >= 2 &&
+ (FIRMWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)) ||
+ SOFTWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)))) {
+ this.sendOtaData(exchange);
+ }
+ }
+
+ @Override
+ protected void processHandlePost(CoapExchange exchange) {
+ log.warn("2) processHandleGet [{}]", exchange);
+ }
+
+ /**
+ * Override the default behavior so that requests to sub resources (typically /{name}/{token}) are handled by
+ * /name resource.
+ */
+ @Override
+ public Resource getChild(String name) {
+ return this;
+ }
+
+
+ private String getTokenFromRequest(Request request) {
+ return (request.getSourceContext() != null ? request.getSourceContext().getPeerAddress().getAddress().getHostAddress() : "null")
+ + ":" + (request.getSourceContext() != null ? request.getSourceContext().getPeerAddress().getPort() : -1) + ":" + request.getTokenString();
+ }
+
+ public class CoapResourceObserver implements ResourceObserver {
+
+ @Override
+ public void changedName(String old) {
+
+ }
+
+ @Override
+ public void changedPath(String old) {
+
+ }
+
+ @Override
+ public void addedChild(Resource child) {
+
+ }
+
+ @Override
+ public void removedChild(Resource child) {
+
+ }
+
+ @Override
+ public void addedObserveRelation(ObserveRelation relation) {
+
+ }
+
+ @Override
+ public void removedObserveRelation(ObserveRelation relation) {
+
+ }
+ }
+
+ private void sendOtaData(CoapExchange exchange) {
+ String idStr = exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-1
+ );
+ UUID currentId = UUID.fromString(idStr);
+ Response response = new Response(CoAP.ResponseCode.CONTENT);
+ byte[] fwData = this.getOtaData(currentId);
+ log.warn("91) read softWare data (length): [{}]", fwData.length);
+ if (fwData != null && fwData.length > 0) {
+ response.setPayload(fwData);
+ if (exchange.getRequestOptions().getBlock2() != null) {
+ int chunkSize = exchange.getRequestOptions().getBlock2().getSzx();
+ boolean lastFlag = fwData.length > chunkSize;
+ response.getOptions().setBlock2(chunkSize, lastFlag, 0);
+ log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, lastFlag);
+ }
+ else {
+ log.warn("92) with block1 Send currentId: [{}], length: [{}], ", currentId.toString(), fwData.length);
+ }
+ exchange.respond(response);
+ }
+ }
+
+ private byte[] getOtaData(UUID currentId) {
+ return otaPackageDataCache.get(currentId.toString());
+ }
+
+}
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
deleted file mode 100644
index 4450859e3e..0000000000
--- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
+++ /dev/null
@@ -1,614 +0,0 @@
-/**
- * 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 lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.eclipse.californium.core.coap.CoAP;
-import org.eclipse.californium.core.coap.Response;
-import org.eclipse.leshan.core.Link;
-import org.eclipse.leshan.core.model.ResourceModel;
-import org.eclipse.leshan.core.node.LwM2mNode;
-import org.eclipse.leshan.core.node.LwM2mObject;
-import org.eclipse.leshan.core.node.LwM2mObjectInstance;
-import org.eclipse.leshan.core.node.LwM2mPath;
-import org.eclipse.leshan.core.node.LwM2mResource;
-import org.eclipse.leshan.core.node.LwM2mSingleResource;
-import org.eclipse.leshan.core.node.ObjectLink;
-import org.eclipse.leshan.core.observation.Observation;
-import org.eclipse.leshan.core.request.ContentFormat;
-import org.eclipse.leshan.core.request.DeleteRequest;
-import org.eclipse.leshan.core.request.DiscoverRequest;
-import org.eclipse.leshan.core.request.DownlinkRequest;
-import org.eclipse.leshan.core.request.ExecuteRequest;
-import org.eclipse.leshan.core.request.ObserveRequest;
-import org.eclipse.leshan.core.request.ReadRequest;
-import org.eclipse.leshan.core.request.WriteAttributesRequest;
-import org.eclipse.leshan.core.request.WriteRequest;
-import org.eclipse.leshan.core.request.exception.ClientSleepingException;
-import org.eclipse.leshan.core.response.DeleteResponse;
-import org.eclipse.leshan.core.response.DiscoverResponse;
-import org.eclipse.leshan.core.response.ExecuteResponse;
-import org.eclipse.leshan.core.response.LwM2mResponse;
-import org.eclipse.leshan.core.response.ReadResponse;
-import org.eclipse.leshan.core.response.ResponseCallback;
-import org.eclipse.leshan.core.response.WriteAttributesResponse;
-import org.eclipse.leshan.core.response.WriteResponse;
-import org.eclipse.leshan.core.util.Hex;
-import org.eclipse.leshan.core.util.NamedThreadFactory;
-import org.eclipse.leshan.server.registration.Registration;
-import org.springframework.stereotype.Service;
-import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
-import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
-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.Lwm2mClientRpcRequest;
-import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
-
-import javax.annotation.PostConstruct;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.stream.Collectors;
-
-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.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED;
-import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getContentFormatByResourceModelType;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEFAULT_TIMEOUT;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESPONSE_REQUEST_CHANNEL;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_PACKAGE_ID;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.createWriteAttributeRequest;
-
-@Slf4j
-@Service
-@TbLwM2mTransportComponent
-@RequiredArgsConstructor
-public class LwM2mTransportRequest {
- private ExecutorService responseRequestExecutor;
-
- public LwM2mValueConverterImpl converter;
-
- private final LwM2mTransportContext context;
- private final LwM2MTransportServerConfig config;
- private final LwM2mClientContext lwM2mClientContext;
- private final DefaultLwM2MTransportMsgHandler handler;
-
- @PostConstruct
- public void init() {
- this.converter = LwM2mValueConverterImpl.getInstance();
- responseRequestExecutor = Executors.newFixedThreadPool(this.config.getResponsePoolSize(),
- new NamedThreadFactory(String.format("LwM2M %s channel response after request", RESPONSE_REQUEST_CHANNEL)));
- }
-
- /**
- * Device management and service enablement, including Read, Write, Execute, Discover, Create, Delete and Write-Attributes
- *
- * @param registration -
- * @param targetIdVer -
- * @param typeOper -
- * @param contentFormatName -
- */
-
- public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper,
- String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest lwm2mClientRpcRequest) {
- try {
- String target = convertPathFromIdVerToObjectId(targetIdVer);
- ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT;
- LwM2mClient lwM2MClient = this.lwM2mClientContext.getOrRegister(registration);
- LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null;
- if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) {
- if (lwM2MClient.isValidObjectVersion(targetIdVer)) {
- timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
- DownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target,
- targetIdVer, resultIds, params, lwm2mClientRpcRequest);
- if (request != null) {
- try {
- this.sendRequest(registration, lwM2MClient, request, timeoutInMs, lwm2mClientRpcRequest);
- } catch (ClientSleepingException e) {
- DownlinkRequest finalRequest = request;
- long finalTimeoutInMs = timeoutInMs;
- Lwm2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest;
- lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest));
- } catch (Exception e) {
- log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e);
- }
- } else if (WRITE_UPDATE.name().equals(typeOper.name())) {
- if (lwm2mClientRpcRequest != null) {
- String errorMsg = String.format("Path %s params is not valid", targetIdVer);
- handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
- }
- } else if (WRITE_REPLACE.name().equals(typeOper.name()) || EXECUTE.name().equals(typeOper.name())) {
- if (lwm2mClientRpcRequest != null) {
- String errorMsg = String.format("Path %s object model is absent", targetIdVer);
- handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
- }
- } else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) {
- log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
- if (lwm2mClientRpcRequest != null) {
- ResourceModel resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
- String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null";
- this.handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
- }
- }
- } else if (lwm2mClientRpcRequest != null) {
- String errorMsg = String.format("Path %s not found in object version", targetIdVer);
- this.handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
- }
- } else {
- switch (typeOper) {
- case OBSERVE_READ_ALL:
- case DISCOVER_ALL:
- Set paths;
- if (OBSERVE_READ_ALL.name().equals(typeOper.name())) {
- Set observations = context.getServer().getObservationService().getObservations(registration);
- paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet());
- } else {
- assert registration != null;
- Link[] objectLinks = registration.getSortedObjectLinks();
- paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet());
- }
- String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO,
- typeOper.name(), paths);
- this.handler.sendLogsToThingsboard(msg, registration.getId());
- if (lwm2mClientRpcRequest != null) {
- String valueMsg = String.format("Paths - %s", paths);
- this.handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
- }
- break;
- case OBSERVE_CANCEL:
- case OBSERVE_CANCEL_ALL:
- int observeCancelCnt = 0;
- String observeCancelMsg = null;
- if (OBSERVE_CANCEL.name().equals(typeOper)) {
- observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration, target);
- observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO,
- OBSERVE_CANCEL.name(), target, observeCancelCnt);
- } else {
- observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration);
- observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO,
- OBSERVE_CANCEL.name(), observeCancelCnt);
- }
- this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest);
- break;
- // lwm2mClientRpcRequest != null
- case FW_UPDATE:
- this.handler.getInfoFirmwareUpdate(lwM2MClient, lwm2mClientRpcRequest);
- break;
- }
- }
- } catch (Exception e) {
- String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
- typeOper.name(), e.getMessage());
- handler.sendLogsToThingsboard(msg, registration.getId());
- if (lwm2mClientRpcRequest != null) {
- String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage());
- handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
- }
- }
- }
-
- private DownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper,
- ContentFormat contentFormat, String target, String targetIdVer,
- LwM2mPath resultIds, Object params, Lwm2mClientRpcRequest rpcRequest) {
- DownlinkRequest request = null;
- switch (typeOper) {
- case READ:
- request = new ReadRequest(contentFormat, target);
- break;
- case DISCOVER:
- request = new DiscoverRequest(target);
- break;
- case OBSERVE:
- String msg = String.format("%s: Send Observation %s.", LOG_LW2M_INFO, targetIdVer);
- log.warn(msg);
- if (resultIds.isResource()) {
- Set observations = context.getServer().getObservationService().getObservations(registration);
- Set paths = observations.stream().filter(observation -> observation.getPath().equals(resultIds)).collect(Collectors.toSet());
- if (paths.size() == 0) {
- request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId());
- } else {
- request = new ReadRequest(contentFormat, target);
- }
- } else if (resultIds.isObjectInstance()) {
- request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId());
- } else if (resultIds.getObjectId() >= 0) {
- request = new ObserveRequest(contentFormat, resultIds.getObjectId());
- }
- break;
- case EXECUTE:
- ResourceModel resourceModelExecute = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
- if (resourceModelExecute != null) {
- if (params != null && !resourceModelExecute.multiple) {
- request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModelExecute.type, ResourceModel.Type.STRING, resultIds));
- } else {
- request = new ExecuteRequest(target);
- }
- }
- break;
- case WRITE_REPLACE:
- /**
- * Request to write a String Single-Instance Resource using the TLV content format.
- * Type from resourceModel -> STRING, INTEGER, FLOAT, BOOLEAN, OPAQUE, TIME, OBJLNK
- * contentFormat -> TLV, TLV, TLV, TLV, OPAQUE, TLV, LINK
- * JSON, TEXT;
- **/
- ResourceModel resourceModelWrite = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
- if (resourceModelWrite != null) {
- contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat);
- request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
- resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type,
- registration, rpcRequest);
- }
- break;
- case WRITE_UPDATE:
- if (resultIds.isResource()) {
- /**
- * send request: path = '/3/0' node == wM2mObjectInstance
- * with params == "\"resources\": {15: resource:{id:15. value:'+01'...}}
- **/
- Collection resources = lwM2MClient.getNewResourceForInstance(
- targetIdVer, params,
- this.config.getModelProvider(),
- this.converter);
- contentFormat = getContentFormatByResourceModelType(lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()),
- contentFormat);
- request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(),
- resultIds.getObjectInstanceId(), resources);
- }
- /**
- * params = "{\"id\":0,\"resources\":[{\"id\":14,\"value\":\"+5\"},{\"id\":15,\"value\":\"+9\"}]}"
- * int rscId = resultIds.getObjectInstanceId();
- * contentFormat – Format of the payload (TLV or JSON).
- */
- else if (resultIds.isObjectInstance()) {
- if (((ConcurrentHashMap) params).size() > 0) {
- Collection resources = lwM2MClient.getNewResourcesForInstance(
- targetIdVer, params,
- this.config.getModelProvider(),
- this.converter);
- if (resources.size() > 0) {
- contentFormat = contentFormat.equals(ContentFormat.JSON) ? contentFormat : ContentFormat.TLV;
- request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(),
- resultIds.getObjectInstanceId(), resources);
- }
- }
- } else if (resultIds.getObjectId() >= 0) {
- request = new ObserveRequest(resultIds.getObjectId());
- }
- break;
- case WRITE_ATTRIBUTES:
- request = createWriteAttributeRequest(target, params, this.handler);
- break;
- case DELETE:
- request = new DeleteRequest(target);
- break;
- }
- return request;
- }
-
- /**
- * @param registration -
- * @param request -
- * @param timeoutInMs -
- */
-
- @SuppressWarnings({"error sendRequest"})
- private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request,
- long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
- context.getServer().send(registration, request, timeoutInMs, (ResponseCallback>) response -> {
-
- if (!lwM2MClient.isInit()) {
- lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
- }
- if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) {
- this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest);
- } else {
- 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());
- handler.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.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
- }
- /** Not Found */
- if (rpcRequest != null) {
- handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR);
- }
- /** Not Found
- set setClient_fw_info... = empty
- **/
- if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
- lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString());
- }
- if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
- lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString());
- }
- if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
- this.afterWriteFwSWUpdateError(registration, request, response.getErrorMessage());
- }
- if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) {
- this.afterExecuteFwSwUpdateError(registration, request, response.getErrorMessage());
- }
- }
- }, e -> {
- /** version == null
- set setClient_fw_info... = empty
- **/
- if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
- lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString());
- }
- if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
- lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString());
- }
- if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
- this.afterWriteFwSWUpdateError(registration, request, e.getMessage());
- }
- if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) {
- this.afterExecuteFwSwUpdateError(registration, request, e.getMessage());
- }
- if (!lwM2MClient.isInit()) {
- lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
- }
- 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());
- handler.sendLogsToThingsboard(msg, registration.getId());
- log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString());
- if (rpcRequest != null) {
- handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
- }
- });
- }
-
- private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId,
- Integer resourceId, Object value, ResourceModel.Type type,
- Registration registration, Lwm2mClientRpcRequest rpcRequest) {
- try {
- if (type != null) {
- switch (type) {
- case STRING: // String
- return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, value.toString()) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, value.toString());
- case INTEGER: // Long
- final long valueInt = Integer.toUnsignedLong(Integer.parseInt(value.toString()));
- return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, valueInt) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueInt);
- case OBJLNK: // ObjectLink
- return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, ObjectLink.fromPath(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, ObjectLink.fromPath(value.toString()));
- case BOOLEAN: // Boolean
- return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Boolean.parseBoolean(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Boolean.parseBoolean(value.toString()));
- case FLOAT: // Double
- return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Double.parseDouble(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Double.parseDouble(value.toString()));
- case TIME: // Date
- 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
- 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:
- }
- }
- if (rpcRequest != null) {
- String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
- String errorMsg = String.format("Bad ResourceModel Operations (E): Resource path - %s ResourceModel type - %s", patn, type);
- rpcRequest.setErrorMsg(errorMsg);
- }
- return null;
- } catch (NumberFormatException e) {
- 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());
- handler.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);
- handler.sentRpcResponse(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
- }
- return null;
- }
- }
-
- private void handleResponse(Registration registration, final String path, LwM2mResponse response,
- DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
- responseRequestExecutor.submit(() -> {
- try {
- this.sendResponse(registration, path, response, request, rpcRequest);
- } catch (Exception e) {
- log.error("[{}] endpoint [{}] path [{}] Exception Unable to after send response.", registration.getEndpoint(), path, e);
- }
- });
- }
-
- /**
- * processing a response from a client
- *
- * @param registration -
- * @param path -
- * @param response -
- */
- private void sendResponse(Registration registration, String path, LwM2mResponse response,
- DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
- String pathIdVer = convertPathFromObjectIdToIdVer(path, registration);
- String msgLog = "";
- if (response instanceof ReadResponse) {
- handler.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
- } else if (response instanceof DeleteResponse) {
- log.warn("11) [{}] Path [{}] DeleteResponse", pathIdVer, response);
- if (rpcRequest != null) {
- rpcRequest.setInfoMsg(null);
- handler.sentRpcResponse(rpcRequest, response.getCode().getName(), null, null);
- }
- } else if (response instanceof DiscoverResponse) {
- String discoverValue = Link.serialize(((DiscoverResponse) response).getObjectLinks());
- msgLog = String.format("%s: type operation: %s path: %s value: %s",
- LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue);
- handler.sendLogsToThingsboard(msgLog, registration.getId());
- log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response);
- if (rpcRequest != null) {
- handler.sentRpcResponse(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE);
- }
- } else if (response instanceof ExecuteResponse) {
- msgLog = String.format("%s: type operation: %s path: %s",
- LOG_LW2M_INFO, EXECUTE.name(), request.getPath().toString());
- log.warn("9) [{}] ", msgLog);
- handler.sendLogsToThingsboard(msgLog, registration.getId());
- if (rpcRequest != null) {
- msgLog = String.format("Start %s path: %S. Preparation finished: %s", EXECUTE.name(), path, rpcRequest.getInfoMsg());
- rpcRequest.setInfoMsg(msgLog);
- handler.sentRpcResponse(rpcRequest, response.getCode().getName(), path, LOG_LW2M_INFO);
- }
-
- } else if (response instanceof WriteAttributesResponse) {
- msgLog = String.format("%s: type operation: %s path: %s value: %s",
- LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString());
- handler.sendLogsToThingsboard(msgLog, registration.getId());
- log.warn("12) [{}] Path [{}] WriteAttributesResponse", pathIdVer, response);
- if (rpcRequest != null) {
- handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE);
- }
- } else if (response instanceof WriteResponse) {
- msgLog = String.format("Type operation: Write path: %s", pathIdVer);
- log.warn("10) [{}] response: [{}]", msgLog, response);
- this.infoWriteResponse(registration, response, request, rpcRequest);
- handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
- }
- }
-
- private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
- try {
- LwM2mNode node = ((WriteRequest) request).getNode();
- String msg = null;
- Object value;
- if (node instanceof LwM2mObject) {
- msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s",
- LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObject) node).toString());
- } else if (node instanceof LwM2mObjectInstance) {
- msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s",
- LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObjectInstance) node).prettyPrint());
- } else if (node instanceof LwM2mSingleResource) {
- LwM2mSingleResource singleResource = (LwM2mSingleResource) node;
- if (singleResource.getType() == ResourceModel.Type.STRING || singleResource.getType() == ResourceModel.Type.OPAQUE) {
- int valueLength;
- if (singleResource.getType() == ResourceModel.Type.STRING) {
- valueLength = ((String) singleResource.getValue()).length();
- value = ((String) singleResource.getValue())
- .substring(Math.min(valueLength, config.getLogMaxLength())).trim();
-
- } else {
- valueLength = ((byte[]) singleResource.getValue()).length;
- value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()),
- Math.min(valueLength, config.getLogMaxLength()))).trim();
- }
- value = valueLength > config.getLogMaxLength() ? value + "..." : value;
- msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path: %s length: %s value: %s",
- LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value);
- } else {
- value = this.converter.convertValue(singleResource.getValue(),
- singleResource.getType(), ResourceModel.Type.STRING, request.getPath());
- msg = String.format("%s: Update finished successfully. Lwm2m code: %d Resource path: %s value: %s",
- LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value);
- }
- }
- if (msg != null) {
- handler.sendLogsToThingsboard(msg, registration.getId());
- if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
- this.afterWriteSuccessFwSwUpdate(registration, request);
- if (rpcRequest != null) {
- rpcRequest.setInfoMsg(msg);
- }
- }
- else if (rpcRequest != null) {
- handler.sentRpcResponse(rpcRequest, response.getCode().getName(), msg, LOG_LW2M_INFO);
- }
- }
- } catch (Exception e) {
- log.trace("Fail convert value from request to string. ", e);
- }
- }
-
- /**
- * After finish operation FwSwUpdate Write (success):
- * fw_state/sw_state = DOWNLOADED
- * send operation Execute
- */
- private void afterWriteSuccessFwSwUpdate(Registration registration, DownlinkRequest request) {
- LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
- if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
- lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name());
- lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
- }
- if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) {
- lwM2MClient.getSwUpdate().setStateUpdate(DOWNLOADED.name());
- lwM2MClient.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
- }
- }
-
- /**
- * After finish operation FwSwUpdate Write (error): fw_state = FAILED
- */
- private void afterWriteFwSWUpdateError(Registration registration, DownlinkRequest request, String msgError) {
- LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
- if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
- lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name());
- lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
- }
- if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) {
- lwM2MClient.getSwUpdate().setStateUpdate(FAILED.name());
- lwM2MClient.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
- }
- }
-
- private void afterExecuteFwSwUpdateError(Registration registration, DownlinkRequest request, String msgError) {
- LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
- if (request.getPath().toString().equals(FW_UPDATE_ID) && lwM2MClient.getFwUpdate() != null) {
- lwM2MClient.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
- }
- if (request.getPath().toString().equals(SW_INSTALL_ID) && lwM2MClient.getSwUpdate() != null) {
- lwM2MClient.getSwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
- }
- }
-
- private void afterObserveCancel(Registration registration, int observeCancelCnt, String observeCancelMsg, Lwm2mClientRpcRequest rpcRequest) {
- handler.sendLogsToThingsboard(observeCancelMsg, registration.getId());
- log.warn("[{}]", observeCancelMsg);
- if (rpcRequest != null) {
- rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt));
- handler.sentRpcResponse(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO);
- }
- }
-}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java
index d31883015e..a50e979b77 100644
--- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java
@@ -37,6 +37,8 @@ import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
import org.eclipse.leshan.core.model.InvalidDDFFileException;
import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel;
+import org.eclipse.leshan.core.node.LwM2mPath;
+import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.request.ContentFormat;
import org.springframework.stereotype.Component;
@@ -48,6 +50,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor;
+import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
+import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -57,6 +61,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.BOOLEAN_V;
+import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId;
@Slf4j
@Component
@@ -65,41 +70,17 @@ import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.
public class LwM2mTransportServerHelper {
private final LwM2mTransportContext context;
- private final LwM2MJsonAdaptor adaptor;
private final AtomicInteger atomicTs = new AtomicInteger(0);
-
public long getTS() {
- int addTs = atomicTs.getAndIncrement() >= 1000 ? atomicTs.getAndSet(0) : atomicTs.get();
- return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) * 1000L + addTs;
- }
-
- /**
- * send to Thingsboard Attribute || Telemetry
- *
- * @param msg - JsonObject: [{name: value}]
- * @return - dummyWriteReplace {\"targetIdVer\":\"/19_1.0/0/0\",\"value\":0082}
- */
- private TransportServiceCallback