Commit ade09738eb458ad99c99e9fd72028ca6795e727f

Authored by nickAS21
Committed by Andrew Shvayka
1 parent c230e3fd

Lwm2m: back: add key to profile - refactoring-light start2

... ... @@ -249,6 +249,11 @@ public class LwM2MTransportHandler {
249 249 profile.getPostClientLwM2mSettings().getAsJsonObject().get("clientUpdateValueAfterConnect").getAsBoolean();
250 250 }
251 251
  252 + public static boolean getClientOnlyObserveAfterConnect (LwM2MClientProfile profile) {
  253 + return profile.getPostClientLwM2mSettings().getAsJsonObject().has("clientOnlyObserveAfterConnect") &&
  254 + profile.getPostClientLwM2mSettings().getAsJsonObject().get("clientOnlyObserveAfterConnect").getAsBoolean();
  255 + }
  256 +
252 257 private static boolean getValidateCredentialsBodyFromThingsboard(JsonObject objectMsg) {
253 258 return (objectMsg != null &&
254 259 !objectMsg.isJsonNull() &&
... ...
... ... @@ -226,7 +226,7 @@ public class LwM2MTransportRequest {
226 226 String msg = String.format(LOG_LW2M_ERROR + ": sendRequest: Resource path - %s msg No SendRequest to Client", target);
227 227 service.sentLogsToThingsboard(msg, registration);
228 228 log.error("[{}] - [{}] No SendRequest", target);
229   - this.handleResponseError(registration, target, lwM2MClient, true);
  229 +// this.handleResponseError(registration, target, lwM2MClient, true);
230 230
231 231 }
232 232 }
... ... @@ -262,9 +262,9 @@ public class LwM2MTransportRequest {
262 262 ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
263 263 service.sentLogsToThingsboard(msg, registration);
264 264 log.error("[{}] - [{}] [{}] error SendRequest", ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
265   - if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest() && isDelayedUpdate) {
266   - this.handleResponseError(registration, request.getPath().toString(), lwM2MClient, isDelayedUpdate);
267   - }
  265 +// if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest() && isDelayedUpdate) {
  266 +// this.handleResponseError(registration, request.getPath().toString(), lwM2MClient, isDelayedUpdate);
  267 +// }
268 268 }
269 269
270 270 }, e -> {
... ... @@ -272,9 +272,9 @@ public class LwM2MTransportRequest {
272 272 request.getPath().toString(), e.toString());
273 273 service.sentLogsToThingsboard(msg, registration);
274 274 log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString());
275   - if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest() && isDelayedUpdate) {
276   - this.handleResponseError(registration, request.getPath().toString(), lwM2MClient, isDelayedUpdate);
277   - }
  275 +// if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest() && isDelayedUpdate) {
  276 +// this.handleResponseError(registration, request.getPath().toString(), lwM2MClient, isDelayedUpdate);
  277 +// }
278 278 });
279 279 }
280 280
... ... @@ -317,16 +317,16 @@ public class LwM2MTransportRequest {
317 317 }
318 318 });
319 319 }
320   -
321   - private void handleResponseError(Registration registration, final String path, LwM2MClient lwM2MClient, boolean isDelayedUpdate) {
322   - executorResponseError.submit(() -> {
323   - try {
324   - if (isDelayedUpdate) lwM2MClient.onSuccessOrErrorDelayedRequests(path);
325   - } catch (RuntimeException t) {
326   - log.error("[{}] endpoint [{}] path [{}] RuntimeException Unable to after send response.", registration.getEndpoint(), path, t);
327   - }
328   - });
329   - }
  320 +//
  321 +// private void handleResponseError(Registration registration, final String path, LwM2MClient lwM2MClient, boolean isDelayedUpdate) {
  322 +// executorResponseError.submit(() -> {
  323 +// try {
  324 +// if (isDelayedUpdate) lwM2MClient.onSuccessOrErrorDelayedRequests(path);
  325 +// } catch (RuntimeException t) {
  326 +// log.error("[{}] endpoint [{}] path [{}] RuntimeException Unable to after send response.", registration.getEndpoint(), path, t);
  327 +// }
  328 +// });
  329 +// }
