paramsIdVer = new ConcurrentHashMap<>();
+ params.forEach((k, v) -> {
+ String targetIdVer = this.getPresentPathIntoProfile(sessionInfo, k);
+ if (targetIdVer != null ) {
+ LwM2mPath targetId = new LwM2mPath(convertPathFromIdVerToObjectId(targetIdVer));
+ if (targetId.isResource()) {
+ paramsIdVer.put(String.valueOf(targetId.getResourceId()), v);
+ }
+ }
+ });
+ return paramsIdVer;
+ }
+
+ public void sentRpcRequest(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) {
+ rpcRequest.setResponseCode(requestCode);
+ if (LOG_LW2M_ERROR.equals(typeMsg)) {
+ rpcRequest.setInfoMsg(null);
+ rpcRequest.setValueMsg(null);
+ if (rpcRequest.getErrorMsg() == null) {
+ msg = msg.isEmpty() ? null : msg;
+ rpcRequest.setErrorMsg(msg);
+ }
+ } else if (LOG_LW2M_INFO.equals(typeMsg)) {
+ if (rpcRequest.getInfoMsg() == null) {
+ rpcRequest.setInfoMsg(msg);
+ }
+ } else if (LOG_LW2M_VALUE.equals(typeMsg)) {
+ if (rpcRequest.getValueMsg() == null) {
+ rpcRequest.setValueMsg(msg);
+ }
+ }
+ this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo());
+ }
+
+ @Override
+ public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, SessionInfoProto sessionInfo) {
+ 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.info("[{}] [{}] 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.getKvLogyToThingsboard(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())) {
+ if (FR_PATH_RESOURCE_VER_ID.equals(convertPathFromIdVerToObjectId(path)) &&
+ lwM2MClient.getFrUpdate().getCurrentFwVersion() != null
+ && !lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())
+ && lwM2MClient.isUpdateFw()) {
+
+ /** version != null
+ * set setClient_fw_version = value
+ **/
+ lwM2MClient.setUpdateFw(false);
+ lwM2MClient.getFrUpdate().setClientFwVersion(lwM2mResource.getValue().toString());
+ log.warn("updateFirmwareClient3");
+ this.updateFirmwareClient(lwM2MClient);
+ }
+ Set paths = new HashSet<>();
+ paths.add(path);
+ this.updateAttrTelemetry(registration, paths);
+ } 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 = 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(resourceValue.getValue(), 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);
+ 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)));
+ }
+
+ /**
+ * @param lwM2MClient -
+ * @param path -
+ * @return - return value of Resource by idPath
+ */
+ private LwM2mResource getResourceValueFromLwM2MClient(LwM2mClient lwM2MClient, String path) {
+ LwM2mResource resourceValue = null;
+ if (new LwM2mPath(convertPathFromIdVerToObjectId(path)).isResource()) {
+ resourceValue = lwM2MClient.getResources().get(path).getLwM2mResource();
+ }
+ return resourceValue;
+ }
+
+ /**
+ * 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.toClientProfile(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());
+ 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);
+ // send attr/telemetry to tingsboard for new path
+ this.updateAttrTelemetry(registration, sendAttrToThingsboard.getPathPostParametersAdd());
+ });
+ }
+ // #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 -
+ */
+ private 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(String.valueOf(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);
+ 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);
+ });
+ }
+
+ /**
+ * @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
+ */
+ private 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));
+ }
+ }
+
+ 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.warn("Failed to decode get attributes request", e);
+ }
+ }
+
+ }
+ }
+
+ public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient) {
+ SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient);
+ if (sessionInfo != null) {
+ TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
+ .setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
+ .setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
+ .setTenantIdMSB(sessionInfo.getTenantIdMSB())
+ .setTenantIdLSB(sessionInfo.getTenantIdLSB())
+ .setType(FirmwareType.FIRMWARE.name())
+ .build();
+ transportService.process(sessionInfo, getFirmwareRequestMsg,
+ new TransportServiceCallback<>() {
+ @Override
+ public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) {
+ if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) {
+ lwM2MClient.getFrUpdate().setCurrentFwVersion(response.getVersion());
+ lwM2MClient.getFrUpdate().setCurrentFwId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId());
+ lwM2MClient.setUpdateFw(true);
+ readRequestToClientFirmwareVer(lwM2MClient.getRegistration());
+ } else {
+ log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.trace("Failed to process credentials ", e);
+ }
+ });
+ }
+ }
+
+ /**
+ * @param registration
+ */
+ public void readRequestToClientFirmwareVer(Registration registration) {
+ String pathIdVer = convertPathFromObjectIdToIdVer(FR_PATH_RESOURCE_VER_ID, registration);
+ lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, READ, ContentFormat.TLV.getName(),
+ null, config.getTimeout(), null);
+ }
+
+ /**
+ * @param lwM2MClient -
+ */
+ public void updateFirmwareClient(LwM2mClient lwM2MClient) {
+ if (!lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())) {
+ int chunkSize = 0;
+ int chunk = 0;
+ byte[] firmwareChunk = firmwareDataCache.get(lwM2MClient.getFrUpdate().getCurrentFwId().toString(), chunkSize, chunk);
+ String verSupportedObject = lwM2MClient.getRegistration().getSupportedObject().get(FR_OBJECT_ID);
+ String targetIdVer = LWM2M_SEPARATOR_PATH + FR_OBJECT_ID + LWM2M_SEPARATOR_KEY + verSupportedObject + LWM2M_SEPARATOR_PATH + 0 + LWM2M_SEPARATOR_PATH + 0;
+ lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(),
+ firmwareChunk, config.getTimeout(), null);
+ log.warn("updateFirmwareClient [{}] [{}]", lwM2MClient.getFrUpdate().getCurrentFwVersion(), lwM2MClient.getFrUpdate().getClientFwVersion());
+ }
+ }
+
+
+ /**
+ * !!! 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)));
+ }
+
+}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
similarity index 64%
rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java
rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
index 0af20b3e51..3df1263a3f 100644
--- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerConfiguration.java
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java
@@ -15,6 +15,7 @@
*/
package org.thingsboard.server.transport.lwm2m.server;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
@@ -27,12 +28,16 @@ import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.eclipse.leshan.server.security.DefaultAuthorizer;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.SecurityChecker;
-import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
+import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
+import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
import java.math.BigInteger;
import java.security.AlgorithmParameters;
import java.security.KeyFactory;
@@ -57,61 +62,86 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getCoapConfig;
+import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig;
@Slf4j
@Component
@TbLwM2mTransportComponent
-public class LwM2mTransportServerConfiguration {
+@RequiredArgsConstructor
+public class DefaultLwM2mTransportService implements LwM2MTransportService {
+
private PublicKey publicKey;
private PrivateKey privateKey;
private boolean pskMode = false;
- private final LwM2mTransportContextServer context;
+
+ private final LwM2mTransportContext context;
+ private final LwM2MTransportServerConfig config;
+ private final LwM2mTransportServerHelper helper;
+ private final LwM2mTransportMsgHandler handler;
private final CaliforniumRegistrationStore registrationStore;
private final EditableSecurityStore securityStore;
private final LwM2mClientContext lwM2mClientContext;
- public LwM2mTransportServerConfiguration(LwM2mTransportContextServer context, CaliforniumRegistrationStore registrationStore, EditableSecurityStore securityStore, LwM2mClientContext lwM2mClientContext) {
- this.context = context;
- this.registrationStore = registrationStore;
- this.securityStore = securityStore;
- this.lwM2mClientContext = lwM2mClientContext;
+ private LeshanServer server;
+
+ @PostConstruct
+ public void init() {
+ if (config.getEnableGenNewKeyPskRpk()) {
+ new LWM2MGenerationPSkRPkECC();
+ }
+ this.server = getLhServer();
+ this.startLhServer();
+ this.context.setServer(server);
+ }
+
+ private void startLhServer() {
+ log.info("Starting LwM2M transport server...");
+ this.server.start();
+ LwM2mServerListener lhServerCertListener = new LwM2mServerListener(handler);
+ this.server.getRegistrationService().addListener(lhServerCertListener.registrationListener);
+ this.server.getPresenceService().addListener(lhServerCertListener.presenceListener);
+ this.server.getObservationService().addListener(lhServerCertListener.observationListener);
+ log.info("Started LwM2M transport server.");
}
- @Bean
- public LeshanServer getLeshanServer() {
- log.info("Starting LwM2M transport Server... PostConstruct");
- return this.getLhServer(this.context.getLwM2MTransportConfigServer().getServerPortNoSec(), this.context.getLwM2MTransportConfigServer().getServerPortSecurity());
+ @PreDestroy
+ public void shutdown() {
+ log.info("Stopping LwM2M transport server!");
+ server.destroy();
+ log.info("LwM2M transport server stopped!");
}
- private LeshanServer getLhServer(Integer serverPortNoSec, Integer serverSecurePort) {
+ private LeshanServer getLhServer() {
LeshanServerBuilder builder = new LeshanServerBuilder();
- builder.setLocalAddress(this.context.getLwM2MTransportConfigServer().getServerHost(), serverPortNoSec);
- builder.setLocalSecureAddress(this.context.getLwM2MTransportConfigServer().getServerHostSecurity(), serverSecurePort);
+ builder.setLocalAddress(config.getHost(), config.getPort());
+ builder.setLocalSecureAddress(config.getSecureHost(), config.getSecurePort());
builder.setDecoder(new DefaultLwM2mNodeDecoder());
- /** Use a magic converter to support bad type send by the UI. */
+ /* Use a magic converter to support bad type send by the UI. */
builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
- /** Create CoAP Config */
- builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort));
- /** Define model provider (Create Models )*/
- LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.context);
- this.context.getLwM2MTransportConfigServer().setModelProvider(modelProvider);
+ /* Create CoAP Config */
+ builder.setCoapConfig(getCoapConfig(config.getPort(), config.getSecurePort()));
+
+ /* Define model provider (Create Models )*/
+ LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.helper, this.context);
+ config.setModelProvider(modelProvider);
builder.setObjectModelProvider(modelProvider);
- /** Create credentials */
+ /* Create credentials */
this.setServerWithCredentials(builder);
- /** Set securityStore with new registrationStore */
+ /* Set securityStore with new registrationStore */
builder.setSecurityStore(securityStore);
builder.setRegistrationStore(registrationStore);
- /** Create DTLS Config */
+ /* Create DTLS Config */
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
- dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportConfigServer().isRecommendedSupportedGroups());
- dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportConfigServer().isRecommendedCiphers());
+
+ dtlsConfig.setServerOnly(true);
+ dtlsConfig.setRecommendedSupportedGroupsOnly(config.isRecommendedSupportedGroups());
+ dtlsConfig.setRecommendedCipherSuitesOnly(config.isRecommendedCiphers());
if (this.pskMode) {
dtlsConfig.setSupportedCipherSuites(
TLS_PSK_WITH_AES_128_CCM_8,
@@ -124,27 +154,28 @@ public class LwM2mTransportServerConfiguration {
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256);
}
- /** Set DTLS Config */
+
+ /* Set DTLS Config */
builder.setDtlsConfig(dtlsConfig);
- /** Create LWM2M server */
+ /* Create LWM2M server */
return builder.build();
}
private void setServerWithCredentials(LeshanServerBuilder builder) {
try {
- if (this.context.getLwM2MTransportConfigServer().getKeyStoreValue() != null) {
+ if (config.getKeyStoreValue() != null) {
if (this.setBuilderX509(builder)) {
- X509Certificate rootCAX509Cert = (X509Certificate) this.context.getLwM2MTransportConfigServer().getKeyStoreValue().getCertificate(this.context.getLwM2MTransportConfigServer().getRootAlias());
+ X509Certificate rootCAX509Cert = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getRootCertificateAlias());
if (rootCAX509Cert != null) {
X509Certificate[] trustedCertificates = new X509Certificate[1];
trustedCertificates[0] = rootCAX509Cert;
builder.setTrustedCertificates(trustedCertificates);
} else {
- /** by default trust all */
+ /* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
}
- /** Set securityStore with registrationStore*/
+ /* Set securityStore with registrationStore*/
builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() {
@Override
protected boolean matchX509Identity(String endpoint, String receivedX509CommonName,
@@ -157,7 +188,7 @@ public class LwM2mTransportServerConfiguration {
this.infoPramsUri("RPK");
this.infoParamsServerKey(this.publicKey, this.privateKey);
} else {
- /** by default trust all */
+ /* by default trust all */
builder.setTrustedCertificates(new X509Certificate[0]);
log.info("Unable to load X509 files for LWM2MServer");
this.pskMode = true;
@@ -169,17 +200,11 @@ public class LwM2mTransportServerConfiguration {
}
private boolean setBuilderX509(LeshanServerBuilder builder) {
- /**
- * For deb => KeyStorePathFile == yml or commandline: KEY_STORE_PATH_FILE
- * For idea => KeyStorePathResource == common/transport/lwm2m/src/main/resources/credentials: in LwM2MTransportContextServer: credentials/serverKeyStore.jks
- */
try {
- X509Certificate serverCertificate = (X509Certificate) this.context.getLwM2MTransportConfigServer().getKeyStoreValue().getCertificate(this.context.getLwM2MTransportConfigServer().getServerAlias());
- PrivateKey privateKey = (PrivateKey) this.context.getLwM2MTransportConfigServer().getKeyStoreValue().getKey(this.context.getLwM2MTransportConfigServer().getServerAlias(), this.context.getLwM2MTransportConfigServer().getKeyStorePasswordServer() == null ? null : this.context.getLwM2MTransportConfigServer().getKeyStorePasswordServer().toCharArray());
+ X509Certificate serverCertificate = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getCertificateAlias());
+ PrivateKey privateKey = (PrivateKey) config.getKeyStoreValue().getKey(config.getCertificateAlias(), config.getKeyStorePassword() == null ? null : config.getKeyStorePassword().toCharArray());
PublicKey publicKey = serverCertificate.getPublicKey();
- if (serverCertificate != null &&
- privateKey != null && privateKey.getEncoded().length > 0 &&
- publicKey != null && publicKey.getEncoded().length > 0) {
+ if (privateKey != null && privateKey.getEncoded().length > 0 && publicKey != null && publicKey.getEncoded().length > 0) {
builder.setPublicKey(serverCertificate.getPublicKey());
builder.setPrivateKey(privateKey);
builder.setCertificateChain(new X509Certificate[]{serverCertificate});
@@ -206,10 +231,12 @@ public class LwM2mTransportServerConfiguration {
}
private void infoPramsUri(String mode) {
- log.info("Server uses [{}]: serverNoSecureURI : [{}], serverSecureURI : [{}]",
- mode,
- this.context.getLwM2MTransportConfigServer().getServerHost() + ":" + this.context.getLwM2MTransportConfigServer().getServerPortNoSec(),
- this.context.getLwM2MTransportConfigServer().getServerHostSecurity() + ":" + this.context.getLwM2MTransportConfigServer().getServerPortSecurity());
+ LwM2MTransportServerConfig lwM2MTransportServerConfig = config;
+ log.info("Server uses [{}]: serverNoSecureURI : [{}:{}], serverSecureURI : [{}:{}]", mode,
+ lwM2MTransportServerConfig.getHost(),
+ lwM2MTransportServerConfig.getPort(),
+ lwM2MTransportServerConfig.getSecureHost(),
+ lwM2MTransportServerConfig.getSecurePort());
}
private boolean setServerRPK(LeshanServerBuilder builder) {
@@ -227,55 +254,42 @@ public class LwM2mTransportServerConfiguration {
return false;
}
-
- /**
- * From yml: server
- * public_x: "${LWM2M_SERVER_PUBLIC_X:405354ea8893471d9296afbc8b020a5c6201b0bb25812a53b849d4480fa5f069}"
- * public_y: "${LWM2M_SERVER_PUBLIC_Y:30c9237e946a3a1692c1cafaa01a238a077f632c99371348337512363f28212b}"
- * private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:274671fe40ce937b8a6352cf0a418e8a39e4bf0bb9bf74c910db953c20c73802}"
- */
private void generateKeyForRPK() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidKeySpecException {
- /** Get Elliptic Curve Parameter spec for secp256r1 */
+ /* Get Elliptic Curve Parameter spec for secp256r1 */
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
- if (this.context.getLwM2MTransportConfigServer().getServerPublicX() != null &&
- !this.context.getLwM2MTransportConfigServer().getServerPublicX().isEmpty() &&
- this.context.getLwM2MTransportConfigServer().getServerPublicY() != null &&
- !this.context.getLwM2MTransportConfigServer().getServerPublicY().isEmpty()) {
- /** Get point values */
- byte[] publicX = Hex.decodeHex(this.context.getLwM2MTransportConfigServer().getServerPublicX().toCharArray());
- byte[] publicY = Hex.decodeHex(this.context.getLwM2MTransportConfigServer().getServerPublicY().toCharArray());
- /** Create key specs */
+ LwM2MTransportServerConfig serverConfig = config;
+ if (StringUtils.isNotEmpty(serverConfig.getPublicX()) && StringUtils.isNotEmpty(serverConfig.getPublicY())) {
+ byte[] publicX = Hex.decodeHex(serverConfig.getPublicX().toCharArray());
+ byte[] publicY = Hex.decodeHex(serverConfig.getPublicY().toCharArray());
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec);
- /** Get keys */
this.publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
}
- if (this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded() != null &&
- !this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded().isEmpty()) {
- /** Get private key */
- byte[] privateS = Hex.decodeHex(this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded().toCharArray());
+ String privateEncodedKey = serverConfig.getPrivateEncoded();
+ if (StringUtils.isNotEmpty(privateEncodedKey)) {
+ byte[] privateS = Hex.decodeHex(privateEncodedKey.toCharArray());
try {
this.privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateS));
} catch (InvalidKeySpecException ignore2) {
- log.error("Invalid Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", this.context.getLwM2MTransportConfigServer().getServerPrivateEncoded());
+ log.error("Invalid Server rpk.PrivateKey.getEncoded () [{}}]. PrivateKey has no EC algorithm", privateEncodedKey);
}
}
}
private void infoParamsServerKey(PublicKey publicKey, PrivateKey privateKey) {
- /** Get x coordinate */
+ /* Get x coordinate */
byte[] x = ((ECPublicKey) publicKey).getW().getAffineX().toByteArray();
if (x[0] == 0)
x = Arrays.copyOfRange(x, 1, x.length);
- /** Get Y coordinate */
+ /* Get Y coordinate */
byte[] y = ((ECPublicKey) publicKey).getW().getAffineY().toByteArray();
if (y[0] == 0)
y = Arrays.copyOfRange(y, 1, y.length);
- /** Get Curves params */
+ /* Get Curves params */
String params = ((ECPublicKey) publicKey).getParams().toString();
String privHex = Hex.encodeHexString(privateKey.getEncoded());
log.info(" \n- Public Key (Hex): [{}] \n" +
@@ -292,4 +306,9 @@ public class LwM2mTransportServerConfiguration {
params);
}
+ @Override
+ public String getName() {
+ return "LWM2M";
+ }
+
}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java
new file mode 100644
index 0000000000..6ad033503c
--- /dev/null
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MTransportService.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.lwm2m.server;
+
+import org.thingsboard.server.common.data.TbTransportService;
+
+public interface LwM2MTransportService extends TbTransportService {
+
+}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java
new file mode 100644
index 0000000000..3de5846ca0
--- /dev/null
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java
@@ -0,0 +1,99 @@
+/**
+ * Copyright © 2016-2021 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.transport.lwm2m.server;
+
+import org.eclipse.californium.core.network.config.NetworkConfig;
+
+public class LwM2mNetworkConfig {
+
+ public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) {
+ NetworkConfig coapConfig = new NetworkConfig();
+ coapConfig.setInt(NetworkConfig.Keys.COAP_PORT,serverPortNoSec);
+ coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT,serverSecurePort);
+ /*
+ Example:Property for large packet:
+ #NetworkConfig config = new NetworkConfig();
+ #config.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE,32);
+ #config.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE,32);
+ #config.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE,2048);
+ #config.setInt(NetworkConfig.Keys.MAX_RETRANSMIT,3);
+ #config.setInt(NetworkConfig.Keys.MAX_TRANSMIT_WAIT,120000);
+ */
+
+ /*
+ Property to indicate if the response should always include the Block2 option \
+ when client request early blockwise negociation but the response can be sent on one packet.
+ - value of false indicate that the server will respond without block2 option if no further blocks are required.
+ - value of true indicate that the server will response with block2 option event if no further blocks are required.
+ CoAP client will try to use block mode
+ or adapt the block size when receiving a 4.13 Entity too large response code
+ */
+ coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true);
+ /*
+ Property to indicate if the response should always include the Block2 option \
+ when client request early blockwise negociation but the response can be sent on one packet.
+ - value of false indicate that the server will respond without block2 option if no further blocks are required.
+ - value of true indicate that the server will response with block2 option event if no further blocks are required.
+ */
+ coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true);
+
+ coapConfig.setInt(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, 300000);
+ /*
+ !!! REQUEST_ENTITY_TOO_LARGE CODE=4.13
+ The maximum size of a resource body (in bytes) that will be accepted
+ as the payload of a POST/PUT or the response to a GET request in a
+ transparent> blockwise transfer.
+ This option serves as a safeguard against excessive memory
+ consumption when many resources contain large bodies that cannot be
+ transferred in a single CoAP message. This option has no impact on
+ *manually* managed blockwise transfers in which the blocks are handled individually.
+ Note that this option does not prevent local clients or resource
+ implementations from sending large bodies as part of a request or response to a peer.
+ The default value of this property is DEFAULT_MAX_RESOURCE_BODY_SIZE = 8192
+ A value of {@code 0} turns off transparent handling of blockwise transfers altogether.
+ */
+ coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024);
+ /*
+ The default DTLS response matcher.
+ Supported values are STRICT, RELAXED, or PRINCIPAL.
+ The default value is STRICT.
+ Create new instance of udp endpoint context matcher.
+ Params:
+ checkAddress
+ – true with address check, (STRICT, UDP)
+ - false, without
+ */
+ coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "STRICT");
+ /*
+ https://tools.ietf.org/html/rfc7959#section-2.9.3
+ The block size (number of bytes) to use when doing a blockwise transfer. \
+ This value serves as the upper limit for block size in blockwise transfers
+ */
+ coapConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024);
+ /*
+ The maximum payload size (in bytes) that can be transferred in a
+ single message, i.e. without requiring a blockwise transfer.
+ NB: this value MUST be adapted to the maximum message size supported by the transport layer.
+ In particular, this value cannot exceed the network's MTU if UDP is used as the transport protocol
+ DEFAULT_VALUE = 1024
+ */
+ coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024);
+
+ coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4);
+
+ return coapConfig;
+ }
+}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
index ffe2f76602..f46b1c05f7 100644
--- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
@@ -26,14 +26,15 @@ import org.eclipse.leshan.server.registration.RegistrationUpdate;
import java.util.Collection;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToIdVerFromObjectId;
+import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO;
+import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
@Slf4j
public class LwM2mServerListener {
- private final LwM2mTransportServiceImpl service;
+ private final LwM2mTransportMsgHandler service;
- public LwM2mServerListener(LwM2mTransportServiceImpl service) {
+ public LwM2mServerListener(LwM2mTransportMsgHandler service) {
this.service = service;
}
@@ -85,16 +86,19 @@ public class LwM2mServerListener {
@Override
public void cancelled(Observation observation) {
- log.info("Received notification cancelled from [{}] ", observation.getPath());
+ String msg = String.format("%s: Cancel Observation %s.", LOG_LW2M_INFO, observation.getPath());
+ service.sendLogsToThingsboard(msg, observation.getRegistrationId());
+ log.trace(msg);
}
@Override
public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
if (registration != null) {
try {
- service.onObservationResponse(registration, convertToIdVerFromObjectId(observation.getPath().toString(), registration), response);
+ service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
+ registration), response, null);
} catch (Exception e) {
- log.error("[{}] onResponse", e.toString());
+ log.error("Observation/Read onResponse", e);
}
}
@@ -107,7 +111,10 @@ public class LwM2mServerListener {
@Override
public void newObservation(Observation observation, Registration registration) {
-// log.info("Received newObservation from [{}] endpoint [{}] ", observation.getPath(), registration.getEndpoint());
+ String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO,
+ observation.getPath());
+ service.sendLogsToThingsboard(msg, registration.getId());
+ log.trace(msg);
}
};
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java
index 0bc78254a6..5d6486d9a2 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
@@ -35,22 +35,22 @@ import java.util.Optional;
@Slf4j
public class LwM2mSessionMsgListener implements GenericFutureListener>, SessionMsgListener {
- private LwM2mTransportServiceImpl service;
+ private DefaultLwM2MTransportMsgHandler handler;
private TransportProtos.SessionInfoProto sessionInfo;
- public LwM2mSessionMsgListener(LwM2mTransportServiceImpl service, TransportProtos.SessionInfoProto sessionInfo) {
- this.service = service;
+ public LwM2mSessionMsgListener(DefaultLwM2MTransportMsgHandler handler, TransportProtos.SessionInfoProto sessionInfo) {
+ this.handler = handler;
this.sessionInfo = sessionInfo;
}
@Override
public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) {
- this.service.onGetAttributesResponse(getAttributesResponse, this.sessionInfo);
+ this.handler.onGetAttributesResponse(getAttributesResponse, this.sessionInfo);
}
@Override
public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) {
- this.service.onAttributeUpdate(attributeUpdateNotification, this.sessionInfo);
+ this.handler.onAttributeUpdate(attributeUpdateNotification, this.sessionInfo);
}
@Override
@@ -60,27 +60,27 @@ public class LwM2mSessionMsgListener implements GenericFutureListener deviceProfileOpt) {
- this.service.onDeviceUpdate(sessionInfo, device, deviceProfileOpt);
+ this.handler.onDeviceUpdate(sessionInfo, device, deviceProfileOpt);
}
@Override
public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
- this.service.onToDeviceRpcRequest(toDeviceRequest);
+ this.handler.onToDeviceRpcRequest(toDeviceRequest,this.sessionInfo);
}
@Override
public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) {
- this.service.onToServerRpcResponse(toServerResponse);
+ this.handler.onToServerRpcResponse(toServerResponse);
}
@Override
@@ -91,14 +91,14 @@ public class LwM2mSessionMsgListener implements GenericFutureListener resourceUpdateMsgOpt) {
if (ResourceType.LWM2M_MODEL.name().equals(resourceUpdateMsgOpt.get().getResourceType())) {
- this.service.onResourceUpdate(resourceUpdateMsgOpt);
+ this.handler.onResourceUpdate(resourceUpdateMsgOpt);
}
}
@Override
public void onResourceDelete(@NotNull Optional resourceDeleteMsgOpt) {
if (ResourceType.LWM2M_MODEL.name().equals(resourceDeleteMsgOpt.get().getResourceType())) {
- this.service.onResourceDelete(resourceDeleteMsgOpt);
+ this.handler.onResourceDelete(resourceDeleteMsgOpt);
}
}
}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContext.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContext.java
new file mode 100644
index 0000000000..6ae204affc
--- /dev/null
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContext.java
@@ -0,0 +1,32 @@
+/**
+ * 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;
+import lombok.Setter;
+import org.eclipse.leshan.server.californium.LeshanServer;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.transport.TransportContext;
+import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
+
+@Component
+@TbLwM2mTransportComponent
+public class LwM2mTransportContext extends TransportContext {
+
+ @Getter @Setter
+ private LeshanServer server;
+
+}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java
deleted file mode 100644
index 170530f084..0000000000
--- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportContextServer.java
+++ /dev/null
@@ -1,162 +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;
-/**
- * Copyright © 2016-2020 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.
- */
-
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
-import org.eclipse.leshan.core.model.DDFFileParser;
-import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
-import org.eclipse.leshan.core.model.InvalidDDFFileException;
-import org.eclipse.leshan.core.model.ObjectModel;
-import org.springframework.stereotype.Component;
-import org.thingsboard.server.common.transport.TransportContext;
-import org.thingsboard.server.common.transport.TransportResourceCache;
-import org.thingsboard.server.common.transport.TransportService;
-import org.thingsboard.server.common.transport.TransportServiceCallback;
-import org.thingsboard.server.common.transport.adaptor.AdaptorException;
-import org.thingsboard.server.common.transport.lwm2m.LwM2MTransportConfigServer;
-import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
-import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
-import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
-import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
-import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
-import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_TELEMETRY;
-
-@Slf4j
-@Component
-@TbLwM2mTransportComponent
-public class LwM2mTransportContextServer extends TransportContext {
-
-
- private final LwM2MTransportConfigServer lwM2MTransportConfigServer;
-
- private final TransportService transportService;
-
- private final TransportResourceCache transportResourceCache;
-
-
- @Getter
- private final LwM2MJsonAdaptor adaptor;
-
- public LwM2mTransportContextServer(LwM2MTransportConfigServer lwM2MTransportConfigServer, TransportService transportService, TransportResourceCache transportResourceCache, LwM2MJsonAdaptor adaptor) {
- this.lwM2MTransportConfigServer = lwM2MTransportConfigServer;
- this.transportService = transportService;
- this.transportResourceCache = transportResourceCache;
- this.adaptor = adaptor;
- }
-
- public LwM2MTransportConfigServer getLwM2MTransportConfigServer() {
- return this.lwM2MTransportConfigServer;
- }
-
- public TransportResourceCache getTransportResourceCache() {
- return this.transportResourceCache;
- }
-
- /**
- * send to Thingsboard Attribute || Telemetry
- *
- * @param msg - JsonObject: [{name: value}]
- * @return - dummy
- */
- private TransportServiceCallback getPubAckCallbackSendAttrTelemetry(final T msg) {
- return new TransportServiceCallback<>() {
- @Override
- public void onSuccess(Void dummy) {
- log.trace("Success to publish msg: {}, dummy: {}", msg, dummy);
- }
-
- @Override
- public void onError(Throwable e) {
- log.trace("[{}] Failed to publish msg: {}", msg, e);
- }
- };
- }
-
- public void sendParametersOnThingsboard(JsonElement msg, String topicName, SessionInfoProto sessionInfo) {
- try {
- if (topicName.equals(LwM2mTransportHandler.DEVICE_ATTRIBUTES_TOPIC)) {
- PostAttributeMsg postAttributeMsg = adaptor.convertToPostAttributes(msg);
- TransportServiceCallback call = this.getPubAckCallbackSendAttrTelemetry(postAttributeMsg);
- transportService.process(sessionInfo, postAttributeMsg, this.getPubAckCallbackSendAttrTelemetry(call));
- } else if (topicName.equals(LwM2mTransportHandler.DEVICE_TELEMETRY_TOPIC)) {
- PostTelemetryMsg postTelemetryMsg = adaptor.convertToPostTelemetry(msg);
- TransportServiceCallback call = this.getPubAckCallbackSendAttrTelemetry(postTelemetryMsg);
- transportService.process(sessionInfo, postTelemetryMsg, this.getPubAckCallbackSendAttrTelemetry(call));
- }
- } catch (AdaptorException e) {
- log.error("[{}] Failed to process publish msg [{}]", topicName, e);
- log.info("[{}] Closing current session due to invalid publish", topicName);
- }
- }
-
- public JsonObject getTelemetryMsgObject(String logMsg) {
- JsonObject telemetries = new JsonObject();
- telemetries.addProperty(LOG_LW2M_TELEMETRY, logMsg);
- return telemetries;
- }
-
- /**
- * @return - sessionInfo after access connect client
- */
- public SessionInfoProto getValidateSessionInfo(ValidateDeviceCredentialsResponseMsg msg, long mostSignificantBits, long leastSignificantBits) {
- return SessionInfoProto.newBuilder()
- .setNodeId(this.getNodeId())
- .setSessionIdMSB(mostSignificantBits)
- .setSessionIdLSB(leastSignificantBits)
- .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
- .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
- .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
- .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
- .setDeviceName(msg.getDeviceInfo().getDeviceName())
- .setDeviceType(msg.getDeviceInfo().getDeviceType())
- .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
- .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
- .build();
- }
-
- public ObjectModel parseFromXmlToObjectModel(byte[] xmlByte, String streamName, DefaultDDFFileValidator ddfValidator) {
- try {
- DDFFileParser ddfFileParser = new DDFFileParser(ddfValidator);
- return ddfFileParser.parseEx(new ByteArrayInputStream(xmlByte), streamName).get(0);
- } catch (IOException | InvalidDDFFileException e) {
- log.error("Could not parse the XML file [{}]", streamName, e);
- return null;
- }
- }
-}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java
similarity index 79%
rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java
rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java
index 4f3cfc44c8..bb0be3aa36 100644
--- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java
@@ -21,11 +21,12 @@ import org.eclipse.leshan.server.registration.Registration;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
import java.util.Collection;
import java.util.Optional;
-public interface LwM2mTransportService {
+public interface LwM2mTransportMsgHandler {
void onRegistered(Registration registration, Collection previousObsersations);
@@ -37,9 +38,7 @@ public interface LwM2mTransportService {
void setCancelObservations(Registration registration);
- void setCancelObservationRecourse(Registration registration, String path);
-
- void onObservationResponse(Registration registration, String path, ReadResponse response);
+ void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest);
void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo);
@@ -51,7 +50,9 @@ public interface LwM2mTransportService {
void onResourceDelete(Optional resourceDeleteMsgOpt);
- void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest);
+ void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest, TransportProtos.SessionInfoProto sessionInfo);
+
+ void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceRpcResponse, TransportProtos.SessionInfoProto sessionInfo);
void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse);
@@ -59,5 +60,7 @@ public interface LwM2mTransportService {
void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);
+ void onAwakeDev(Registration registration);
+ void sendLogsToThingsboard(String msg, String registrationId);
}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
index 2c728260d0..0a5a545065 100644
--- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
@@ -15,17 +15,19 @@
*/
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.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mNode;
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.CancelObservationRequest;
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;
@@ -44,236 +46,346 @@ 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.californium.LeshanServer;
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.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEFAULT_TIMEOUT;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_DISCOVER;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_OBSERVE;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_READ;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.POST_TYPE_OPER_EXECUTE;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.POST_TYPE_OPER_OBSERVE_CANCEL;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.POST_TYPE_OPER_WRITE_REPLACE;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.PUT_TYPE_OPER_WRITE_ATTRIBUTES;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.PUT_TYPE_OPER_WRITE_UPDATE;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.RESPONSE_CHANNEL;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToIdVerFromObjectId;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToObjectIdFromIdVer;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.createWriteAttributeRequest;
+import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT;
+import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST;
+import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND;
+import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEFAULT_TIMEOUT;
+import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FR_PATH_RESOURCE_VER_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.OBSERVE_CANCEL;
+import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL;
+import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESPONSE_CHANNEL;
+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 executorResponse;
- private LwM2mValueConverterImpl converter;
-
- private final LwM2mTransportContextServer lwM2mTransportContextServer;
+ public LwM2mValueConverterImpl converter;
+ private final LwM2mTransportContext context;
+ private final LwM2MTransportServerConfig config;
private final LwM2mClientContext lwM2mClientContext;
-
- private final LeshanServer leshanServer;
-
- private final LwM2mTransportServiceImpl serviceImpl;
-
- public LwM2mTransportRequest(LwM2mTransportContextServer lwM2mTransportContextServer, LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer, LwM2mTransportServiceImpl serviceImpl) {
- this.lwM2mTransportContextServer = lwM2mTransportContextServer;
- this.lwM2mClientContext = lwM2mClientContext;
- this.leshanServer = leshanServer;
- this.serviceImpl = serviceImpl;
- }
+ private final DefaultLwM2MTransportMsgHandler serviceImpl;
@PostConstruct
public void init() {
this.converter = LwM2mValueConverterImpl.getInstance();
- executorResponse = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getResponsePoolSize(),
+ executorResponse = Executors.newFixedThreadPool(this.config.getResponsePoolSize(),
new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL)));
}
/**
* Device management and service enablement, including Read, Write, Execute, Discover, Create, Delete and Write-Attributes
*
- * @param registration -
- * @param targetIdVer -
- * @param typeOper -
- * @param contentFormatParam -
- * @param observation -
+ * @param registration -
+ * @param targetIdVer -
+ * @param typeOper -
+ * @param contentFormatName -
*/
- public void sendAllRequest(Registration registration, String targetIdVer, String typeOper,
- String contentFormatParam, Observation observation, Object params, long timeoutInMs) {
- String target = convertToObjectIdFromIdVer(targetIdVer);
- LwM2mPath resultIds = new LwM2mPath(target);
- if (registration != null && resultIds.getObjectId() >= 0) {
+
+ public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper,
+ String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
+ try {
+ String target = convertPathFromIdVerToObjectId(targetIdVer);
DownlinkRequest request = null;
- ContentFormat contentFormat = contentFormatParam != null ? ContentFormat.fromName(contentFormatParam.toUpperCase()) : null;
- LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
- ResourceModel resource = null;
- timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
- switch (typeOper) {
- case GET_TYPE_OPER_READ:
- request = new ReadRequest(contentFormat, target);
- break;
- case GET_TYPE_OPER_DISCOVER:
- request = new DiscoverRequest(target);
- break;
- case GET_TYPE_OPER_OBSERVE:
- if (resultIds.isResource()) {
- request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId());
- } else if (resultIds.isObjectInstance()) {
- request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId());
- } else if (resultIds.getObjectId() >= 0) {
- request = new ObserveRequest(resultIds.getObjectId());
- }
- break;
- case POST_TYPE_OPER_OBSERVE_CANCEL:
- request = new CancelObservationRequest(observation);
- break;
- case POST_TYPE_OPER_EXECUTE:
- resource = lwM2MClient.getResourceModel(targetIdVer);
- if (params != null && resource != null && !resource.multiple) {
- request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resource.type, ResourceModel.Type.STRING, resultIds));
- } else {
- request = new ExecuteRequest(target);
+ 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_READ_ALL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) {
+ if (lwM2MClient.isValidObjectVersion(targetIdVer)) {
+ timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
+ ResourceModel resourceModel = null;
+ switch (typeOper) {
+ case READ:
+ request = new ReadRequest(contentFormat, target);
+ break;
+ case DISCOVER:
+ request = new DiscoverRequest(target);
+ break;
+ case OBSERVE:
+ if (resultIds.isResource()) {
+ request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId());
+ } 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 OBSERVE_CANCEL:
+ /*
+ lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
+ At server side this will not remove the observation from the observation store, to do it you need to use
+ {@code ObservationService#cancelObservation()}
+ */
+ context.getServer().getObservationService().cancelObservations(registration, target);
+ break;
+ case EXECUTE:
+ resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config
+ .getModelProvider());
+ if (params != null && !resourceModel.multiple) {
+ request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModel.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.
+ resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
+ if (contentFormat.equals(ContentFormat.TLV)) {
+ request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(),
+ resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type,
+ registration, rpcRequest);
+ }
+ // Mode.REPLACE && Request to write a String Single-Instance Resource using the given content format (TEXT, TLV, JSON)
+ else if (!contentFormat.equals(ContentFormat.TLV)) {
+ request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
+ resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.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.getNewOneResourceForInstance(
+ targetIdVer, params,
+ this.config.getModelProvider(),
+ this.converter);
+ 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();
+ */
+
+ else if (resultIds.isObjectInstance()) {
+ if (((ConcurrentHashMap) params).size() > 0) {
+ Collection resources = lwM2MClient.getNewManyResourcesForInstance(
+ targetIdVer, params,
+ this.config.getModelProvider(),
+ this.converter);
+ if (resources.size()>0) {
+ request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(),
+ resultIds.getObjectInstanceId(), resources);
+ }
+ else {
+ Lwm2mClientRpcRequest rpcRequestClone = (Lwm2mClientRpcRequest) rpcRequest.clone();
+ if (rpcRequestClone != null) {
+ String errorMsg = String.format("Path %s params is not valid", targetIdVer);
+ serviceImpl.sentRpcRequest(rpcRequestClone, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
+ rpcRequest = null;
+ }
+ }
+ }
+ } else if (resultIds.getObjectId() >= 0) {
+ request = new ObserveRequest(resultIds.getObjectId());
+ }
+ break;
+ case WRITE_ATTRIBUTES:
+ request = createWriteAttributeRequest(target, params);
+ break;
+ case DELETE:
+ request = new DeleteRequest(target);
+ break;
}
- break;
- case POST_TYPE_OPER_WRITE_REPLACE:
- // Request to write a String Single-Instance Resource using the TLV content format.
- resource = lwM2MClient.getResourceModel(targetIdVer);
- if (resource != null && contentFormat != null) {
-// if (contentFormat.equals(ContentFormat.TLV) && !resource.multiple) {
- if (contentFormat.equals(ContentFormat.TLV)) {
- request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resource.type, registration);
+
+ if (request != null) {
+ try {
+ this.sendRequest(registration, lwM2MClient, request, timeoutInMs, rpcRequest);
+ } catch (ClientSleepingException e) {
+ DownlinkRequest finalRequest = request;
+ long finalTimeoutInMs = timeoutInMs;
+ Lwm2mClientRpcRequest finalRpcRequest = rpcRequest;
+ lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest));
+ } catch (Exception e) {
+ log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e);
}
- // Mode.REPLACE && Request to write a String Single-Instance Resource using the given content format (TEXT, TLV, JSON)
-// else if (!contentFormat.equals(ContentFormat.TLV) && !resource.multiple) {
- else if (!contentFormat.equals(ContentFormat.TLV)) {
- request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resource.type, registration);
+ } else if (OBSERVE_CANCEL == typeOper) {
+ log.trace("[{}], [{}] - [{}] SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
+ if (rpcRequest != null) {
+ rpcRequest.setInfoMsg(null);
+ serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null);
+ }
+ } else {
+ log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
+ if (rpcRequest != null) {
+ String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null";
+ serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
}
}
- break;
- case PUT_TYPE_OPER_WRITE_UPDATE:
- if (resultIds.getResourceId() >= 0) {
-// ResourceModel resourceModel = leshanServer.getModelProvider().getObjectModel(registration).getObjectModel(resultIds.getObjectId()).resources.get(resultIds.getResourceId());
-// ResourceModel.Type typeRes = resourceModel.type;
- LwM2mNode node = LwM2mSingleResource.newStringResource(resultIds.getResourceId(), (String) this.converter.convertValue(params, resource.type, ResourceModel.Type.STRING, resultIds));
- request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, target, node);
- }
- break;
- case PUT_TYPE_OPER_WRITE_ATTRIBUTES:
- request = createWriteAttributeRequest(target, params);
- break;
- }
-
- if (request != null) {
- try {
- this.sendRequest(registration, lwM2MClient, request, timeoutInMs);
- } catch (ClientSleepingException e) {
- DownlinkRequest finalRequest = request;
- long finalTimeoutInMs = timeoutInMs;
- lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs));
- } catch (Exception e) {
- log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper, e);
+ } else if (rpcRequest != null) {
+ String errorMsg = String.format("Path %s not found in object version", targetIdVer);
+ serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
}
- } else {
- log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper, targetIdVer);
+ } else if (OBSERVE_READ_ALL.name().equals(typeOper.name())) {
+ Set observations = context.getServer().getObservationService().getObservations(registration);
+ Set observationPaths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet());
+ String msg = String.format("%s: type operation %s observation paths - %s", LOG_LW2M_INFO,
+ OBSERVE_READ_ALL.type, observationPaths);
+ serviceImpl.sendLogsToThingsboard(msg, registration.getId());
+ log.trace("[{}] [{}], [{}]", typeOper.name(), registration.getEndpoint(), msg);
+ if (rpcRequest != null) {
+ String valueMsg = String.format("Observation paths - %s", observationPaths);
+ serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
+ }
+ }
+ } catch (Exception e) {
+ String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
+ typeOper.name(), e.getMessage());
+ serviceImpl.sendLogsToThingsboard(msg, registration.getId());
+ try {
+ throw new Exception(e);
+ } catch (Exception exception) {
+ exception.printStackTrace();
}
}
}
/**
- *
* @param registration -
- * @param request -
- * @param timeoutInMs -
+ * @param request -
+ * @param timeoutInMs -
*/
- @SuppressWarnings("unchecked")
- private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, long timeoutInMs) {
- leshanServer.send(registration, request, timeoutInMs, (ResponseCallback>) response -> {
+ @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.initValue(this.serviceImpl, convertToIdVerFromObjectId(request.getPath().toString(), registration));
+ lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
}
if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) {
- this.handleResponse(registration, request.getPath().toString(), response, request);
- if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest()) {
- LwM2mNode node = ((WriteRequest) request).getNode();
- Object value = this.converter.convertValue(((LwM2mSingleResource) node).getValue(),
- ((LwM2mSingleResource) node).getType(), ResourceModel.Type.STRING, request.getPath());
- String msg = String.format("%s: sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client",
- LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(),
- response.getCode().getName(), request.getPath().toString(), value);
- serviceImpl.sendLogsToThingsboard(msg, registration);
- log.debug("[{}] [{}] - [{}] [{}] Update SendRequest[{}]", registration.getEndpoint(),
- ((Response) response.getCoapResponse()).getCode(), response.getCode(),
- request.getPath().toString(), value);
- }
+ this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest);
} else {
- String msg = String.format("%s: sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", LOG_LW2M_ERROR,
+ String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(),
((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
- serviceImpl.sendLogsToThingsboard(msg, registration);
- log.error("[{}], [{}] - [{}] [{}] error SendRequest", registration.getEndpoint(), ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
+ serviceImpl.sendLogsToThingsboard(msg, registration.getId());
+ log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(),
+ ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
+ if (!lwM2MClient.isInit()) {
+ lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
+ }
+ if (rpcRequest != null) {
+ serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR);
+ }
+ /* Not Found
+ set setClient_fw_version = empty
+ */
+ if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) {
+ lwM2MClient.setUpdateFw(false);
+ lwM2MClient.getFrUpdate().setClientFwVersion("");
+ log.warn("updateFirmwareClient1");
+ serviceImpl.updateFirmwareClient(lwM2MClient);
+ }
}
}, e -> {
+ /* version == null
+ set setClient_fw_version = empty
+ */
+ if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) {
+ lwM2MClient.setUpdateFw(false);
+ lwM2MClient.getFrUpdate().setClientFwVersion("");
+ log.warn("updateFirmwareClient2");
+ serviceImpl.updateFirmwareClient(lwM2MClient);
+ }
if (!lwM2MClient.isInit()) {
- lwM2MClient.initValue(this.serviceImpl, convertToIdVerFromObjectId(request.getPath().toString(), registration));
+ lwM2MClient.initReadValue(this.serviceImpl, 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());
+ serviceImpl.sendLogsToThingsboard(msg, registration.getId());
+ log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString());
+ if (rpcRequest != null) {
+ serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
}
- String msg = String.format("%s: sendRequest: Resource path - %s msg error - %s SendRequest to Client",
- LOG_LW2M_ERROR, request.getPath().toString(), e.toString());
- serviceImpl.sendLogsToThingsboard(msg, registration);
- log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString());
});
-
}
- private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, Integer resourceId, Object value, ResourceModel.Type type, Registration registration) {
+ private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId,
+ Integer resourceId, Object value, ResourceModel.Type type,
+ Registration registration, Lwm2mClientRpcRequest rpcRequest) {
try {
- 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
- return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray()));
- default:
+ 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());
- serviceImpl.sendLogsToThingsboard(msg, registration);
+ serviceImpl.sendLogsToThingsboard(msg, registration.getId());
log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
+ if (rpcRequest != null) {
+ String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value);
+ serviceImpl.sentRpcRequest(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
+ }
return null;
}
}
- private void handleResponse(Registration registration, final String path, LwM2mResponse response, DownlinkRequest request) {
+ private void handleResponse(Registration registration, final String path, LwM2mResponse response,
+ DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
executorResponse.submit(() -> {
try {
- sendResponse(registration, path, response, request);
+ this.sendResponse(registration, path, response, request, rpcRequest);
} catch (Exception e) {
log.error("[{}] endpoint [{}] path [{}] Exception Unable to after send response.", registration.getEndpoint(), path, e);
}
@@ -282,27 +394,85 @@ public class LwM2mTransportRequest {
/**
* processing a response from a client
+ *
* @param registration -
- * @param path -
- * @param response -
+ * @param path -
+ * @param response -
*/
- private void sendResponse(Registration registration, String path, LwM2mResponse response, DownlinkRequest request) {
- String pathIdVer = convertToIdVerFromObjectId(path, registration);
+ private void sendResponse(Registration registration, String path, LwM2mResponse response,
+ DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
+ String pathIdVer = convertPathFromObjectIdToIdVer(path, registration);
if (response instanceof ReadResponse) {
- serviceImpl.onObservationResponse(registration, pathIdVer, (ReadResponse) response);
+ serviceImpl.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
} else if (response instanceof CancelObservationResponse) {
log.info("[{}] Path [{}] CancelObservationResponse 3_Send", pathIdVer, response);
+
} else if (response instanceof DeleteResponse) {
log.info("[{}] Path [{}] DeleteResponse 5_Send", pathIdVer, response);
} else if (response instanceof DiscoverResponse) {
- log.info("[{}] Path [{}] DiscoverResponse 6_Send", pathIdVer, response);
+ log.info("[{}] [{}] - [{}] [{}] Discovery value: [{}]", registration.getEndpoint(),
+ ((Response) response.getCoapResponse()).getCode(), response.getCode(),
+ request.getPath().toString(), ((DiscoverResponse) response).getObjectLinks());
+ if (rpcRequest != null) {
+ String discoveryMsg = String.format("%s",
+ Arrays.stream(((DiscoverResponse) response).getObjectLinks()).collect(Collectors.toSet()));
+ serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), discoveryMsg, LOG_LW2M_VALUE);
+ }
} else if (response instanceof ExecuteResponse) {
log.info("[{}] Path [{}] ExecuteResponse 7_Send", pathIdVer, response);
} else if (response instanceof WriteAttributesResponse) {
log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response);
} else if (response instanceof WriteResponse) {
- log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", pathIdVer, response);
+ log.info("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response);
+ this.infoWriteResponse(registration, response, request);
serviceImpl.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
}
+ if (rpcRequest != null) {
+ if (response instanceof ExecuteResponse
+ || response instanceof WriteAttributesResponse
+ || response instanceof DeleteResponse) {
+ rpcRequest.setInfoMsg(null);
+ serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null);
+ } else if (response instanceof WriteResponse) {
+ serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);
+ }
+ }
+ }
+
+ private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request) {
+ try {
+ LwM2mNode node = ((WriteRequest) request).getNode();
+ String msg;
+ Object value;
+ 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()));
+
+ } else {
+ valueLength = ((byte[]) singleResource.getValue()).length;
+ value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()),
+ Math.min(valueLength, config.getLogMaxLength())));
+ }
+ 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) {
+ serviceImpl.sendLogsToThingsboard(msg, registration.getId());
+ log.warn("[{}] [{}] [{}] - [{}] [{}] Update finished successfully: [{}]", request.getClass().getName(), registration.getEndpoint(),
+ ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString(), value);
+ }
+ } catch (Exception e) {
+ log.trace("Fail convert value from request to string. ", e);
+ }
}
}
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
new file mode 100644
index 0000000000..79902348a9
--- /dev/null
+++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java
@@ -0,0 +1,221 @@
+/**
+ * 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;
+/**
+ * Copyright © 2016-2020 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.
+ */
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.leshan.core.model.DDFFileParser;
+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.codec.CodecException;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.transport.TransportServiceCallback;
+import org.thingsboard.server.gen.transport.TransportProtos;
+import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
+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 java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.BOOLEAN_V;
+import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY;
+
+@Slf4j
+@Component
+@TbLwM2mTransportComponent
+@RequiredArgsConstructor
+public class LwM2mTransportServerHelper {
+
+ private final LwM2mTransportContext context;
+ private final LwM2MJsonAdaptor adaptor;
+
+ /**
+ * send to Thingsboard Attribute || Telemetry
+ *
+ * @param msg - JsonObject: [{name: value}]
+ * @return - dummy
+ */
+ private TransportServiceCallback getPubAckCallbackSendAttrTelemetry(final T msg) {
+ return new TransportServiceCallback<>() {
+ @Override
+ public void onSuccess(Void dummy) {
+ log.trace("Success to publish msg: {}, dummy: {}", msg, dummy);
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ log.trace("[{}] Failed to publish msg: {}", msg, e);
+ }
+ };
+ }
+
+ public void sendParametersOnThingsboardAttribute(List result, SessionInfoProto sessionInfo) {
+ PostAttributeMsg.Builder request = PostAttributeMsg.newBuilder();
+ request.addAllKv(result);
+ PostAttributeMsg postAttributeMsg = request.build();
+ TransportServiceCallback call = this.getPubAckCallbackSendAttrTelemetry(postAttributeMsg);
+ context.getTransportService().process(sessionInfo, postAttributeMsg, this.getPubAckCallbackSendAttrTelemetry(call));
+ }
+
+ public void sendParametersOnThingsboardTelemetry(List result, SessionInfoProto sessionInfo) {
+ PostTelemetryMsg.Builder request = PostTelemetryMsg.newBuilder();
+ TransportProtos.TsKvListProto.Builder builder = TransportProtos.TsKvListProto.newBuilder();
+ builder.setTs(System.currentTimeMillis());
+ builder.addAllKv(result);
+ request.addTsKvList(builder.build());
+ PostTelemetryMsg postTelemetryMsg = request.build();
+ TransportServiceCallback call = this.getPubAckCallbackSendAttrTelemetry(postTelemetryMsg);
+ context.getTransportService().process(sessionInfo, postTelemetryMsg, this.getPubAckCallbackSendAttrTelemetry(call));
+ }
+
+ /**
+ * @return - sessionInfo after access connect client
+ */
+ public SessionInfoProto getValidateSessionInfo(TransportProtos.ValidateDeviceCredentialsResponseMsg msg, long mostSignificantBits, long leastSignificantBits) {
+ return SessionInfoProto.newBuilder()
+ .setNodeId(context.getNodeId())
+ .setSessionIdMSB(mostSignificantBits)
+ .setSessionIdLSB(leastSignificantBits)
+ .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
+ .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
+ .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
+ .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
+ .setCustomerIdMSB(msg.getDeviceInfo().getCustomerIdMSB())
+ .setCustomerIdLSB(msg.getDeviceInfo().getCustomerIdLSB())
+ .setDeviceName(msg.getDeviceInfo().getDeviceName())
+ .setDeviceType(msg.getDeviceInfo().getDeviceType())
+ .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
+ .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
+ .build();
+ }
+
+ public ObjectModel parseFromXmlToObjectModel(byte[] xmlByte, String streamName, DefaultDDFFileValidator ddfValidator) {
+ try {
+ DDFFileParser ddfFileParser = new DDFFileParser(ddfValidator);
+ return ddfFileParser.parseEx(new ByteArrayInputStream(xmlByte), streamName).get(0);
+ } catch (IOException | InvalidDDFFileException e) {
+ log.error("Could not parse the XML file [{}]", streamName, e);
+ return null;
+ }
+ }
+
+ /**
+ *
+ * @param logMsg - info about Logs
+ * @return- KeyValueProto for telemetry (Logs)
+ */
+ public List getKvLogyToThingsboard(String logMsg) {
+ List result = new ArrayList<>();
+ result.add(TransportProtos.KeyValueProto.newBuilder()
+ .setKey(LOG_LW2M_TELEMETRY)
+ .setType(TransportProtos.KeyValueType.STRING_V)
+ .setStringV(logMsg).build());
+ return result;
+ }
+
+ /**
+ * @return - KeyValueProto for attribute/telemetry (change value)
+ * @throws CodecException -
+ */
+
+ public TransportProtos.KeyValueProto getKvAttrTelemetryToThingsboard(ResourceModel.Type resourceType, String resourceName, Object value, boolean isMultiInstances) {
+ TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(resourceName);
+ if (isMultiInstances) {
+ kvProto.setType(TransportProtos.KeyValueType.JSON_V)
+ .setJsonV((String) value);
+ } else {
+ switch (resourceType) {
+ case BOOLEAN:
+ kvProto.setType(BOOLEAN_V).setBoolV((Boolean) value).build();
+ break;
+ case STRING:
+ case TIME:
+ case OPAQUE:
+ case OBJLNK:
+ kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV((String) value);
+ break;
+ case INTEGER:
+ kvProto.setType(TransportProtos.KeyValueType.LONG_V).setLongV((Long) value);
+ break;
+ case FLOAT:
+ kvProto.setType(TransportProtos.KeyValueType.DOUBLE_V).setDoubleV((Double) value);
+ }
+ }
+ return kvProto.build();
+ }
+
+ /**
+ *
+ * @param currentType -
+ * @param resourcePath -
+ * @return
+ */
+ public static ResourceModel.Type getResourceModelTypeEqualsKvProtoValueType(ResourceModel.Type currentType, String resourcePath) {
+ switch (currentType) {
+ case BOOLEAN:
+ return ResourceModel.Type.BOOLEAN;
+ case STRING:
+ case TIME:
+ case OPAQUE:
+ case OBJLNK:
+ return ResourceModel.Type.STRING;
+ case INTEGER:
+ return ResourceModel.Type.INTEGER;
+ case FLOAT:
+ return ResourceModel.Type.FLOAT;
+ default:
+ }
+ throw new CodecException("Invalid ResourceModel_Type for resource %s, got %s", resourcePath, currentType);
+ }
+
+ public static Object getValueFromKvProto(TransportProtos.KeyValueProto kv) {
+ switch (kv.getType()) {
+ case BOOLEAN_V:
+ return kv.getBoolV();
+ case LONG_V:
+ return kv.getLongV();
+ case DOUBLE_V:
+ return kv.getDoubleV();
+ case STRING_V:
+ return kv.getStringV();
+ case JSON_V:
+ return kv.getJsonV();
+ }
+ return null;
+ }
+}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java
deleted file mode 100644
index 4fad90910a..0000000000
--- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerInitializer.java
+++ /dev/null
@@ -1,64 +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.extern.slf4j.Slf4j;
-import org.eclipse.leshan.server.californium.LeshanServer;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
-import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
-
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-
-@Slf4j
-@Component("LwM2MTransportServerInitializer")
-@TbLwM2mTransportComponent
-public class LwM2mTransportServerInitializer {
-
- @Autowired
- private LwM2mTransportServiceImpl service;
-
- @Autowired
- private LeshanServer leshanServer;
-
- @Autowired
- private LwM2mTransportContextServer context;
-
- @PostConstruct
- public void init() {
- if (this.context.getLwM2MTransportConfigServer().getEnableGenNewKeyPskRpk()) {
- new LWM2MGenerationPSkRPkECC();
- }
- this.startLhServer();
- }
-
- private void startLhServer() {
- this.leshanServer.start();
- LwM2mServerListener lhServerCertListener = new LwM2mServerListener(service);
- this.leshanServer.getRegistrationService().addListener(lhServerCertListener.registrationListener);
- this.leshanServer.getPresenceService().addListener(lhServerCertListener.presenceListener);
- this.leshanServer.getObservationService().addListener(lhServerCertListener.observationListener);
- }
-
- @PreDestroy
- public void shutdown() {
- log.info("Stopping LwM2M transport Server!");
- leshanServer.destroy();
- log.info("LwM2M transport Server stopped!");
- }
-}
diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java
deleted file mode 100644
index d863504b44..0000000000
--- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java
+++ /dev/null
@@ -1,1266 +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 com.fasterxml.jackson.core.type.TypeReference;
-import com.google.common.collect.Sets;
-import com.google.gson.Gson;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.reflect.TypeToken;
-import lombok.extern.slf4j.Slf4j;
-import org.eclipse.leshan.core.model.ResourceModel;
-import org.eclipse.leshan.core.node.LwM2mObject;
-import org.eclipse.leshan.core.node.LwM2mObjectInstance;
-import org.eclipse.leshan.core.node.LwM2mPath;
-import org.eclipse.leshan.core.node.LwM2mResource;
-import org.eclipse.leshan.core.observation.Observation;
-import org.eclipse.leshan.core.request.ContentFormat;
-import org.eclipse.leshan.core.request.WriteRequest;
-import org.eclipse.leshan.core.response.ReadResponse;
-import org.eclipse.leshan.core.util.NamedThreadFactory;
-import org.eclipse.leshan.server.californium.LeshanServer;
-import org.eclipse.leshan.server.registration.Registration;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Service;
-import org.thingsboard.common.util.JacksonUtil;
-import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.DeviceProfile;
-import org.thingsboard.server.common.transport.TransportService;
-import org.thingsboard.server.common.transport.adaptor.AdaptorException;
-import org.thingsboard.server.common.transport.adaptor.JsonConverter;
-import org.thingsboard.server.common.transport.service.DefaultTransportService;
-import org.thingsboard.server.gen.transport.TransportProtos;
-import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
-import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
-import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
-import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
-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.LwM2mClientProfile;
-import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters;
-import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
-
-import javax.annotation.PostConstruct;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Random;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
-import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
-import static org.thingsboard.server.common.transport.util.JsonUtils.getJsonObject;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_REQUEST;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_TOPIC;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_TELEMETRY_TOPIC;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_DISCOVER;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_OBSERVE;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_READ;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_TELEMETRY;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LWM2M_STRATEGY_2;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.POST_TYPE_OPER_EXECUTE;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.POST_TYPE_OPER_WRITE_REPLACE;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.PUT_TYPE_OPER_WRITE_ATTRIBUTES;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.SERVICE_CHANNEL;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToIdVerFromObjectId;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToObjectIdFromIdVer;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getAckCallback;
-import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.validateObjectVerFromKey;
-
-@Slf4j
-@Service
-@TbLwM2mTransportComponent
-public class LwM2mTransportServiceImpl implements LwM2mTransportService {
-
- private ExecutorService executorRegistered;
- private ExecutorService executorUpdateRegistered;
- private ExecutorService executorUnRegistered;
- private LwM2mValueConverterImpl converter;
-
- private final TransportService transportService;
-
- public final LwM2mTransportContextServer lwM2mTransportContextServer;
-
- private final LwM2mClientContext lwM2mClientContext;
-
- private final LeshanServer leshanServer;
-
- private final LwM2mTransportRequest lwM2mTransportRequest;
-
- public LwM2mTransportServiceImpl(TransportService transportService, LwM2mTransportContextServer lwM2mTransportContextServer, LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer, @Lazy LwM2mTransportRequest lwM2mTransportRequest) {
- this.transportService = transportService;
- this.lwM2mTransportContextServer = lwM2mTransportContextServer;
- this.lwM2mClientContext = lwM2mClientContext;
- this.leshanServer = leshanServer;
- this.lwM2mTransportRequest = lwM2mTransportRequest;
- }
-
- @PostConstruct
- public void init() {
- this.lwM2mTransportContextServer.getScheduler().scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) lwM2mTransportContextServer.getLwM2MTransportConfigServer().getSessionReportTimeout()), lwM2mTransportContextServer.getLwM2MTransportConfigServer().getSessionReportTimeout(), TimeUnit.MILLISECONDS);
- this.executorRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getRegisteredPoolSize(),
- new NamedThreadFactory(String.format("LwM2M %s channel registered", SERVICE_CHANNEL)));
- this.executorUpdateRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getUpdateRegisteredPoolSize(),
- new NamedThreadFactory(String.format("LwM2M %s channel update registered", SERVICE_CHANNEL)));
- this.executorUnRegistered = Executors.newFixedThreadPool(this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getUnRegisteredPoolSize(),
- new NamedThreadFactory(String.format("LwM2M %s channel un registered", SERVICE_CHANNEL)));
- this.converter = LwM2mValueConverterImpl.getInstance();
- }
-
- /**
- * Start registration device
- * Create session: Map, LwM2MClient>
- * 1. replaceNewRegistration -> (solving the problem of incorrect termination of the previous session with this endpoint)
- * 1.1 When we initialize the registration, we register the session by endpoint.
- * 1.2 If the server has incomplete requests (canceling the registration of the previous session),
- * delete the previous session only by the previous registration.getId
- * 1.2 Add Model (Entity) for client (from registration & observe) by registration.getId
- * 1.2 Remove from sessions Model by enpPoint
- * Next -> Create new LwM2MClient for current session -> setModelClient...
- *
- * @param registration - Registration LwM2M Client
- * @param previousObservations - may be null
- */
- public void onRegistered(Registration registration, Collection previousObservations) {
- executorRegistered.submit(() -> {
- try {
- log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId());
- LwM2mClient lwM2MClient = this.lwM2mClientContext.updateInSessionsLwM2MClient(registration);
- if (lwM2MClient != null) {
- SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
- if (sessionInfo != null) {
- this.initLwM2mClient(lwM2MClient, sessionInfo);
- transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo));
- transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
- transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
- this.initLwM2mFromClientValue(registration, lwM2MClient);
- this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration);
- } else {
- log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
- }
- } else {
- log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null);
- }
- } catch (Throwable t) {
- log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t);
- }
- });
- }
-
- /**
- * if sessionInfo removed from sessions, then new registerAsyncSession
- *
- * @param registration - Registration LwM2M Client
- */
- public void updatedReg(Registration registration) {
- executorUpdateRegistered.submit(() -> {
- try {
- SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
- if (sessionInfo != null) {
- this.checkInactivity(sessionInfo);
- LwM2mClient lwM2MClient = this.lwM2mClientContext.getLwM2MClient(sessionInfo);
- if (lwM2MClient.getDeviceId() == null && lwM2MClient.getProfileId() == null) {
- initLwM2mClient(lwM2MClient, sessionInfo);
- } else {
- if (registration.getBindingMode().useQueueMode()) {
- LwM2mQueuedRequest request;
- while ((request = lwM2MClient.getQueuedRequests().poll()) != null) {
- request.send();
- }
- }
- }
-
- log.info("Client: [{}] updatedReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
- } else {
- log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
- }
- } catch (Throwable t) {
- log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t);
- }
- });
- }
-
- /**
- * @param registration - Registration LwM2M Client
- * @param observations - All paths observations before unReg
- * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect
- */
- public void unReg(Registration registration, Collection observations) {
- executorUnRegistered.submit(() -> {
- try {
- this.setCancelObservations(registration);
- this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration);
- this.closeClientSession(registration);
- } catch (Throwable t) {
- log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
- }
- });
- }
-
- private void initLwM2mClient(LwM2mClient lwM2MClient, SessionInfoProto sessionInfo) {
- lwM2MClient.setDeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB()));
- lwM2MClient.setProfileId(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
- lwM2MClient.setDeviceName(sessionInfo.getDeviceName());
- lwM2MClient.setDeviceProfileName(sessionInfo.getDeviceType());
- }
-
- private void closeClientSession(Registration registration) {
- SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
- if (sessionInfo != null) {
- transportService.deregisterSession(sessionInfo);
- this.doCloseSession(sessionInfo);
- lwM2mClientContext.delRemoveSessionAndListener(registration.getId());
- if (lwM2mClientContext.getProfiles().size() > 0) {
- this.syncSessionsAndProfiles();
- }
- log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
- } else {
- log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
- }
- }
-
- @Override
- public void onSleepingDev(Registration registration) {
- log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint());
- this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration);
-
- //TODO: associate endpointId with device information.
- }
-
- @Override
- public void setCancelObservations(Registration registration) {
- if (registration != null) {
- Set observations = leshanServer.getObservationService().getObservations(registration);
- observations.forEach(observation -> this.setCancelObservationRecourse(registration, observation.getPath().toString()));
- }
- }
-
- /**
- * lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
- * At server side this will not remove the observation from the observation store, to do it you need to use
- * {@code ObservationService#cancelObservation()}
- */
- @Override
- public void setCancelObservationRecourse(Registration registration, String path) {
- leshanServer.getObservationService().cancelObservations(registration, path);
- }
-
- /**
- * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource
- *
- * @param registration - Registration LwM2M Client
- * @param path - observe
- * @param response - observe
- */
- @Override
- public void onObservationResponse(Registration registration, String path, ReadResponse response) {
- if (response.getContent() != null) {
- if (response.getContent() instanceof LwM2mObject) {
- LwM2mObject lwM2mObject = (LwM2mObject) response.getContent();
- this.updateObjectResourceValue(registration, lwM2mObject, path);
- } else if (response.getContent() instanceof LwM2mObjectInstance) {
- LwM2mObjectInstance lwM2mObjectInstance = (LwM2mObjectInstance) response.getContent();
- this.updateObjectInstanceResourceValue(registration, lwM2mObjectInstance, path);
- } else if (response.getContent() instanceof LwM2mResource) {
- LwM2mResource lwM2mResource = (LwM2mResource) response.getContent();
- this.updateResourcesValue(registration, lwM2mResource, path);
- }
- }
- }
-
- /**
- * Update - send request in change value resources in Client
- * Path to resources from profile equal keyName or from ModelObject equal name
- * Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)
- * Delete - nothing *
- *
- * @param msg -
- */
- @Override
- public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
- if (msg.getSharedUpdatedCount() > 0) {
- JsonElement el = JsonConverter.toJson(msg);
- el.getAsJsonObject().entrySet().forEach(de -> {
- String pathIdVer = this.getPathAttributeUpdate(sessionInfo, de.getKey());
- String value = de.getValue().getAsString();
- LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
- LwM2mClientProfile clientProfile = lwM2mClientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
- if (pathIdVer != null && !pathIdVer.isEmpty() && (this.validatePathInAttrProfile(clientProfile, pathIdVer) || this.validatePathInTelemetryProfile(clientProfile, pathIdVer))) {
- ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer);
- if (resourceModel != null && resourceModel.operations.isWritable()) {
- lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), pathIdVer, POST_TYPE_OPER_WRITE_REPLACE,
- ContentFormat.TLV.getName(), null, value, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout());
- } else {
- log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, value);
- String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated",
- LOG_LW2M_ERROR, pathIdVer, value);
- this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
- }
- } else {
- log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value);
- String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated",
- LOG_LW2M_ERROR, de.getKey(), value);
- this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
- }
- });
- } else if (msg.getSharedDeletedCount() > 0) {
- log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
- }
- }
-
- /**
- * @param sessionInfo -
- * @param deviceProfile -
- */
- @Override
- public void onDeviceProfileUpdate(SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
- Set registrationIds = lwM2mClientContext.getLwM2mClients().entrySet()
- .stream()
- .filter(e -> e.getValue().getProfileId().equals(deviceProfile.getUuidId()))
- .map(Map.Entry::getKey).sorted().collect(Collectors.toCollection(LinkedHashSet::new));
- if (registrationIds.size() > 0) {
- this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile);
- }
- }
-
- /**
- * @param sessionInfo -
- * @param device -
- * @param deviceProfileOpt -
- */
- @Override
- public void onDeviceUpdate(SessionInfoProto sessionInfo, Device device, Optional deviceProfileOpt) {
- Optional registrationIdOpt = lwM2mClientContext.getLwM2mClients().entrySet().stream()
- .filter(e -> device.getUuidId().equals(e.getValue().getDeviceId()))
- .map(Map.Entry::getKey)
- .findFirst();
- registrationIdOpt.ifPresent(registrationId -> this.onDeviceUpdateLwM2MClient(registrationId, device, deviceProfileOpt));
- }
-
- /**
- * @param resourceUpdateMsgOpt -
- */
- @Override
- public void onResourceUpdate(Optional resourceUpdateMsgOpt) {
- String idVer = resourceUpdateMsgOpt.get().getResourceKey();
- lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.updateResourceModel(idVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getModelProvider()));
- }
-
- /**
- * @param resourceDeleteMsgOpt -
- */
- @Override
- public void onResourceDelete(Optional resourceDeleteMsgOpt) {
- String pathIdVer = resourceDeleteMsgOpt.get().getResourceKey();
- lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.deleteResources(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getModelProvider()));
- }
-
- public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest) {
- log.info("[{}] toDeviceRpcRequest", toDeviceRequest);
- }
-
- 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, POST_TYPE_OPER_EXECUTE,
- ContentFormat.TLV.getName(), null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout());
- }
-
- /**
- * 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 -
- */
- protected void onAwakeDev(Registration registration) {
- log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
- this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration);
- //TODO: associate endpointId with device information.
- }
-
- /**
- * This method is used to sync with sessions
- * Removes a profile if not used in sessions
- */
- private void syncSessionsAndProfiles() {
- Map profilesClone = lwM2mClientContext.getProfiles().entrySet()
- .stream()
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
- profilesClone.forEach((k, v) -> {
- String registrationId = lwM2mClientContext.getLwM2mClients().entrySet()
- .stream()
- .filter(e -> e.getValue().getProfileId().equals(k))
- .findFirst()
- .map(Map.Entry::getKey) // return the key of the matching entry if found
- .orElse("");
- if (registrationId.isEmpty()) {
- lwM2mClientContext.getProfiles().remove(k);
- }
- });
- }
-
- /**
- * @param msg - text msg
- * @param registration - Id of Registration LwM2M Client
- */
- public void sendLogsToThingsboard(String msg, Registration registration) {
- if (msg != null) {
- JsonObject telemetries = new JsonObject();
- telemetries.addProperty(LOG_LW2M_TELEMETRY, msg);
- this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration);
- }
- }
-
-
- /**
- * // !!! Ok
- * Prepare send to Thigsboard callback - Attribute or Telemetry
- *
- * @param msg - JsonArray: [{name: value}]
- * @param topicName - Api Attribute or Telemetry
- * @param registration - Id of Registration LwM2M Client
- */
- public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) {
- SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
- if (sessionInfo != null) {
- lwM2mTransportContextServer.sendParametersOnThingsboard(msg, topicName, sessionInfo);
- } else {
- log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null);
- }
- }
-
- /**
- * #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 = lwM2mClientContext.getProfile(registration);
- Set clientObjects = lwM2mClientContext.getSupportedIdVerInClient(registration);
- if (clientObjects != null && clientObjects.size() > 0) {
- if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
- // #2
- lwM2MClient.getPendingRequests().addAll(clientObjects);
- clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
- null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout()));
- }
- // #1
- this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, GET_TYPE_OPER_READ, clientObjects);
- this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, GET_TYPE_OPER_OBSERVE, clientObjects);
- this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, PUT_TYPE_OPER_WRITE_ATTRIBUTES, clientObjects);
-// this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, GET_TYPE_OPER_DISCOVER, clientObjects);
- }
- }
-
- /**
- * @param registration -
- * @param lwM2mObject -
- * @param path -
- */
- private void updateObjectResourceValue(Registration registration, LwM2mObject lwM2mObject, String path) {
- LwM2mPath pathIds = new LwM2mPath(path);
- lwM2mObject.getInstances().forEach((instanceId, instance) -> {
- String pathInstance = pathIds.toString() + "/" + instanceId;
- this.updateObjectInstanceResourceValue(registration, instance, pathInstance);
- });
- }
-
- /**
- * @param registration -
- * @param lwM2mObjectInstance -
- * @param path -
- */
- private void updateObjectInstanceResourceValue(Registration registration, LwM2mObjectInstance lwM2mObjectInstance, String path) {
- LwM2mPath pathIds = new LwM2mPath(path);
- 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)
- *
- * @param registration - Registration LwM2M Client
- * @param lwM2mResource - LwM2mSingleResource response.getContent()
- * @param path - resource
- */
- private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) {
- LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
- if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getModelProvider())) {
- 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) {
- JsonObject attributes = new JsonObject();
- JsonObject telemetries = new JsonObject();
- try {
- this.getParametersFromProfile(attributes, telemetries, registration, paths);
- } catch (Exception e) {
- log.error("UpdateAttrTelemetry", e);
- }
- if (attributes.getAsJsonObject().entrySet().size() > 0)
- this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration);
- if (telemetries.getAsJsonObject().entrySet().size() > 0)
- this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration);
- }
-
- /**
- * @param clientProfile -
- * @param path -
- * @return true if path isPresent in postAttributeProfile
- */
- private boolean validatePathInAttrProfile(LwM2mClientProfile clientProfile, String path) {
- try {
- List attributesSet = new Gson().fromJson(clientProfile.getPostAttributeProfile(),
- new TypeToken>() {
- }.getType());
- return attributesSet.stream().anyMatch(p -> p.equals(path));
- } catch (Exception e) {
- log.error("Fail Validate Path [{}] ClientProfile.Attribute", path, e);
- return false;
- }
- }
-
- /**
- * @param clientProfile -
- * @param path -
- * @return true if path isPresent in postAttributeProfile
- */
- private boolean validatePathInTelemetryProfile(LwM2mClientProfile clientProfile, String path) {
- try {
- List telemetriesSet = new Gson().fromJson(clientProfile.getPostTelemetryProfile(), new TypeToken>() {
- }.getType());
- return telemetriesSet.stream().anyMatch(p -> p.equals(path));
- } catch (Exception e) {
- log.error("Fail Validate Path [{}] ClientProfile.Telemetry", path, e);
- return false;
- }
- }
-
- /**
- * Start observe/read: Attr/Telemetry
- * #1 - Analyze: path in resource profile == client resource
- *
- * @param registration -
- */
- private void initReadAttrTelemetryObserveToClient(Registration registration, LwM2mClient lwM2MClient,
- String typeOper, Set clientObjects) {
- LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration);
- Set result = null;
- ConcurrentHashMap params = null;
- if (GET_TYPE_OPER_READ.equals(typeOper)) {
- result = JacksonUtil.fromString(lwM2MClientProfile.getPostAttributeProfile().toString(),
- new TypeReference<>() {
- });
- result.addAll(JacksonUtil.fromString(lwM2MClientProfile.getPostTelemetryProfile().toString(),
- new TypeReference<>() {
- }));
- } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
- result = JacksonUtil.fromString(lwM2MClientProfile.getPostObserveProfile().toString(),
- new TypeReference<>() {
- });
- } else if (GET_TYPE_OPER_DISCOVER.equals(typeOper)) {
- result = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()).keySet();
- ;
- } else if (PUT_TYPE_OPER_WRITE_ATTRIBUTES.equals(typeOper)) {
- params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile());
- result = params.keySet();
- }
- if (!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.getPendingRequests().addAll(pathSend);
- ConcurrentHashMap finalParams = params;
- pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(),
- null, finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout()));
- if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
- lwM2MClient.initValue(this, null);
- }
- }
- }
- }
-
- private ConcurrentHashMap