...
|
...
|
@@ -15,6 +15,7 @@ |
15
|
15
|
*/
|
16
|
16
|
package org.thingsboard.server.transport.lwm2m.server;
|
17
|
17
|
|
|
18
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
18
|
19
|
import com.google.gson.Gson;
|
19
|
20
|
import com.google.gson.JsonArray;
|
20
|
21
|
import com.google.gson.JsonElement;
|
...
|
...
|
@@ -42,10 +43,9 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
42
|
43
|
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
|
43
|
44
|
import org.thingsboard.server.common.transport.service.DefaultTransportService;
|
44
|
45
|
import org.thingsboard.server.gen.transport.TransportProtos;
|
|
46
|
+import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
|
45
|
47
|
import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
|
46
|
48
|
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
|
47
|
|
-import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
|
48
|
|
-import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
|
49
|
49
|
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
|
50
|
50
|
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientProfile;
|
51
|
51
|
import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
|
...
|
...
|
@@ -54,6 +54,7 @@ import org.thingsboard.server.transport.lwm2m.server.secure.LwM2mInMemorySecurit |
54
|
54
|
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
|
55
|
55
|
|
56
|
56
|
import javax.annotation.PostConstruct;
|
|
57
|
+import java.io.IOException;
|
57
|
58
|
import java.util.ArrayList;
|
58
|
59
|
import java.util.Arrays;
|
59
|
60
|
import java.util.Collection;
|
...
|
...
|
@@ -152,6 +153,7 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { |
152
|
153
|
lwM2MClient.setSessionUuid(UUID.randomUUID());
|
153
|
154
|
this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client Registered", registration);
|
154
|
155
|
LwM2MClientProfile lwM2MClientProfile = lwM2mInMemorySecurityStore.getProfile(registration.getId());
|
|
156
|
+ this.putDelayedUpdateResourcesThingsboard(lwM2MClient);
|
155
|
157
|
this.setLwM2mFromClientValue(lwServer, registration, lwM2MClient, lwM2MClientProfile);
|
156
|
158
|
SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
|
157
|
159
|
if (sessionInfo != null) {
|
...
|
...
|
@@ -163,9 +165,9 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { |
163
|
165
|
transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null);
|
164
|
166
|
transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null);
|
165
|
167
|
this.sentLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration);
|
166
|
|
- if (LwM2MTransportHandler.getClientUpdateValueAfterConnect(lwM2MClientProfile)) {
|
167
|
|
- this.putDelayedUpdateResourcesThingsboard(lwM2MClient);
|
168
|
|
- }
|
|
168
|
+// if (LwM2MTransportHandler.getClientUpdateValueAfterConnect(lwM2MClientProfile)) {
|
|
169
|
+// this.putDelayedUpdateResourcesThingsboard(lwM2MClient);
|
|
170
|
+// }
|
169
|
171
|
} else {
|
170
|
172
|
log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
|
171
|
173
|
}
|
...
|
...
|
@@ -237,448 +239,271 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { |
237
|
239
|
//TODO: associate endpointId with device information.
|
238
|
240
|
}
|
239
|
241
|
|
240
|
|
- /**
|
241
|
|
- * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay,
|
242
|
|
- * * if you need to do long time processing use a dedicated thread pool.
|
243
|
|
- *
|
244
|
|
- * @param registration -
|
245
|
|
- */
|
246
|
|
- protected void onAwakeDev(Registration registration) {
|
247
|
|
- log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
|
248
|
|
- //TODO: associate endpointId with device information.
|
|
242
|
+ @Override
|
|
243
|
+ public void setCancelObservations(LeshanServer lwServer, Registration registration) {
|
|
244
|
+ if (registration != null) {
|
|
245
|
+ Set<Observation> observations = lwServer.getObservationService().getObservations(registration);
|
|
246
|
+ observations.forEach(observation -> this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString()));
|
|
247
|
+ }
|
249
|
248
|
}
|
250
|
249
|
|
251
|
250
|
/**
|
252
|
|
- * This method is used to sync with sessions
|
253
|
|
- * Removes a profile if not used in sessions
|
|
251
|
+ * lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
|
|
252
|
+ * At server side this will not remove the observation from the observation store, to do it you need to use
|
|
253
|
+ * {@code ObservationService#cancelObservation()}
|
254
|
254
|
*/
|
255
|
|
- private void syncSessionsAndProfiles() {
|
256
|
|
- Map<UUID, LwM2MClientProfile> profilesClone = lwM2mInMemorySecurityStore.getProfiles().entrySet()
|
257
|
|
- .stream()
|
258
|
|
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
259
|
|
- profilesClone.forEach((k, v) -> {
|
260
|
|
- String registrationId = lwM2mInMemorySecurityStore.getSessions().entrySet()
|
261
|
|
- .stream()
|
262
|
|
- .filter(e -> e.getValue().getProfileUuid().equals(k))
|
263
|
|
- .findFirst()
|
264
|
|
- .map(Map.Entry::getKey) // return the key of the matching entry if found
|
265
|
|
- .orElse("");
|
266
|
|
- if (registrationId.isEmpty()) {
|
267
|
|
- lwM2mInMemorySecurityStore.getProfiles().remove(k);
|
268
|
|
- }
|
269
|
|
- });
|
|
255
|
+ @Override
|
|
256
|
+ public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) {
|
|
257
|
+ lwServer.getObservationService().cancelObservations(registration, path);
|
270
|
258
|
}
|
271
|
259
|
|
272
|
260
|
/**
|
273
|
|
- * #0 Add new ObjectModel to context
|
274
|
|
- * Create new LwM2MClient for current session -> setModelClient...
|
275
|
|
- * if need all value after registration:
|
276
|
|
- * #1.1 Add all ObjectLinks (instance) to control the process of executing requests to the client
|
277
|
|
- * to get the client model with current values
|
278
|
|
- * if not need all value after registration (only observe)
|
279
|
|
- * #1.2 Get observe
|
280
|
|
- * #2 Get the client model with current values. Analyze the response in -> lwM2MTransportRequest.sendResponse
|
|
261
|
+ * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource
|
281
|
262
|
*
|
282
|
|
- * @param lwServer - LeshanServer
|
283
|
263
|
* @param registration - Registration LwM2M Client
|
284
|
|
- * @param lwM2MClient - object with All parameters off client
|
|
264
|
+ * @param path - observe
|
|
265
|
+ * @param response - observe
|
285
|
266
|
*/
|
286
|
|
- private void setLwM2mFromClientValue(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient, LwM2MClientProfile lwM2MClientProfile) {
|
287
|
|
- // #1.1
|
288
|
|
- // get all instances in client
|
289
|
|
- Set<String> clientInstances = this.getAllInstancesInClient(registration);
|
290
|
|
- if (clientInstances != null && LwM2MTransportHandler.getClientUpdateValueAfterConnect(lwM2MClientProfile)) {
|
291
|
|
- lwM2MClient.getPendingRequests().addAll(clientInstances);
|
292
|
|
- // #2
|
293
|
|
- clientInstances.forEach(path -> {
|
294
|
|
- lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
|
295
|
|
- lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false);
|
296
|
|
- });
|
297
|
|
- } else {
|
298
|
|
- // #1.2
|
299
|
|
- this.onSentObserveToClient(lwServer, registration);
|
300
|
|
- }
|
301
|
|
-
|
302
|
|
- }
|
303
|
|
-
|
304
|
|
- // get all instances in client
|
305
|
|
- private Set<String> getAllInstancesInClient(Registration registration) {
|
306
|
|
- Set<String> clientInstances = ConcurrentHashMap.newKeySet();
|
307
|
|
- Arrays.stream(registration.getObjectLinks()).forEach(url -> {
|
308
|
|
- LwM2mPath pathIds = new LwM2mPath(url.getUrl());
|
309
|
|
- if (pathIds.isObjectInstance() && !pathIds.isResource()) {
|
310
|
|
- clientInstances.add(url.getUrl());
|
|
267
|
+ @Override
|
|
268
|
+ public void onObservationResponse(Registration registration, String path, ReadResponse response) {
|
|
269
|
+ if (response.getContent() != null) {
|
|
270
|
+ if (response.getContent() instanceof LwM2mObject) {
|
|
271
|
+ LwM2mObject lwM2mObject = (LwM2mObject) response.getContent();
|
|
272
|
+ this.updateObjectResourceValue(registration, lwM2mObject, path);
|
|
273
|
+ } else if (response.getContent() instanceof LwM2mObjectInstance) {
|
|
274
|
+ LwM2mObjectInstance lwM2mObjectInstance = (LwM2mObjectInstance) response.getContent();
|
|
275
|
+ this.updateObjectInstanceResourceValue(registration, lwM2mObjectInstance, path);
|
|
276
|
+ } else if (response.getContent() instanceof LwM2mResource) {
|
|
277
|
+ LwM2mResource lwM2mResource = (LwM2mResource) response.getContent();
|
|
278
|
+ this.updateResourcesValue(registration, lwM2mResource, path);
|
311
|
279
|
}
|
312
|
|
- });
|
313
|
|
- return (clientInstances.size() > 0) ? clientInstances : null;
|
314
|
|
- }
|
315
|
|
-
|
316
|
|
- /**
|
317
|
|
- * @param registration - Registration LwM2M Client
|
318
|
|
- * @return - sessionInfo after access connect client
|
319
|
|
- */
|
320
|
|
- private SessionInfoProto getValidateSessionInfo(Registration registration) {
|
321
|
|
- LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
|
322
|
|
- return getNewSessionInfoProto(lwM2MClient);
|
323
|
|
-
|
|
280
|
+ }
|
324
|
281
|
}
|
325
|
282
|
|
326
|
283
|
/**
|
327
|
|
- * @param registrationId -
|
328
|
|
- * @return -
|
|
284
|
+ * Update - sent request in change value resources in Client
|
|
285
|
+ * Path to resources from profile equal keyName or from ModelObject equal name
|
|
286
|
+ * Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)
|
|
287
|
+ * Delete - nothing *
|
|
288
|
+ *
|
|
289
|
+ * @param msg -
|
329
|
290
|
*/
|
330
|
|
- private SessionInfoProto getValidateSessionInfo(String registrationId) {
|
331
|
|
- LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);
|
332
|
|
- return getNewSessionInfoProto(lwM2MClient);
|
333
|
|
- }
|
334
|
|
-
|
335
|
|
- private SessionInfoProto getNewSessionInfoProto(LwM2MClient lwM2MClient) {
|
336
|
|
- if (lwM2MClient != null) {
|
337
|
|
- ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse();
|
338
|
|
- if (msg == null || msg.getDeviceInfo() == null) {
|
339
|
|
- log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED);
|
340
|
|
- this.closeClientSession(lwM2MClient.getRegistration());
|
341
|
|
- return null;
|
342
|
|
- } else {
|
343
|
|
- return SessionInfoProto.newBuilder()
|
344
|
|
- .setNodeId(this.context.getNodeId())
|
345
|
|
- .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits())
|
346
|
|
- .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits())
|
347
|
|
- .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
|
348
|
|
- .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
|
349
|
|
- .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
|
350
|
|
- .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
|
351
|
|
- .setDeviceName(msg.getDeviceInfo().getDeviceName())
|
352
|
|
- .setDeviceType(msg.getDeviceInfo().getDeviceType())
|
353
|
|
- .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
|
354
|
|
- .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
|
355
|
|
- .build();
|
356
|
|
- }
|
|
291
|
+ @Override
|
|
292
|
+ public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
|
|
293
|
+ if (msg.getSharedUpdatedCount() > 0) {
|
|
294
|
+ JsonElement el = JsonConverter.toJson(msg);
|
|
295
|
+ el.getAsJsonObject().entrySet().forEach(de -> {
|
|
296
|
+ String path = this.getPathAttributeUpdate(sessionInfo, de.getKey());
|
|
297
|
+ String value = de.getValue().getAsString();
|
|
298
|
+ LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();
|
|
299
|
+ LwM2MClientProfile profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
|
|
300
|
+ ResourceModel resourceModel = context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path));
|
|
301
|
+ if (!path.isEmpty() && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) {
|
|
302
|
+ if (resourceModel != null && resourceModel.operations.isWritable()) {
|
|
303
|
+ lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
|
|
304
|
+ ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(),
|
|
305
|
+ false);
|
|
306
|
+ } else {
|
|
307
|
+ log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value);
|
|
308
|
+ String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value);
|
|
309
|
+ this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
|
|
310
|
+ }
|
|
311
|
+ } else {
|
|
312
|
+ log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value);
|
|
313
|
+ String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", de.getKey(), value);
|
|
314
|
+ this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
|
|
315
|
+ }
|
|
316
|
+ });
|
|
317
|
+ } else if (msg.getSharedDeletedCount() > 0) {
|
|
318
|
+ log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
|
357
|
319
|
}
|
358
|
|
- return null;
|
359
|
320
|
}
|
360
|
321
|
|
361
|
322
|
/**
|
362
|
|
- * Add attribute/telemetry information from Client and credentials/Profile to client model and start observe
|
363
|
|
- * !!! if the resource has an observation, but no telemetry or attribute - the observation will not use
|
364
|
|
- * #1 Sending Attribute Telemetry with value to thingsboard only once at the start of the connection
|
365
|
|
- * #2 Start observe
|
366
|
|
- *
|
367
|
|
- * @param lwM2MClient - LwM2M Client
|
|
323
|
+ * @param sessionInfo -
|
|
324
|
+ * @param deviceProfile -
|
368
|
325
|
*/
|
369
|
|
-
|
370
|
|
- public void updatesAndSentModelParameter(LwM2MClient lwM2MClient) {
|
371
|
|
- // #1
|
372
|
|
- this.updateAttrTelemetry(lwM2MClient.getRegistration(), true, null);
|
373
|
|
- // #2
|
374
|
|
- this.onSentObserveToClient(lwM2MClient.getLwServer(), lwM2MClient.getRegistration());
|
375
|
|
-
|
|
326
|
+ @Override
|
|
327
|
+ public void onDeviceProfileUpdate(SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
|
|
328
|
+ Set<String> registrationIds = lwM2mInMemorySecurityStore.getSessions().entrySet()
|
|
329
|
+ .stream()
|
|
330
|
+ .filter(e -> e.getValue().getProfileUuid().equals(deviceProfile.getUuidId()))
|
|
331
|
+ .map(Map.Entry::getKey).sorted().collect(Collectors.toCollection(LinkedHashSet::new));
|
|
332
|
+ if (registrationIds.size() > 0) {
|
|
333
|
+ this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile);
|
|
334
|
+ }
|
376
|
335
|
}
|
377
|
336
|
|
378
|
337
|
/**
|
379
|
|
- * If there is a difference in values between the current resource values and the shared attribute values
|
380
|
|
- * when the client connects to the server
|
381
|
|
- * #1 get attributes name from profile include name resources in ModelObject if resource isWritable
|
382
|
|
- * #2.1 #1 size > 0 => send Request getAttributes to thingsboard
|
383
|
|
- * #2.2 #1 size == 0 => continue normal process
|
384
|
|
- *
|
385
|
|
- * @param lwM2MClient - LwM2M Client
|
|
338
|
+ * @param sessionInfo -
|
|
339
|
+ * @param device -
|
|
340
|
+ * @param deviceProfileOpt -
|
386
|
341
|
*/
|
387
|
|
- public void putDelayedUpdateResourcesThingsboard(LwM2MClient lwM2MClient) {
|
388
|
|
- SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
|
389
|
|
- if (sessionInfo != null) {
|
390
|
|
- //#1.1 + #1.2
|
391
|
|
- List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient);
|
392
|
|
- if (attrSharedNames.size() > 0) {
|
393
|
|
- //#2.1
|
394
|
|
- try {
|
395
|
|
- TransportProtos.GetAttributeRequestMsg getAttributeMsg = context.getAdaptor().convertToGetAttributes(null, attrSharedNames);
|
396
|
|
- lwM2MClient.getDelayedRequestsId().add(getAttributeMsg.getRequestId());
|
397
|
|
- transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
|
398
|
|
- } catch (AdaptorException e) {
|
399
|
|
- log.warn("Failed to decode get attributes request", e);
|
400
|
|
- }
|
401
|
|
- }
|
402
|
|
- // #2.2
|
403
|
|
- else {
|
404
|
|
- lwM2MClient.onSuccessOrErrorDelayedRequests(null);
|
405
|
|
- }
|
406
|
|
- }
|
|
342
|
+ @Override
|
|
343
|
+ public void onDeviceUpdate(SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) {
|
|
344
|
+ Optional<String> registrationIdOpt = lwM2mInMemorySecurityStore.getSessions().entrySet().stream()
|
|
345
|
+ .filter(e -> device.getUuidId().equals(e.getValue().getDeviceUuid()))
|
|
346
|
+ .map(Map.Entry::getKey)
|
|
347
|
+ .findFirst();
|
|
348
|
+ registrationIdOpt.ifPresent(registrationId -> this.onDeviceUpdateLwM2MClient(registrationId, device, deviceProfileOpt));
|
407
|
349
|
}
|
408
|
350
|
|
409
|
351
|
/**
|
410
|
|
- * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values
|
411
|
|
- * #1 Get path resource by result attributesResponse
|
412
|
|
- * #1.1 If two names have equal path => last time attribute
|
413
|
|
- * #2.1 if there is a difference in values between the current resource values and the shared attribute values
|
414
|
|
- * => sent to client Request Update of value (new value from shared attribute)
|
415
|
|
- * and LwM2MClient.delayedRequests.add(path)
|
416
|
|
- * #2.1 if there is not a difference in values between the current resource values and the shared attribute values
|
|
352
|
+ * Trigger Server path = "/1/0/8"
|
417
|
353
|
*
|
418
|
|
- * @param attributesResponse -
|
419
|
|
- * @param sessionInfo -
|
|
354
|
+ * Trigger bootStrap path = "/1/0/9" - have to implemented on client
|
420
|
355
|
*/
|
421
|
|
- public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
|
422
|
|
- LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(sessionInfo);
|
423
|
|
- if (lwM2MClient.getDelayedRequestsId().contains(attributesResponse.getRequestId())) {
|
424
|
|
- attributesResponse.getSharedAttributeListList().forEach(attr -> {
|
425
|
|
- String path = this.getPathAttributeUpdate(sessionInfo, attr.getKv().getKey());
|
426
|
|
- // #1.1
|
427
|
|
- if (lwM2MClient.getDelayedRequests().containsKey(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) {
|
428
|
|
- lwM2MClient.getDelayedRequests().put(path, attr);
|
429
|
|
- } else {
|
430
|
|
- lwM2MClient.getDelayedRequests().put(path, attr);
|
431
|
|
- }
|
432
|
|
- });
|
433
|
|
- // #2.1
|
434
|
|
- lwM2MClient.getDelayedRequests().forEach((k, v) -> {
|
435
|
|
- ArrayList<TransportProtos.KeyValueProto> listV = new ArrayList<>();
|
436
|
|
- listV.add(v.getKv());
|
437
|
|
- this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k);
|
438
|
|
- });
|
439
|
|
- lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId());
|
440
|
|
- if (lwM2MClient.getDelayedRequests().size() == 0) {
|
441
|
|
- lwM2MClient.onSuccessOrErrorDelayedRequests(null);
|
442
|
|
- }
|
443
|
|
- }
|
444
|
|
- }
|
445
|
|
-
|
446
|
|
- private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) {
|
447
|
|
- if (valueNew != null && !valueNew.toString().equals(valueOld.toString())) {
|
448
|
|
- lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
|
449
|
|
- ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(),
|
450
|
|
- true);
|
451
|
|
- }
|
|
356
|
+ @Override
|
|
357
|
+ public void doTrigger(LeshanServer lwServer, Registration registration, String path) {
|
|
358
|
+ lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE,
|
|
359
|
+ ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),
|
|
360
|
+ false);
|
452
|
361
|
}
|
453
|
362
|
|
454
|
363
|
/**
|
455
|
|
- * Get names and keyNames from profile shared!!!! attr resources IsWritable
|
|
364
|
+ * Deregister session in transport
|
456
|
365
|
*
|
457
|
|
- * @param lwM2MClient -
|
458
|
|
- * @return ArrayList keyNames from profile attr resources shared!!!! && IsWritable
|
|
366
|
+ * @param sessionInfo - lwm2m client
|
459
|
367
|
*/
|
460
|
|
- private List<String> getNamesAttrFromProfileIsWritable(LwM2MClient lwM2MClient) {
|
461
|
|
- LwM2MClientProfile profile = lwM2mInMemorySecurityStore.getProfile(lwM2MClient.getProfileUuid());
|
462
|
|
- Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);
|
463
|
|
- ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), ConcurrentHashMap.class);
|
464
|
|
-
|
465
|
|
- ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()
|
466
|
|
- .stream()
|
467
|
|
- .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null &&
|
468
|
|
- context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable()))
|
469
|
|
- .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
|
470
|
|
-
|
471
|
|
- Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();
|
472
|
|
- namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));
|
473
|
|
- return new ArrayList<>(namesIsWritable);
|
|
368
|
+ @Override
|
|
369
|
+ public void doDisconnect(SessionInfoProto sessionInfo) {
|
|
370
|
+ transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null);
|
|
371
|
+ transportService.deregisterSession(sessionInfo);
|
474
|
372
|
}
|
475
|
373
|
|
476
|
|
-
|
477
|
374
|
/**
|
478
|
|
- * Sent Attribute and Telemetry to Thingsboard
|
479
|
|
- * #1 - get AttrName/TelemetryName with value:
|
480
|
|
- * #1.1 from Client
|
481
|
|
- * #1.2 from LwM2MClient:
|
482
|
|
- * -- resourceId == path from LwM2MClientProfile.postAttributeProfile/postTelemetryProfile/postObserveProfile
|
483
|
|
- * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId)
|
484
|
|
- * #2 - set Attribute/Telemetry
|
|
375
|
+ * Session device in thingsboard is closed
|
485
|
376
|
*
|
486
|
|
- * @param registration - Registration LwM2M Client
|
|
377
|
+ * @param sessionInfo - lwm2m client
|
487
|
378
|
*/
|
488
|
|
- private void updateAttrTelemetry(Registration registration, boolean start, Set<String> paths) {
|
489
|
|
- JsonObject attributes = new JsonObject();
|
490
|
|
- JsonObject telemetries = new JsonObject();
|
491
|
|
- if (start) {
|
492
|
|
- // #1.1
|
493
|
|
- JsonObject attributeClient = this.getAttributeClient(registration);
|
494
|
|
- if (attributeClient != null) {
|
495
|
|
- attributeClient.entrySet().forEach(p -> attributes.add(p.getKey(), p.getValue()));
|
496
|
|
- }
|
497
|
|
- }
|
498
|
|
- // #1.2
|
499
|
|
- try {
|
500
|
|
- writeLock.lock();
|
501
|
|
- this.getParametersFromProfile(attributes, telemetries, registration, paths);
|
502
|
|
- } catch (Exception e) {
|
503
|
|
- log.error("UpdateAttrTelemetry", e);
|
504
|
|
- } finally {
|
505
|
|
- writeLock.unlock();
|
506
|
|
- }
|
507
|
|
- if (attributes.getAsJsonObject().entrySet().size() > 0)
|
508
|
|
- this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration);
|
509
|
|
- if (telemetries.getAsJsonObject().entrySet().size() > 0)
|
510
|
|
- this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration);
|
|
379
|
+ private void doCloseSession(SessionInfoProto sessionInfo) {
|
|
380
|
+ TransportProtos.SessionEvent event = SessionEvent.CLOSED;
|
|
381
|
+ TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder()
|
|
382
|
+ .setSessionType(TransportProtos.SessionType.ASYNC)
|
|
383
|
+ .setEvent(event).build();
|
|
384
|
+ transportService.process(sessionInfo, msg, null);
|
511
|
385
|
}
|
512
|
386
|
|
513
|
387
|
/**
|
514
|
|
- * get AttrName/TelemetryName with value from Client
|
|
388
|
+ * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay,
|
|
389
|
+ * * if you need to do long time processing use a dedicated thread pool.
|
515
|
390
|
*
|
516
|
391
|
* @param registration -
|
517
|
|
- * @return - JsonObject, format: {name: value}}
|
518
|
392
|
*/
|
519
|
|
- private JsonObject getAttributeClient(Registration registration) {
|
520
|
|
- if (registration.getAdditionalRegistrationAttributes().size() > 0) {
|
521
|
|
- JsonObject resNameValues = new JsonObject();
|
522
|
|
- registration.getAdditionalRegistrationAttributes().forEach(resNameValues::addProperty);
|
523
|
|
- return resNameValues;
|
524
|
|
- }
|
525
|
|
- return null;
|
|
393
|
+ protected void onAwakeDev(Registration registration) {
|
|
394
|
+ log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint());
|
|
395
|
+ //TODO: associate endpointId with device information.
|
526
|
396
|
}
|
527
|
397
|
|
528
|
398
|
/**
|
529
|
|
- * @param attributes - new JsonObject
|
530
|
|
- * @param telemetry - new JsonObject
|
531
|
|
- * @param registration - Registration LwM2M Client
|
532
|
|
- * result: add to JsonObject those resources to which the user is subscribed and they have a value
|
533
|
|
- * if path==null add All resources else only one
|
534
|
|
- * (attributes/telemetry): new {name(Attr/Telemetry):value}
|
|
399
|
+ * This method is used to sync with sessions
|
|
400
|
+ * Removes a profile if not used in sessions
|
535
|
401
|
*/
|
536
|
|
- private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set<String> path) {
|
537
|
|
- LwM2MClientProfile lwM2MClientProfile = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid());
|
538
|
|
- lwM2MClientProfile.getPostAttributeProfile().forEach(p -> {
|
539
|
|
- LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
|
540
|
|
- if (pathIds.isResource()) {
|
541
|
|
- if (path == null || path.contains(p.getAsString())) {
|
542
|
|
- this.addParameters(p.getAsString().toString(), attributes, registration);
|
543
|
|
- }
|
544
|
|
- }
|
545
|
|
- });
|
546
|
|
- lwM2MClientProfile.getPostTelemetryProfile().forEach(p -> {
|
547
|
|
- LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
|
548
|
|
- if (pathIds.isResource()) {
|
549
|
|
- if (path == null || path.contains(p.getAsString())) {
|
550
|
|
- this.addParameters(p.getAsString().toString(), telemetry, registration);
|
551
|
|
- }
|
|
402
|
+ private void syncSessionsAndProfiles() {
|
|
403
|
+ Map<UUID, LwM2MClientProfile> profilesClone = lwM2mInMemorySecurityStore.getProfiles().entrySet()
|
|
404
|
+ .stream()
|
|
405
|
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
|
406
|
+ profilesClone.forEach((k, v) -> {
|
|
407
|
+ String registrationId = lwM2mInMemorySecurityStore.getSessions().entrySet()
|
|
408
|
+ .stream()
|
|
409
|
+ .filter(e -> e.getValue().getProfileUuid().equals(k))
|
|
410
|
+ .findFirst()
|
|
411
|
+ .map(Map.Entry::getKey) // return the key of the matching entry if found
|
|
412
|
+ .orElse("");
|
|
413
|
+ if (registrationId.isEmpty()) {
|
|
414
|
+ lwM2mInMemorySecurityStore.getProfiles().remove(k);
|
552
|
415
|
}
|
553
|
416
|
});
|
554
|
417
|
}
|
555
|
418
|
|
556
|
419
|
/**
|
557
|
|
- * @param parameters - JsonObject attributes/telemetry
|
558
|
|
- * @param registration - Registration LwM2M Client
|
559
|
|
- */
|
560
|
|
- private void addParameters(String path, JsonObject parameters, Registration registration) {
|
561
|
|
- LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registration.getId());
|
562
|
|
- JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()).getPostKeyNameProfile();
|
563
|
|
- String resName = String.valueOf(names.get(path));
|
564
|
|
- if (resName != null && !resName.isEmpty()) {
|
565
|
|
- try {
|
566
|
|
- String resValue = this.getResourceValueToString(lwM2MClient, path);
|
567
|
|
- if (resValue != null) {
|
568
|
|
- parameters.addProperty(resName, resValue);
|
569
|
|
- }
|
570
|
|
- } catch (Exception e) {
|
571
|
|
- log.error(e.getStackTrace().toString());
|
572
|
|
- }
|
573
|
|
- }
|
574
|
|
- }
|
575
|
|
-
|
576
|
|
- /**
|
577
|
|
- * Prepare Sent to Thigsboard callback - Attribute or Telemetry
|
578
|
|
- *
|
579
|
|
- * @param msg - JsonArray: [{name: value}]
|
580
|
|
- * @param topicName - Api Attribute or Telemetry
|
|
420
|
+ * @param msg - text msg
|
581
|
421
|
* @param registration - Id of Registration LwM2M Client
|
582
|
422
|
*/
|
583
|
|
- public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) {
|
584
|
|
- SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
|
585
|
|
- if (sessionInfo != null) {
|
586
|
|
- context.sentParametersOnThingsboard(msg, topicName, sessionInfo);
|
587
|
|
- } else {
|
588
|
|
- log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null);
|
589
|
|
- }
|
590
|
|
- }
|
591
|
|
-
|
592
|
|
- /**
|
593
|
|
- * Start observe
|
594
|
|
- * #1 - Analyze:
|
595
|
|
- * #1.1 path in observe == (attribute or telemetry)
|
596
|
|
- * #2 Analyze after sent request (response):
|
597
|
|
- * #2.1 First: lwM2MTransportRequest.sendResponse -> ObservationListener.newObservation
|
598
|
|
- * #2.2 Next: ObservationListener.onResponse *
|
599
|
|
- *
|
600
|
|
- * @param lwServer - LeshanServer
|
601
|
|
- * @param registration - Registration LwM2M Client
|
602
|
|
- */
|
603
|
|
- private void onSentObserveToClient(LeshanServer lwServer, Registration registration) {
|
604
|
|
- if (lwServer.getObservationService().getObservations(registration).size() > 0) {
|
605
|
|
- this.setCancelObservations(lwServer, registration);
|
606
|
|
- }
|
607
|
|
- LwM2MClientProfile lwM2MClientProfile = lwM2mInMemorySecurityStore.getProfile(registration.getId());
|
608
|
|
- Set<String> clientInstances = this.getAllInstancesInClient(registration);
|
609
|
|
- lwM2MClientProfile.getPostObserveProfile().forEach(p -> {
|
610
|
|
- // #1.1
|
611
|
|
- String target = p.getAsString().toString();
|
612
|
|
- String[] resPath = target.split("/");
|
613
|
|
- String instance = "/" + resPath[1] + "/" + resPath[2];
|
614
|
|
- if (clientInstances.contains(instance)) {
|
615
|
|
- // #2
|
616
|
|
-// if (this.getResourceValueToString(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()), target) != null) {
|
617
|
|
- lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE,
|
618
|
|
- null, null, null, null, this.context.getCtxServer().getTimeout(),
|
619
|
|
- false);
|
620
|
|
-// }
|
621
|
|
- }
|
622
|
|
- });
|
|
423
|
+ public void sentLogsToThingsboard(String msg, Registration registration) {
|
|
424
|
+ if (msg != null) {
|
|
425
|
+ JsonObject telemetries = new JsonObject();
|
|
426
|
+ telemetries.addProperty(LOG_LW2M_TELEMETRY, msg);
|
|
427
|
+ this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration);
|
|
428
|
+ }
|
623
|
429
|
}
|
624
|
430
|
|
625
|
|
- public void setCancelObservations(LeshanServer lwServer, Registration registration) {
|
626
|
|
- if (registration != null) {
|
627
|
|
- Set<Observation> observations = lwServer.getObservationService().getObservations(registration);
|
628
|
|
- observations.forEach(observation -> this.setCancelObservationRecourse(lwServer, registration, observation.getPath().toString()));
|
|
431
|
+
|
|
432
|
+ /**
|
|
433
|
+ * // !!! Ok
|
|
434
|
+ * Prepare Sent to Thigsboard callback - Attribute or Telemetry
|
|
435
|
+ *
|
|
436
|
+ * @param msg - JsonArray: [{name: value}]
|
|
437
|
+ * @param topicName - Api Attribute or Telemetry
|
|
438
|
+ * @param registration - Id of Registration LwM2M Client
|
|
439
|
+ */
|
|
440
|
+ public void updateParametersOnThingsboard(JsonElement msg, String topicName, Registration registration) {
|
|
441
|
+ SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration);
|
|
442
|
+ if (sessionInfo != null) {
|
|
443
|
+ context.sentParametersOnThingsboard(msg, topicName, sessionInfo);
|
|
444
|
+ } else {
|
|
445
|
+ log.error("Client: [{}] updateParametersOnThingsboard [{}] sessionInfo ", registration, null);
|
629
|
446
|
}
|
630
|
447
|
}
|
631
|
448
|
|
632
|
449
|
/**
|
633
|
|
- * lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
|
634
|
|
- * At server side this will not remove the observation from the observation store, to do it you need to use
|
635
|
|
- * {@code ObservationService#cancelObservation()}
|
|
450
|
+ * #1 сlientOnlyObserveAfterConnect == true
|
|
451
|
+ * - Only Observe Request to the client marked as observe from the profile configuration.
|
|
452
|
+ * #2. сlientOnlyObserveAfterConnect == false & clientUpdateValueAfterConnect == false
|
|
453
|
+ * - Request to the client after registration to read the values of the resources marked as attribute or telemetry from the profile configuration.
|
|
454
|
+ * - then Observe Request to the client marked as observe from the profile configuration.
|
|
455
|
+ * #3. сlientOnlyObserveAfterConnect == false & clientUpdateValueAfterConnect == true
|
|
456
|
+ * После регистрации отправляю запрос на read всех ресурсов, котрые послк регистрации, а затем запрос на observe (edited)
|
|
457
|
+ * - Request to the client after registration to read all resource values for all objects
|
|
458
|
+ * - then Observe Request to the client marked as observe from the profile configuration.
|
|
459
|
+ *
|
|
460
|
+ * @param lwServer - LeshanServer
|
|
461
|
+ * @param registration - Registration LwM2M Client
|
|
462
|
+ * @param lwM2MClient - object with All parameters off client
|
636
|
463
|
*/
|
637
|
|
- public void setCancelObservationRecourse(LeshanServer lwServer, Registration registration, String path) {
|
638
|
|
- lwServer.getObservationService().cancelObservations(registration, path);
|
|
464
|
+ private void setLwM2mFromClientValue(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient, LwM2MClientProfile lwM2MClientProfile) {
|
|
465
|
+ Set<String> clientObjects = this.getAllOjectsInClient(registration);
|
|
466
|
+ if (clientObjects != null) {
|
|
467
|
+ // #2
|
|
468
|
+ if (!LwM2MTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile) && !LwM2MTransportHandler.getClientUpdateValueAfterConnect(lwM2MClientProfile)) {
|
|
469
|
+ this.onSentReadAttrTelemetryToClient(lwServer, registration);
|
|
470
|
+ }
|
|
471
|
+ // #3
|
|
472
|
+ else {
|
|
473
|
+ clientObjects.forEach(path -> {
|
|
474
|
+ lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
|
|
475
|
+ lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false);
|
|
476
|
+ });
|
|
477
|
+ }
|
|
478
|
+ }
|
|
479
|
+ // #1
|
|
480
|
+ this.onSentObserveToClient(lwServer, registration);
|
639
|
481
|
}
|
640
|
482
|
|
641
|
483
|
/**
|
642
|
|
- * @param parameters - JsonArray postAttributeProfile/postTelemetryProfile
|
643
|
|
- * @param path - recourse from postObserveProfile
|
644
|
|
- * @return rez - true if path observe is in attribute/telemetry
|
|
484
|
+ * @param registration -
|
|
485
|
+ * @param lwM2mObject -
|
|
486
|
+ * @param path -
|
645
|
487
|
*/
|
646
|
|
-// private boolean getValidateObserve(JsonElement parameters, String path) {
|
647
|
|
-// AtomicBoolean rez = new AtomicBoolean(false);
|
648
|
|
-// if (parameters.isJsonArray()) {
|
649
|
|
-// parameters.getAsJsonArray().forEach(p -> {
|
650
|
|
-// if (p.getAsString().toString().equals(path)) rez.set(true);
|
651
|
|
-// }
|
652
|
|
-// );
|
653
|
|
-// } else if (parameters.isJsonObject()) {
|
654
|
|
-// rez.set((parameters.getAsJsonObject().entrySet()).stream().map(json -> json.toString())
|
655
|
|
-// .filter(path::equals).findAny().orElse(null) != null);
|
656
|
|
-// }
|
657
|
|
-// return rez.get();
|
658
|
|
-// }
|
|
488
|
+ private void updateObjectResourceValue(Registration registration, LwM2mObject lwM2mObject, String path) {
|
|
489
|
+ LwM2mPath pathIds = new LwM2mPath(path);
|
|
490
|
+ lwM2mObject.getInstances().forEach((instanceId, instance) -> {
|
|
491
|
+ String pathInstance = pathIds.toString() + "/" + instanceId;
|
|
492
|
+ this.updateObjectInstanceResourceValue(registration, instance, pathInstance);
|
|
493
|
+ });
|
|
494
|
+ }
|
659
|
495
|
|
660
|
496
|
/**
|
661
|
|
- * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource
|
662
|
|
- *
|
663
|
|
- * @param registration - Registration LwM2M Client
|
664
|
|
- * @param path - observe
|
665
|
|
- * @param response - observe
|
|
497
|
+ * @param registration -
|
|
498
|
+ * @param lwM2mObjectInstance -
|
|
499
|
+ * @param path -
|
666
|
500
|
*/
|
667
|
|
-
|
668
|
|
- public void onObservationResponse(Registration registration, String path, ReadResponse response) {
|
669
|
|
- if (response.getContent() != null) {
|
670
|
|
- if (response.getContent() instanceof LwM2mObject) {
|
671
|
|
- LwM2mObject lwM2mObject = (LwM2mObject) response.getContent();
|
672
|
|
- } else if (response.getContent() instanceof LwM2mObjectInstance) {
|
673
|
|
- LwM2mObjectInstance lwM2mObjectInstance = (LwM2mObjectInstance) response.getContent();
|
674
|
|
- } else if (response.getContent() instanceof LwM2mResource) {
|
675
|
|
- LwM2mResource lwM2mResource = (LwM2mResource) response.getContent();
|
676
|
|
- this.onObservationSetResourcesValue(registration, lwM2mResource, path);
|
677
|
|
-// } else if (response.getContent() instanceof LwM2mMultipleResource) {
|
678
|
|
-// LwM2mMultipleResource resource = (LwM2mMultipleResource) response.getContent();
|
679
|
|
-// this.onObservationSetResourcesValue(registration, null, resource.getValues(), path);
|
680
|
|
- }
|
681
|
|
- }
|
|
501
|
+ private void updateObjectInstanceResourceValue(Registration registration, LwM2mObjectInstance lwM2mObjectInstance, String path) {
|
|
502
|
+ LwM2mPath pathIds = new LwM2mPath(path);
|
|
503
|
+ lwM2mObjectInstance.getResources().forEach((resourceId, resource) -> {
|
|
504
|
+ String pathRez = pathIds.toString() + "/" + resourceId;
|
|
505
|
+ this.updateResourcesValue(registration, resource, pathRez);
|
|
506
|
+ });
|
682
|
507
|
}
|
683
|
508
|
|
684
|
509
|
/**
|
...
|
...
|
@@ -687,77 +512,53 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { |
687
|
512
|
* #2 Update new Resources (replace old Resource Value on new Resource Value)
|
688
|
513
|
*
|
689
|
514
|
* @param registration - Registration LwM2M Client
|
690
|
|
- * @param - LwM2mSingleResource response.getContent()
|
691
|
|
- * @param - LwM2mSingleResource response.getContent()
|
|
515
|
+ * @param - LwM2mSingleResource response.getContent()
|
|
516
|
+ * @param - LwM2mSingleResource response.getContent()
|
692
|
517
|
* @param path - resource
|
693
|
518
|
*/
|
694
|
|
- private void onObservationSetResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) {
|
|
519
|
+ private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) {
|
695
|
520
|
LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
|
696
|
521
|
lwM2MClient.updateResourceValue(path, lwM2mResource);
|
697
|
|
- log.warn("upDateResize: [{}] [{}] [{}]", lwM2MClient.getEndPoint(), lwM2MClient.getResources().size(), path);
|
|
522
|
+ log.warn("upDateResize: [{}] [{}]", lwM2MClient.getEndPoint(), path);
|
698
|
523
|
Set<String> paths = new HashSet<>();
|
699
|
524
|
paths.add(path);
|
700
|
525
|
this.updateAttrTelemetry(registration, false, paths);
|
701
|
526
|
}
|
702
|
527
|
|
703
|
528
|
/**
|
704
|
|
- * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...)
|
705
|
|
- * config attr/telemetry... in profile
|
706
|
|
- */
|
707
|
|
- public void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto updateCredentials) {
|
708
|
|
- log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList());
|
709
|
|
- }
|
710
|
|
-
|
711
|
|
- /**
|
712
|
|
- * Update - sent request in change value resources in Client
|
713
|
|
- * Path to resources from profile equal keyName or from ModelObject equal name
|
714
|
|
- * Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)
|
715
|
|
- * Delete - nothing *
|
|
529
|
+ * Sent Attribute and Telemetry to Thingsboard
|
|
530
|
+ * #1 - get AttrName/TelemetryName with value:
|
|
531
|
+ * #1.1 from Client
|
|
532
|
+ * #1.2 from LwM2MClient:
|
|
533
|
+ * -- resourceId == path from LwM2MClientProfile.postAttributeProfile/postTelemetryProfile/postObserveProfile
|
|
534
|
+ * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId)
|
|
535
|
+ * #2 - set Attribute/Telemetry
|
716
|
536
|
*
|
717
|
|
- * @param msg -
|
|
537
|
+ * @param registration - Registration LwM2M Client
|
718
|
538
|
*/
|
719
|
|
- public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
|
720
|
|
- if (msg.getSharedUpdatedCount() > 0) {
|
721
|
|
- JsonElement el = JsonConverter.toJson(msg);
|
722
|
|
- el.getAsJsonObject().entrySet().forEach(de -> {
|
723
|
|
- String path = this.getPathAttributeUpdate(sessionInfo, de.getKey());
|
724
|
|
- String value = de.getValue().getAsString();
|
725
|
|
- LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSession(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).entrySet().iterator().next().getValue();
|
726
|
|
- LwM2MClientProfile profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
|
727
|
|
- ResourceModel resourceModel = context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(path));
|
728
|
|
- if (!path.isEmpty() && (this.validatePathInAttrProfile(profile, path) || this.validatePathInTelemetryProfile(profile, path))) {
|
729
|
|
- if (resourceModel != null && resourceModel.operations.isWritable()) {
|
730
|
|
- lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
|
731
|
|
- ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(),
|
732
|
|
- false);
|
733
|
|
- } else {
|
734
|
|
- log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value);
|
735
|
|
- String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value);
|
736
|
|
- this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
|
737
|
|
- }
|
738
|
|
- } else {
|
739
|
|
- log.error("Attribute name - [{}] value - [{}] is not present as attribute in profile and cannot be updated", de.getKey(), value);
|
740
|
|
- String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", de.getKey(), value);
|
741
|
|
- this.sentLogsToThingsboard(logMsg, lwM2MClient.getRegistration());
|
742
|
|
- }
|
743
|
|
- });
|
744
|
|
- } else if (msg.getSharedDeletedCount() > 0) {
|
745
|
|
- log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
|
|
539
|
+ private void updateAttrTelemetry(Registration registration, boolean start, Set<String> paths) {
|
|
540
|
+ JsonObject attributes = new JsonObject();
|
|
541
|
+ JsonObject telemetries = new JsonObject();
|
|
542
|
+ if (start) {
|
|
543
|
+ // #1.1
|
|
544
|
+ JsonObject attributeClient = this.getAttributeClient(registration);
|
|
545
|
+ if (attributeClient != null) {
|
|
546
|
+ attributeClient.entrySet().forEach(p -> attributes.add(p.getKey(), p.getValue()));
|
|
547
|
+ }
|
746
|
548
|
}
|
747
|
|
- }
|
748
|
|
-
|
749
|
|
- /**
|
750
|
|
- * Get path to resource from profile equal keyName or from ModelObject equal name
|
751
|
|
- * Only for resource: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)
|
752
|
|
- *
|
753
|
|
- * @param sessionInfo -
|
754
|
|
- * @param name -
|
755
|
|
- * @return path if path isPresent in postProfile
|
756
|
|
- */
|
757
|
|
- private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) {
|
758
|
|
- String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name);
|
759
|
|
-// return !profilePath.isEmpty() ? profilePath : this.getPathAttributeUpdateModelObject(name);
|
760
|
|
- return !profilePath.isEmpty() ? profilePath : null;
|
|
549
|
+ // #1.2
|
|
550
|
+ try {
|
|
551
|
+ writeLock.lock();
|
|
552
|
+ this.getParametersFromProfile(attributes, telemetries, registration, paths);
|
|
553
|
+ } catch (Exception e) {
|
|
554
|
+ log.error("UpdateAttrTelemetry", e);
|
|
555
|
+ } finally {
|
|
556
|
+ writeLock.unlock();
|
|
557
|
+ }
|
|
558
|
+ if (attributes.getAsJsonObject().entrySet().size() > 0)
|
|
559
|
+ this.updateParametersOnThingsboard(attributes, DEVICE_ATTRIBUTES_TOPIC, registration);
|
|
560
|
+ if (telemetries.getAsJsonObject().entrySet().size() > 0)
|
|
561
|
+ this.updateParametersOnThingsboard(telemetries, DEVICE_TELEMETRY_TOPIC, registration);
|
761
|
562
|
}
|
762
|
563
|
|
763
|
564
|
/**
|
...
|
...
|
@@ -780,85 +581,194 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { |
780
|
581
|
return telemetriesSet.stream().filter(p -> p.equals(path)).findFirst().isPresent();
|
781
|
582
|
}
|
782
|
583
|
|
|
584
|
+ /**
|
|
585
|
+ * Start observe
|
|
586
|
+ * #1 - Analyze:
|
|
587
|
+ * #1.1 path in observe profile == client resource
|
|
588
|
+ *
|
|
589
|
+ * @param lwServer - LeshanServer
|
|
590
|
+ * @param registration - Registration LwM2M Client
|
|
591
|
+ */
|
|
592
|
+ private void onSentObserveToClient(LeshanServer lwServer, Registration registration) {
|
|
593
|
+ if (lwServer.getObservationService().getObservations(registration).size() > 0) {
|
|
594
|
+ this.setCancelObservations(lwServer, registration);
|
|
595
|
+ }
|
|
596
|
+ LwM2MClientProfile lwM2MClientProfile = lwM2mInMemorySecurityStore.getProfile(registration.getId());
|
|
597
|
+ Set<String> clientInstances = this.getAllInstancesInClient(registration);
|
|
598
|
+ lwM2MClientProfile.getPostObserveProfile().forEach(p -> {
|
|
599
|
+ // #1.1
|
|
600
|
+ String target = p.getAsString().toString();
|
|
601
|
+ String[] resPath = target.split("/");
|
|
602
|
+ String instance = "/" + resPath[1] + "/" + resPath[2];
|
|
603
|
+ if (clientInstances.contains(instance)) {
|
|
604
|
+ lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_OBSERVE,
|
|
605
|
+ null, null, null, null, this.context.getCtxServer().getTimeout(),
|
|
606
|
+ false);
|
|
607
|
+ }
|
|
608
|
+ });
|
|
609
|
+ }
|
783
|
610
|
|
784
|
611
|
/**
|
785
|
|
- * Get path to resource from profile equal keyName
|
|
612
|
+ * Update parameters device in LwM2MClient
|
|
613
|
+ * If new deviceProfile != old deviceProfile => update deviceProfile
|
786
|
614
|
*
|
787
|
|
- * @param sessionInfo -
|
788
|
|
- * @param name -
|
789
|
|
- * @return -
|
|
615
|
+ * @param registrationId -
|
|
616
|
+ * @param device -
|
790
|
617
|
*/
|
791
|
|
- private String getPathAttributeUpdateProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
|
792
|
|
- LwM2MClientProfile profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
|
793
|
|
- return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
|
794
|
|
- .filter(e -> e.getValue().getAsString().equals(name)).findFirst().map(Map.Entry::getKey)
|
795
|
|
- .orElse("");
|
|
618
|
+ private void onDeviceUpdateLwM2MClient(String registrationId, Device device, Optional<DeviceProfile> deviceProfileOpt) {
|
|
619
|
+ LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registrationId);
|
|
620
|
+ lwM2MClient.setDeviceName(device.getName());
|
|
621
|
+ if (!lwM2MClient.getProfileUuid().equals(device.getDeviceProfileId().getId())) {
|
|
622
|
+ Set<String> registrationIds = new HashSet<>();
|
|
623
|
+ registrationIds.add(registrationId);
|
|
624
|
+ deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile));
|
|
625
|
+ }
|
|
626
|
+
|
|
627
|
+ lwM2MClient.setProfileUuid(device.getDeviceProfileId().getId());
|
796
|
628
|
}
|
797
|
629
|
|
798
|
630
|
/**
|
799
|
|
- * Update resource (attribute) value on thingsboard after update value in client
|
|
631
|
+ * @param registration -
|
|
632
|
+ * @return - all object in client
|
|
633
|
+ */
|
|
634
|
+ private Set<String> getAllOjectsInClient(Registration registration) {
|
|
635
|
+ Set<String> clientObjects = ConcurrentHashMap.newKeySet();
|
|
636
|
+ Arrays.stream(registration.getObjectLinks()).forEach(url -> {
|
|
637
|
+ LwM2mPath pathIds = new LwM2mPath(url.getUrl());
|
|
638
|
+ if (pathIds.isObjectInstance()) {
|
|
639
|
+ clientObjects.add("/" + pathIds.getObjectId());
|
|
640
|
+ }
|
|
641
|
+ });
|
|
642
|
+ return (clientObjects.size() > 0) ? clientObjects : null;
|
|
643
|
+ }
|
|
644
|
+
|
|
645
|
+ /**
|
|
646
|
+ * @param registration -
|
|
647
|
+ * @return all instances in client
|
|
648
|
+ */
|
|
649
|
+ private Set<String> getAllInstancesInClient(Registration registration) {
|
|
650
|
+ Set<String> clientInstances = ConcurrentHashMap.newKeySet();
|
|
651
|
+ Arrays.stream(registration.getObjectLinks()).forEach(url -> {
|
|
652
|
+ LwM2mPath pathIds = new LwM2mPath(url.getUrl());
|
|
653
|
+ if (pathIds.isObjectInstance()) {
|
|
654
|
+ clientInstances.add(url.getUrl());
|
|
655
|
+ }
|
|
656
|
+ });
|
|
657
|
+ return (clientInstances.size() > 0) ? clientInstances : null;
|
|
658
|
+ }
|
|
659
|
+
|
|
660
|
+ private void onSentReadAttrTelemetryToClient(LeshanServer lwServer, Registration registration) {
|
|
661
|
+ LwM2MClientProfile lwM2MClientProfile = lwM2mInMemorySecurityStore.getProfile(registration.getId());
|
|
662
|
+ Set<String> clientInstances = this.getAllInstancesInClient(registration);
|
|
663
|
+ Set<String> attr = ConcurrentHashMap.newKeySet();
|
|
664
|
+ try {
|
|
665
|
+ attr = new ObjectMapper().readValue(lwM2MClientProfile.getPostAttributeProfile().getAsJsonArray().toString().getBytes(), Set.class);
|
|
666
|
+ attr.addAll(new ObjectMapper().readValue(lwM2MClientProfile.getPostTelemetryProfile().getAsJsonArray().toString().getBytes(), Set.class));
|
|
667
|
+ } catch (IOException e) {
|
|
668
|
+ e.printStackTrace();
|
|
669
|
+ }
|
|
670
|
+ attr.forEach(p -> {
|
|
671
|
+ // #1.1
|
|
672
|
+ String target = p;
|
|
673
|
+ String[] resPath = target.split("/");
|
|
674
|
+ String instance = "/" + resPath[1] + "/" + resPath[2];
|
|
675
|
+ if (clientInstances.contains(instance)) {
|
|
676
|
+ lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
|
|
677
|
+ null, null, null, this.context.getCtxServer().getTimeout(), false);
|
|
678
|
+ }
|
|
679
|
+ });
|
|
680
|
+ }
|
|
681
|
+
|
|
682
|
+ /**
|
|
683
|
+ * get AttrName/TelemetryName with value from Client
|
800
|
684
|
*
|
801
|
685
|
* @param registration -
|
802
|
|
- * @param path -
|
803
|
|
- * @param request -
|
|
686
|
+ * @return - JsonObject, format: {name: value}}
|
804
|
687
|
*/
|
805
|
|
- public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) {
|
806
|
|
-// ResourceModel resource = context.getCtxServer().getResourceModel(registration, new LwM2mPath(path));
|
807
|
|
-// if (resource.multiple) {
|
808
|
|
- this.onObservationSetResourcesValue(registration, ((LwM2mResource) request.getNode()), path);
|
809
|
|
-// } else {
|
810
|
|
-// this.onObservationSetResourcesValue(registration, ((LwM2mSingleResource) request.getNode()).getValue(), null, path);
|
811
|
|
-// }
|
812
|
|
- if (isDelayedUpdate) {
|
813
|
|
- lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null)
|
814
|
|
- .onSuccessOrErrorDelayedRequests(request.getPath().toString());
|
|
688
|
+ private JsonObject getAttributeClient(Registration registration) {
|
|
689
|
+ if (registration.getAdditionalRegistrationAttributes().size() > 0) {
|
|
690
|
+ JsonObject resNameValues = new JsonObject();
|
|
691
|
+ registration.getAdditionalRegistrationAttributes().forEach(resNameValues::addProperty);
|
|
692
|
+ return resNameValues;
|
815
|
693
|
}
|
|
694
|
+ return null;
|
|
695
|
+ }
|
|
696
|
+
|
|
697
|
+ /**
|
|
698
|
+ * @param attributes - new JsonObject
|
|
699
|
+ * @param telemetry - new JsonObject
|
|
700
|
+ * @param registration - Registration LwM2M Client
|
|
701
|
+ * @param path
|
|
702
|
+ */
|
|
703
|
+ private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set<String> path) {
|
|
704
|
+ LwM2MClientProfile lwM2MClientProfile = lwM2mInMemorySecurityStore.getProfile(registration.getId());
|
|
705
|
+ lwM2MClientProfile.getPostAttributeProfile().forEach(p -> {
|
|
706
|
+ LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
|
|
707
|
+ if (pathIds.isResource()) {
|
|
708
|
+ if (path == null || path.contains(p.getAsString())) {
|
|
709
|
+ this.addParameters(p.getAsString().toString(), attributes, registration);
|
|
710
|
+ }
|
|
711
|
+ }
|
|
712
|
+ });
|
|
713
|
+ lwM2MClientProfile.getPostTelemetryProfile().forEach(p -> {
|
|
714
|
+ LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
|
|
715
|
+ if (pathIds.isResource()) {
|
|
716
|
+ if (path == null || path.contains(p.getAsString())) {
|
|
717
|
+ this.addParameters(p.getAsString().toString(), telemetry, registration);
|
|
718
|
+ }
|
|
719
|
+ }
|
|
720
|
+ });
|
816
|
721
|
}
|
817
|
722
|
|
818
|
723
|
/**
|
819
|
|
- * @param sessionInfo -
|
820
|
|
- * @param deviceProfile -
|
|
724
|
+ * @param parameters - JsonObject attributes/telemetry
|
|
725
|
+ * @param registration - Registration LwM2M Client
|
821
|
726
|
*/
|
822
|
|
- public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
|
823
|
|
- Set<String> registrationIds = lwM2mInMemorySecurityStore.getSessions().entrySet()
|
824
|
|
- .stream()
|
825
|
|
- .filter(e -> e.getValue().getProfileUuid().equals(deviceProfile.getUuidId()))
|
826
|
|
- .map(Map.Entry::getKey).sorted().collect(Collectors.toCollection(LinkedHashSet::new));
|
827
|
|
- if (registrationIds.size() > 0) {
|
828
|
|
- this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile);
|
|
727
|
+ private void addParameters(String path, JsonObject parameters, Registration registration) {
|
|
728
|
+ LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registration.getId());
|
|
729
|
+ JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2MClient.getProfileUuid()).getPostKeyNameProfile();
|
|
730
|
+ String resName = String.valueOf(names.get(path));
|
|
731
|
+ if (resName != null && !resName.isEmpty()) {
|
|
732
|
+ try {
|
|
733
|
+ String resValue = this.getResourceValueToString(lwM2MClient, path);
|
|
734
|
+ if (resValue != null) {
|
|
735
|
+ parameters.addProperty(resName, resValue);
|
|
736
|
+ }
|
|
737
|
+ } catch (Exception e) {
|
|
738
|
+ log.error(e.getStackTrace().toString());
|
|
739
|
+ }
|
829
|
740
|
}
|
830
|
741
|
}
|
831
|
742
|
|
832
|
743
|
/**
|
833
|
|
- * @param sessionInfo -
|
834
|
|
- * @param device -
|
835
|
|
- * @param deviceProfileOpt -
|
|
744
|
+ * @param path - path resource
|
|
745
|
+ * @return - value of Resource or null
|
836
|
746
|
*/
|
837
|
|
- public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) {
|
838
|
|
- Optional<String> registrationIdOpt = lwM2mInMemorySecurityStore.getSessions().entrySet().stream()
|
839
|
|
- .filter(e -> device.getUuidId().equals(e.getValue().getDeviceUuid()))
|
840
|
|
- .map(Map.Entry::getKey)
|
841
|
|
- .findFirst();
|
842
|
|
- registrationIdOpt.ifPresent(registrationId -> this.onDeviceUpdateLwM2MClient(registrationId, device, deviceProfileOpt));
|
|
747
|
+ private String getResourceValueToString(LwM2MClient lwM2MClient, String path) {
|
|
748
|
+ LwM2mPath pathIds = new LwM2mPath(path);
|
|
749
|
+ ResourceValue resourceValue = this.returnResourceValueFromLwM2MClient(lwM2MClient, pathIds);
|
|
750
|
+ return (resourceValue == null) ? null :
|
|
751
|
+ (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds);
|
|
752
|
+ }
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+ private ResourceValue returnResourceValueFromLwM2MClient(LwM2MClient lwM2MClient, LwM2mPath pathIds) {
|
|
756
|
+ ResourceValue resourceValue = null;
|
|
757
|
+ if (pathIds.isResource()) {
|
|
758
|
+ resourceValue = lwM2MClient.getResources().get(pathIds.toString());
|
|
759
|
+ }
|
|
760
|
+ return resourceValue;
|
843
|
761
|
}
|
844
|
762
|
|
845
|
763
|
/**
|
846
|
|
- * Update parameters device in LwM2MClient
|
847
|
|
- * If new deviceProfile != old deviceProfile => update deviceProfile
|
|
764
|
+ * Update resource (attribute) value on thingsboard after update value in client
|
848
|
765
|
*
|
849
|
|
- * @param registrationId -
|
850
|
|
- * @param device -
|
|
766
|
+ * @param registration -
|
|
767
|
+ * @param path -
|
|
768
|
+ * @param request -
|
851
|
769
|
*/
|
852
|
|
- private void onDeviceUpdateLwM2MClient(String registrationId, Device device, Optional<DeviceProfile> deviceProfileOpt) {
|
853
|
|
- LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getSessions().get(registrationId);
|
854
|
|
- lwM2MClient.setDeviceName(device.getName());
|
855
|
|
- if (!lwM2MClient.getProfileUuid().equals(device.getDeviceProfileId().getId())) {
|
856
|
|
- Set<String> registrationIds = new HashSet<>();
|
857
|
|
- registrationIds.add(registrationId);
|
858
|
|
- deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceUpdateChangeProfile(registrationIds, deviceProfile));
|
859
|
|
- }
|
860
|
|
-
|
861
|
|
- lwM2MClient.setProfileUuid(device.getDeviceProfileId().getId());
|
|
770
|
+ public void onWriteResponseOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) {
|
|
771
|
+ this.updateResourcesValue(registration, ((LwM2mResource) request.getNode()), path);
|
862
|
772
|
}
|
863
|
773
|
|
864
|
774
|
/**
|
...
|
...
|
@@ -989,16 +899,6 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { |
989
|
899
|
return analyzerParameters;
|
990
|
900
|
}
|
991
|
901
|
|
992
|
|
- private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap<String, String> keyNameOld, ConcurrentMap<String, String> keyNameNew) {
|
993
|
|
- ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
|
994
|
|
- Set<String> paths = keyNameNew.entrySet()
|
995
|
|
- .stream()
|
996
|
|
- .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey())))
|
997
|
|
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet();
|
998
|
|
- analyzerParameters.setPathPostParametersAdd(paths);
|
999
|
|
- return analyzerParameters;
|
1000
|
|
- }
|
1001
|
|
-
|
1002
|
902
|
private ResultsAnalyzerParameters getAnalyzerParametersIn(Set<String> parametersObserve, Set<String> parameters) {
|
1003
|
903
|
ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
|
1004
|
904
|
analyzerParameters.setPathPostParametersAdd(parametersObserve
|
...
|
...
|
@@ -1031,60 +931,147 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { |
1031
|
931
|
});
|
1032
|
932
|
}
|
1033
|
933
|
|
|
934
|
+ private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap<String, String> keyNameOld, ConcurrentMap<String, String> keyNameNew) {
|
|
935
|
+ ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
|
|
936
|
+ Set<String> paths = keyNameNew.entrySet()
|
|
937
|
+ .stream()
|
|
938
|
+ .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey())))
|
|
939
|
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet();
|
|
940
|
+ analyzerParameters.setPathPostParametersAdd(paths);
|
|
941
|
+ return analyzerParameters;
|
|
942
|
+ }
|
|
943
|
+
|
1034
|
944
|
private void cancelObserveIsValue(LeshanServer lwServer, Registration registration, Set<String> paramAnallyzer) {
|
1035
|
945
|
LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
|
1036
|
946
|
paramAnallyzer.forEach(p -> {
|
1037
|
|
- if (this.getResourceValue(lwM2MClient, new LwM2mPath(p)) != null) {
|
|
947
|
+ if (this.returnResourceValueFromLwM2MClient(lwM2MClient, new LwM2mPath(p)) != null) {
|
1038
|
948
|
this.setCancelObservationRecourse(lwServer, registration, p);
|
1039
|
949
|
}
|
1040
|
950
|
}
|
1041
|
951
|
);
|
1042
|
952
|
}
|
1043
|
953
|
|
1044
|
|
- private ResourceValue getResourceValue(LwM2MClient lwM2MClient, LwM2mPath pathIds) {
|
1045
|
|
- ResourceValue resourceValue = null;
|
1046
|
|
- if (pathIds.isResource()) {
|
1047
|
|
- resourceValue = lwM2MClient.getResources().get(pathIds.toString());
|
|
954
|
+ private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) {
|
|
955
|
+ if (valueNew != null && !valueNew.toString().equals(valueOld.toString())) {
|
|
956
|
+ lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
|
|
957
|
+ ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(),
|
|
958
|
+ true);
|
1048
|
959
|
}
|
1049
|
|
- return resourceValue;
|
1050
|
960
|
}
|
1051
|
961
|
|
1052
|
962
|
/**
|
1053
|
|
- * Trigger Server path = "/1/0/8"
|
|
963
|
+ * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...)
|
|
964
|
+ * config attr/telemetry... in profile
|
|
965
|
+ */
|
|
966
|
+ public void onToTransportUpdateCredentials(TransportProtos.ToTransportUpdateCredentialsProto updateCredentials) {
|
|
967
|
+ log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList());
|
|
968
|
+ }
|
|
969
|
+
|
|
970
|
+ /**
|
|
971
|
+ * Get path to resource from profile equal keyName or from ModelObject equal name
|
|
972
|
+ * Only for resource: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase)
|
1054
|
973
|
*
|
1055
|
|
- * Trigger bootStrap path = "/1/0/9" - have to implemented on client
|
|
974
|
+ * @param sessionInfo -
|
|
975
|
+ * @param name -
|
|
976
|
+ * @return path if path isPresent in postProfile
|
1056
|
977
|
*/
|
1057
|
|
- public void doTrigger(LeshanServer lwServer, Registration registration, String path) {
|
1058
|
|
- lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_EXECUTE,
|
1059
|
|
- ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),
|
1060
|
|
- false);
|
|
978
|
+ private String getPathAttributeUpdate(TransportProtos.SessionInfoProto sessionInfo, String name) {
|
|
979
|
+ String profilePath = this.getPathAttributeUpdateProfile(sessionInfo, name);
|
|
980
|
+ return !profilePath.isEmpty() ? profilePath : null;
|
1061
|
981
|
}
|
1062
|
982
|
|
1063
|
983
|
/**
|
1064
|
|
- * Session device in thingsboard is closed
|
|
984
|
+ * Get path to resource from profile equal keyName
|
1065
|
985
|
*
|
1066
|
|
- * @param sessionInfo - lwm2m client
|
|
986
|
+ * @param sessionInfo -
|
|
987
|
+ * @param name -
|
|
988
|
+ * @return -
|
1067
|
989
|
*/
|
1068
|
|
- private void doCloseSession(SessionInfoProto sessionInfo) {
|
1069
|
|
- TransportProtos.SessionEvent event = SessionEvent.CLOSED;
|
1070
|
|
- TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder()
|
1071
|
|
- .setSessionType(TransportProtos.SessionType.ASYNC)
|
1072
|
|
- .setEvent(event).build();
|
1073
|
|
- transportService.process(sessionInfo, msg, null);
|
|
990
|
+ private String getPathAttributeUpdateProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
|
|
991
|
+ LwM2MClientProfile profile = lwM2mInMemorySecurityStore.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
|
|
992
|
+ return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
|
|
993
|
+ .filter(e -> e.getValue().getAsString().equals(name)).findFirst().map(Map.Entry::getKey)
|
|
994
|
+ .orElse("");
|
1074
|
995
|
}
|
1075
|
996
|
|
1076
|
997
|
/**
|
1077
|
|
- * Deregister session in transport
|
|
998
|
+ * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values
|
|
999
|
+ * #1 Get path resource by result attributesResponse
|
|
1000
|
+ * #1.1 If two names have equal path => last time attribute
|
|
1001
|
+ * #2.1 if there is a difference in values between the current resource values and the shared attribute values
|
|
1002
|
+ * => sent to client Request Update of value (new value from shared attribute)
|
|
1003
|
+ * and LwM2MClient.delayedRequests.add(path)
|
|
1004
|
+ * #2.1 if there is not a difference in values between the current resource values and the shared attribute values
|
1078
|
1005
|
*
|
1079
|
|
- * @param sessionInfo - lwm2m client
|
|
1006
|
+ * @param attributesResponse -
|
|
1007
|
+ * @param sessionInfo -
|
1080
|
1008
|
*/
|
1081
|
|
- public void doDisconnect(SessionInfoProto sessionInfo) {
|
1082
|
|
- transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null);
|
1083
|
|
- transportService.deregisterSession(sessionInfo);
|
|
1009
|
+ public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
|
|
1010
|
+ LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(sessionInfo);
|
|
1011
|
+ attributesResponse.getSharedAttributeListList().forEach(attr -> {
|
|
1012
|
+ String path = this.getPathAttributeUpdate(sessionInfo, attr.getKv().getKey());
|
|
1013
|
+ // #1.1
|
|
1014
|
+ if (lwM2MClient.getDelayedRequests().containsKey(path) && attr.getTs() > lwM2MClient.getDelayedRequests().get(path).getTs()) {
|
|
1015
|
+ lwM2MClient.getDelayedRequests().put(path, attr);
|
|
1016
|
+ } else {
|
|
1017
|
+ lwM2MClient.getDelayedRequests().put(path, attr);
|
|
1018
|
+ }
|
|
1019
|
+ });
|
|
1020
|
+ // #2.1
|
|
1021
|
+ lwM2MClient.getDelayedRequests().forEach((k, v) -> {
|
|
1022
|
+ ArrayList<TransportProtos.KeyValueProto> listV = new ArrayList<>();
|
|
1023
|
+ listV.add(v.getKv());
|
|
1024
|
+ this.putDelayedUpdateResourcesClient(lwM2MClient, this.getResourceValueToString(lwM2MClient, k), getJsonObject(listV).get(v.getKv().getKey()), k);
|
|
1025
|
+ });
|
1084
|
1026
|
}
|
1085
|
1027
|
|
1086
|
|
- private void checkInactivityAndReportActivity() {
|
1087
|
|
- lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> this.checkInactivity(this.getValidateSessionInfo(key)));
|
|
1028
|
+ /**
|
|
1029
|
+ * @param lwM2MClient -
|
|
1030
|
+ * @return
|
|
1031
|
+ */
|
|
1032
|
+ private SessionInfoProto getNewSessionInfoProto(LwM2MClient lwM2MClient) {
|
|
1033
|
+ if (lwM2MClient != null) {
|
|
1034
|
+ TransportProtos.ValidateDeviceCredentialsResponseMsg msg = lwM2MClient.getCredentialsResponse();
|
|
1035
|
+ if (msg == null || msg.getDeviceInfo() == null) {
|
|
1036
|
+ log.error("[{}] [{}]", lwM2MClient.getEndPoint(), CLIENT_NOT_AUTHORIZED);
|
|
1037
|
+ this.closeClientSession(lwM2MClient.getRegistration());
|
|
1038
|
+ return null;
|
|
1039
|
+ } else {
|
|
1040
|
+ return SessionInfoProto.newBuilder()
|
|
1041
|
+ .setNodeId(this.context.getNodeId())
|
|
1042
|
+ .setSessionIdMSB(lwM2MClient.getSessionUuid().getMostSignificantBits())
|
|
1043
|
+ .setSessionIdLSB(lwM2MClient.getSessionUuid().getLeastSignificantBits())
|
|
1044
|
+ .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
|
|
1045
|
+ .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
|
|
1046
|
+ .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
|
|
1047
|
+ .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
|
|
1048
|
+ .setDeviceName(msg.getDeviceInfo().getDeviceName())
|
|
1049
|
+ .setDeviceType(msg.getDeviceInfo().getDeviceType())
|
|
1050
|
+ .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
|
|
1051
|
+ .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
|
|
1052
|
+ .build();
|
|
1053
|
+ }
|
|
1054
|
+ }
|
|
1055
|
+ return null;
|
|
1056
|
+ }
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+ /**
|
|
1060
|
+ * @param registration - Registration LwM2M Client
|
|
1061
|
+ * @return - sessionInfo after access connect client
|
|
1062
|
+ */
|
|
1063
|
+ private SessionInfoProto getValidateSessionInfo(Registration registration) {
|
|
1064
|
+ LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(registration, null);
|
|
1065
|
+ return getNewSessionInfoProto(lwM2MClient);
|
|
1066
|
+ }
|
|
1067
|
+
|
|
1068
|
+ /**
|
|
1069
|
+ * @param registrationId -
|
|
1070
|
+ * @return -
|
|
1071
|
+ */
|
|
1072
|
+ private SessionInfoProto getValidateSessionInfo(String registrationId) {
|
|
1073
|
+ LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClientWithReg(null, registrationId);
|
|
1074
|
+ return getNewSessionInfoProto(lwM2MClient);
|
1088
|
1075
|
}
|
1089
|
1076
|
|
1090
|
1077
|
/**
|
...
|
...
|
@@ -1098,22 +1085,55 @@ public class LwM2MTransportServiceImpl implements LwM2MTransportService { |
1098
|
1085
|
}
|
1099
|
1086
|
}
|
1100
|
1087
|
|
1101
|
|
- public void sentLogsToThingsboard(String msg, Registration registration) {
|
1102
|
|
- if (msg != null) {
|
1103
|
|
- JsonObject telemetries = new JsonObject();
|
1104
|
|
- telemetries.addProperty(LOG_LW2M_TELEMETRY, msg);
|
1105
|
|
- this.updateParametersOnThingsboard(telemetries, LwM2MTransportHandler.DEVICE_TELEMETRY_TOPIC, registration);
|
|
1088
|
+ private void checkInactivityAndReportActivity() {
|
|
1089
|
+ lwM2mInMemorySecurityStore.getSessions().forEach((key, value) -> this.checkInactivity(this.getValidateSessionInfo(key)));
|
|
1090
|
+ }
|
|
1091
|
+
|
|
1092
|
+ /**
|
|
1093
|
+ * If there is a difference in values between the current resource values and the shared attribute values
|
|
1094
|
+ * when the client connects to the server
|
|
1095
|
+ * #1 get attributes name from profile include name resources in ModelObject if resource isWritable
|
|
1096
|
+ * #2.1 #1 size > 0 => send Request getAttributes to thingsboard
|
|
1097
|
+ *
|
|
1098
|
+ * @param lwM2MClient - LwM2M Client
|
|
1099
|
+ */
|
|
1100
|
+ public void putDelayedUpdateResourcesThingsboard(LwM2MClient lwM2MClient) {
|
|
1101
|
+ SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration());
|
|
1102
|
+ if (sessionInfo != null) {
|
|
1103
|
+ //#1.1 + #1.2
|
|
1104
|
+ List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient);
|
|
1105
|
+ if (attrSharedNames.size() > 0) {
|
|
1106
|
+ //#2.1
|
|
1107
|
+ try {
|
|
1108
|
+ TransportProtos.GetAttributeRequestMsg getAttributeMsg = context.getAdaptor().convertToGetAttributes(null, attrSharedNames);
|
|
1109
|
+ transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST));
|
|
1110
|
+ } catch (AdaptorException e) {
|
|
1111
|
+ log.warn("Failed to decode get attributes request", e);
|
|
1112
|
+ }
|
|
1113
|
+ }
|
1106
|
1114
|
}
|
1107
|
1115
|
}
|
1108
|
1116
|
|
|
1117
|
+
|
1109
|
1118
|
/**
|
1110
|
|
- * @param path - path resource
|
1111
|
|
- * @return - value of Resource or null
|
|
1119
|
+ * Get names and keyNames from profile shared!!!! attr resources IsWritable
|
|
1120
|
+ *
|
|
1121
|
+ * @param lwM2MClient -
|
|
1122
|
+ * @return ArrayList keyNames from profile attr resources shared!!!! && IsWritable
|
1112
|
1123
|
*/
|
1113
|
|
- private String getResourceValueToString(LwM2MClient lwM2MClient, String path) {
|
1114
|
|
- LwM2mPath pathIds = new LwM2mPath(path);
|
1115
|
|
- ResourceValue resourceValue = this.getResourceValue(lwM2MClient, pathIds);
|
1116
|
|
- return (resourceValue == null) ? null :
|
1117
|
|
- (String) this.converter.convertValue(resourceValue.getResourceValue(), this.context.getCtxServer().getResourceModelType(lwM2MClient.getRegistration(), pathIds), ResourceModel.Type.STRING, pathIds);
|
|
1124
|
+ private List<String> getNamesAttrFromProfileIsWritable(LwM2MClient lwM2MClient) {
|
|
1125
|
+ LwM2MClientProfile profile = lwM2mInMemorySecurityStore.getProfile(lwM2MClient.getProfileUuid());
|
|
1126
|
+ Set attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), Set.class);
|
|
1127
|
+ ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), ConcurrentHashMap.class);
|
|
1128
|
+
|
|
1129
|
+ ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet()
|
|
1130
|
+ .stream()
|
|
1131
|
+ .filter(e -> (attrSet.contains(e.getKey()) && context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())) != null &&
|
|
1132
|
+ context.getCtxServer().getResourceModel(lwM2MClient.getRegistration(), new LwM2mPath(e.getKey())).operations.isWritable()))
|
|
1133
|
+ .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
|
|
1134
|
+
|
|
1135
|
+ Set<String> namesIsWritable = ConcurrentHashMap.newKeySet();
|
|
1136
|
+ namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values()));
|
|
1137
|
+ return new ArrayList<>(namesIsWritable);
|
1118
|
1138
|
}
|
1119
|
1139
|
} |
...
|
...
|
|