330 330
331 331 /**
332 332 * processing a response from a client
... ... @@ -336,26 +336,10 @@ public class LwM2MTransportRequest {
336 336 * @param lwM2MClient -
337 337 */
338 338 private void sendResponse(Registration registration, String path, LwM2mResponse response, DownlinkRequest request, LwM2MClient lwM2MClient, boolean isDelayedUpdate) {
339   - if (response instanceof ObserveResponse) {
  339 + if (response instanceof ObserveResponse || response instanceof ReadResponse) {
340 340 service.onObservationResponse(registration, path, (ReadResponse) response);
341 341 } else if (response instanceof CancelObservationResponse) {
342 342 log.info("[{}] Path [{}] CancelObservationResponse 3_Send", path, response);
343   - } else if (response instanceof ReadResponse) {
344   - /**
345   - * Use only at the first start after registration
346   - * Fill with data -> Model client
347   - */
348   - if (lwM2MClient != null) {
349   - if (lwM2MClient.getPendingRequests().size() > 0) {
350   - lwM2MClient.onSuccessHandler(path, response);
351   - }
352   - }
353   - /**
354   - * Use after registration on request
355   - */
356   - else {
357   - service.onObservationResponse(registration, path, (ReadResponse) response);
358   - }
359 343 } else if (response instanceof DeleteResponse) {
360 344 log.info("[{}] Path [{}] DeleteResponse 5_Send", path, response);
361 345 } else if (response instanceof DiscoverResponse) {
... ... @@ -366,7 +350,7 @@ public class LwM2MTransportRequest {
366 350 log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", path, response);
367 351 } else if (response instanceof WriteResponse) {
368 352 log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", path, response);
369   - service.onAttributeUpdateOk(registration, path, (WriteRequest) request, isDelayedUpdate);
  353 + service.onWriteResponseOk(registration, path, (WriteRequest) request, isDelayedUpdate);
370 354 }
371 355 }
372 356 }
... ...
... ... @@ -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 }
... ...
... ... @@ -18,12 +18,8 @@ package org.thingsboard.server.transport.lwm2m.server.client;
18 18 import lombok.Data;
19 19 import lombok.extern.slf4j.Slf4j;
20 20 import org.eclipse.leshan.core.node.LwM2mMultipleResource;
21   -import org.eclipse.leshan.core.node.LwM2mObjectInstance;
22   -import org.eclipse.leshan.core.node.LwM2mPath;
23 21 import org.eclipse.leshan.core.node.LwM2mResource;
24 22 import org.eclipse.leshan.core.node.LwM2mSingleResource;
25   -import org.eclipse.leshan.core.response.LwM2mResponse;
26   -import org.eclipse.leshan.core.response.ReadResponse;
27 23 import org.eclipse.leshan.server.californium.LeshanServer;
28 24 import org.eclipse.leshan.server.registration.Registration;
29 25 import org.eclipse.leshan.server.security.SecurityInfo;
... ... @@ -33,7 +29,6 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportServiceImpl;
33 29 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
34 30
35 31 import java.util.Map;
36   -import java.util.Set;
37 32 import java.util.UUID;
38 33 import java.util.concurrent.ConcurrentHashMap;
39 34
... ... @@ -54,10 +49,10 @@ public class LwM2MClient implements Cloneable {
54 49 private ValidateDeviceCredentialsResponseMsg credentialsResponse;
55 50 private Map<String, String> attributes;
56 51 private Map<String, ResourceValue> resources;
57   - private Set<String> pendingRequests;
  52 + // private Set<String> pendingRequests;
58 53 private Map<String, TransportProtos.TsKvProto> delayedRequests;
59   - private Set<Integer> delayedRequestsId;
60   - private Map<String, LwM2mResponse> responses;
  54 +// private Set<Integer> delayedRequestsId;
  55 +// private Map<String, LwM2mResponse> responses;
61 56 private final LwM2mValueConverterImpl converter;
62 57
63 58 public Object clone() throws CloneNotSupportedException {
... ... @@ -70,69 +65,76 @@ public class LwM2MClient implements Cloneable {
70 65 this.securityInfo = securityInfo;
71 66 this.credentialsResponse = credentialsResponse;
72 67 this.attributes = new ConcurrentHashMap<>();
73   - this.pendingRequests = ConcurrentHashMap.newKeySet();
  68 +// this.pendingRequests = ConcurrentHashMap.newKeySet();
74 69 this.delayedRequests = new ConcurrentHashMap<>();
75 70 this.resources = new ConcurrentHashMap<>();
76   - this.delayedRequestsId = ConcurrentHashMap.newKeySet();
  71 +// this.delayedRequestsId = ConcurrentHashMap.newKeySet();
77 72 this.profileUuid = profileUuid;
78 73 /**
79 74 * Key <objectId>, response<Value -> instance -> resources: value...>
80 75 */
81   - this.responses = new ConcurrentHashMap<>();
  76 +// this.responses = new ConcurrentHashMap<>();
82 77 this.converter = LwM2mValueConverterImpl.getInstance();
83 78 }
84 79
85   - /**
86   - * Fill with data -> Model client
87   - *
88   - * @param path -
89   - * @param response -
90   - */
91   - public void onSuccessHandler(String path, LwM2mResponse response) {
92   - this.responses.put(path, response);
93   - this.pendingRequests.remove(path);
94   - if (this.pendingRequests.size() == 0) {
95   - this.initValue();
96   - this.lwM2MTransportServiceImpl.putDelayedUpdateResourcesThingsboard(this);
97   - }
98   - }
  80 +// /**
  81 +// * Fill with data -> Model client
  82 +// *
  83 +// * @param path -
  84 +// * @param response -
  85 +// */
  86 +// public void onSuccessHandler(String path, LwM2mResponse response) {
  87 +// this.responses.put(path, response);
  88 +// this.pendingRequests.remove(path);
  89 +// if (this.pendingRequests.size() == 0) {
  90 +// this.initValue();
  91 +// this.lwM2MTransportServiceImpl.putDelayedUpdateResourcesThingsboard(this);
  92 +// }
  93 +// }
  94 +//
  95 +// private void initValue() {
  96 +// this.responses.forEach((key, lwM2mResponse) -> {
  97 +// LwM2mPath pathIds = new LwM2mPath(key);
  98 +// if (pathIds.isObjectInstance()) {
  99 +// ((LwM2mObjectInstance) ((ReadResponse) lwM2mResponse).getContent()).getResources().forEach((k, v) -> {
  100 +// String pathRez = pathIds.toString() + "/" + k;
  101 +// this.updateResourceValue(pathRez, v);
  102 +// });
  103 +// }
  104 +// else if (pathIds.isResource()) {
  105 +// this.updateResourceValue(pathIds.toString(), ((LwM2mResource) ((ReadResponse) lwM2mResponse).getContent()));
  106 +// }
  107 +// });
  108 +// if (this.responses.size() == 0) this.responses = new ConcurrentHashMap<>();
  109 +// }
99 110
100   - private void initValue() {
101   - this.responses.forEach((key, lwM2mResponse) -> {
102   - LwM2mPath pathIds = new LwM2mPath(key);
103   - if (pathIds.isObjectInstance()) {
104   - ((LwM2mObjectInstance) ((ReadResponse) lwM2mResponse).getContent()).getResources().forEach((k, v) -> {
105   - String pathRez = pathIds.toString() + "/" + k;
106   - this.updateResourceValue(pathRez, v);
107   - });
108   - }
109   - else if (pathIds.isResource()) {
110   - this.updateResourceValue(pathIds.toString(), ((LwM2mResource) ((ReadResponse) lwM2mResponse).getContent()));
111   - }
112   - });
113   - if (this.responses.size() == 0) this.responses = new ConcurrentHashMap<>();
114   - }
  111 +// public void updateObjectInstanceResourceValue(String pathInst, LwM2mObjectInstance instance) {
  112 +// LwM2mPath pathIds = new LwM2mPath(pathInst);
  113 +// instance.getResources().forEach((k, v) -> {
  114 +// String pathRez = pathIds.toString() + "/" + k;
  115 +// this.updateResourceValue(pathRez, v);
  116 +// });
  117 +// }
115 118
116 119 public void updateResourceValue(String pathRez, LwM2mResource rez) {
117   - if (rez instanceof LwM2mMultipleResource){
  120 + if (rez instanceof LwM2mMultipleResource) {
118 121 this.resources.put(pathRez, new ResourceValue(rez.getValues(), null, true));
119   - }
120   - else if (rez instanceof LwM2mSingleResource) {
  122 + } else if (rez instanceof LwM2mSingleResource) {
121 123 this.resources.put(pathRez, new ResourceValue(null, rez.getValue(), false));
122 124 }
123 125 }
124 126
125   - /**
126   - * if path != null
127   - *
128   - * @param path
129   - */
130   - public void onSuccessOrErrorDelayedRequests(String path) {
131   - if (path != null) this.delayedRequests.remove(path);
132   - if (this.delayedRequests.size() == 0 && this.getDelayedRequestsId().size() == 0) {
133   - this.lwM2MTransportServiceImpl.updatesAndSentModelParameter(this);
134   - }
135   - }
  127 +// /**
  128 +// * if path != null
  129 +// *
  130 +// * @param path
  131 +// */
  132 +// public void onSuccessOrErrorDelayedRequests(String path) {
  133 +// if (path != null) this.delayedRequests.remove(path);
  134 +// if (this.delayedRequests.size() == 0 && this.getDelayedRequestsId().size() == 0) {
  135 +// this.lwM2MTransportServiceImpl.updatesAndSentModelParameter(this);
  136 +// }
  137 +// }
136 138
137 139 }
138 140
... ...
... ... @@ -21,14 +21,20 @@
21 21 <ng-template matTabContent>
22 22 <section [formGroup]="lwm2mDeviceProfileFormGroup">
23 23 <div class="mat-padding" style="padding-bottom: 0px">
24   - <mat-checkbox formControlName="clientUpdateValueAfterConnect" color="primary"
  24 + <mat-checkbox formControlName="clientOnlyObserveAfterConnect" color="primary"
  25 + matTooltip="{{ translate.get('device-profile.lwm2m.client-only-observe-after-connect-tip',
  26 + { count: +lwm2mDeviceProfileFormGroup.get('clientOnlyObserveAfterConnect').value }) | async }}"
  27 + matTooltipPosition="above">
  28 + {{ translate.get('device-profile.lwm2m.client-only-observe-after-connect',
  29 + { count: +lwm2mDeviceProfileFormGroup.get('clientOnlyObserveAfterConnect').value }) | async }}
  30 + </mat-checkbox>
  31 + <mat-checkbox *ngIf="!lwm2mDeviceProfileFormGroup.get('clientOnlyObserveAfterConnect').value"
  32 + formControlName="clientUpdateValueAfterConnect" color="primary"
25 33 matTooltip="{{ translate.get('device-profile.lwm2m.client-update-value-after-connect-tip',
26   - { count: +lwm2mDeviceProfileFormGroup.get('clientUpdateValueAfterConnect').value,
27   - value: lwm2mDeviceProfileFormGroup.get('clientUpdateValueAfterConnect').value }) | async }}"
  34 + { count: +lwm2mDeviceProfileFormGroup.get('clientUpdateValueAfterConnect').value }) | async }}"
28 35 matTooltipPosition="above">
29 36 {{ translate.get('device-profile.lwm2m.client-update-value-after-connect',
30   - { count: +lwm2mDeviceProfileFormGroup.get('clientUpdateValueAfterConnect').value,
31   - value: lwm2mDeviceProfileFormGroup.get('clientUpdateValueAfterConnect').value }) | async }}
  37 + { count: +lwm2mDeviceProfileFormGroup.get('clientUpdateValueAfterConnect').value }) | async }}
32 38 </mat-checkbox>
33 39 </div>
34 40 <div class="mat-padding" style="padding-top: 0">
... ...
... ... @@ -74,6 +74,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
74 74 private deviceProfileService: DeviceProfileService,
75 75 @Inject(WINDOW) private window: Window) {
76 76 this.lwm2mDeviceProfileFormGroup = this.fb.group({
  77 + clientOnlyObserveAfterConnect: [true, []],
77 78 clientUpdateValueAfterConnect: [false, []],
78 79 objectIds: [null, Validators.required],
79 80 observeAttrTelemetry: [null, Validators.required],
... ... @@ -144,6 +145,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
144 145
145 146 private updateWriteValue = (value: ModelValue): void => {
146 147 this.lwm2mDeviceProfileFormGroup.patchValue({
  148 + clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect,
147 149 clientUpdateValueAfterConnect: this.configurationValue.clientLwM2mSettings.clientUpdateValueAfterConnect,
148 150 objectIds: value,
149 151 observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value['objectsList']),
... ... @@ -176,6 +178,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
176 178
177 179 private updateDeviceProfileValue(config): void {
178 180 if (this.lwm2mDeviceProfileFormGroup.valid) {
  181 + this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect =
  182 + config.clientOnlyObserveAfterConnect;
179 183 this.configurationValue.clientLwM2mSettings.clientUpdateValueAfterConnect =
180 184 config.clientUpdateValueAfterConnect;
181 185 this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M);
... ...
... ... @@ -97,7 +97,8 @@ export interface ProfileConfigModels {
97 97 }
98 98
99 99 export interface ClientLwM2mSettings {
100   - clientUpdateValueAfterConnect: false;
  100 + clientOnlyObserveAfterConnect: boolean;
  101 + clientUpdateValueAfterConnect: boolean;
101 102 }
102 103 export interface ObservableAttributes {
103 104 observe: string[];
... ... @@ -145,17 +146,26 @@ function getDefaultProfileBootstrapSecurityConfig(hostname: any): BootstrapSecur
145 146 };
146 147 }
147 148
  149 +function getDefaultProfileObserveAttrConfig(): ObservableAttributes {
  150 + return {
  151 + observe: [],
  152 + attribute: [],
  153 + telemetry: [],
  154 + keyName: {}
  155 + };
  156 +}
  157 +
  158 +function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings {
  159 + return {
  160 + clientOnlyObserveAfterConnect: true,
  161 + clientUpdateValueAfterConnect: false
  162 + };
  163 +}
  164 +
148 165 export function getDefaultProfileConfig(hostname?: any): ProfileConfigModels {
149 166 return {
150   - clientLwM2mSettings: {
151   - clientUpdateValueAfterConnect: false
152   - },
153   - observeAttr: {
154   - observe: [],
155   - attribute: [],
156   - telemetry: [],
157   - keyName: {}
158   - },
  167 + clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig(),
  168 + observeAttr: getDefaultProfileObserveAttrConfig(),
159 169 bootstrap: getDefaultProfileBootstrapSecurityConfig((hostname) ? hostname : DEFAULT_HOST_NAME)
160 170 };
161 171 }
... ...
... ... @@ -1103,6 +1103,8 @@
1103 1103 "schedule-time-to": "To",
1104 1104 "schedule-days-of-week-required": "At least one day of week should be selected.",
1105 1105 "lwm2m": {
  1106 + "client-only-observe-after-connect": "{ count, plural, 1 {Only Observe Request to the client after registration} other {Read&Observe Request to the client after registration} }",
  1107 + "client-only-observe-after-connect-tip": "{ count, plural, 1 {Only Observe Request to the client marked as observe from the profile configuration.} other {Read Request to the client after registration to read the values of the resources then Observe Request to the client marked as observe from the profile configuration.} }",
1106 1108 "client-update-value-after-connect": "{ count, plural, 1 {Request to the client after registration for All resource values} other {Request to the client after registration to read values only as attributes or telemetry} }",
1107 1109 "client-update-value-after-connect-tip": "{ count, plural, 1 {Request to the client after registration to read all resource values for all objects} other {Request to the client after registration to read the values of the resources marked as attribute or telemetry from the profile configuration.} }",
1108 1110 "object-list": "Object list",
... ...