Commit 854307fd780068e429f601f1853ec1bd0fd1ebcb

Authored by Igor Kulikov
2 parents 69271305 aea24389

Merge branch 'master' of github.com:thingsboard/thingsboard

Showing 86 changed files with 1778 additions and 581 deletions
@@ -197,3 +197,17 @@ $$; @@ -197,3 +197,17 @@ $$;
197 ALTER TABLE api_usage_state 197 ALTER TABLE api_usage_state
198 ADD COLUMN IF NOT EXISTS alarm_exec VARCHAR(32); 198 ADD COLUMN IF NOT EXISTS alarm_exec VARCHAR(32);
199 UPDATE api_usage_state SET alarm_exec = 'ENABLED' WHERE alarm_exec IS NULL; 199 UPDATE api_usage_state SET alarm_exec = 'ENABLED' WHERE alarm_exec IS NULL;
  200 +
  201 +CREATE TABLE IF NOT EXISTS rpc (
  202 + id uuid NOT NULL CONSTRAINT rpc_pkey PRIMARY KEY,
  203 + created_time bigint NOT NULL,
  204 + tenant_id uuid NOT NULL,
  205 + device_id uuid NOT NULL,
  206 + expiration_time bigint NOT NULL,
  207 + request varchar(10000000) NOT NULL,
  208 + response varchar(10000000),
  209 + status varchar(255) NOT NULL
  210 +);
  211 +
  212 +CREATE INDEX IF NOT EXISTS idx_rpc_tenant_id_device_id ON rpc(tenant_id, device_id);
  213 +
@@ -65,6 +65,7 @@ import org.thingsboard.server.dao.relation.RelationService; @@ -65,6 +65,7 @@ import org.thingsboard.server.dao.relation.RelationService;
65 import org.thingsboard.server.dao.resource.ResourceService; 65 import org.thingsboard.server.dao.resource.ResourceService;
66 import org.thingsboard.server.dao.rule.RuleChainService; 66 import org.thingsboard.server.dao.rule.RuleChainService;
67 import org.thingsboard.server.dao.rule.RuleNodeStateService; 67 import org.thingsboard.server.dao.rule.RuleNodeStateService;
  68 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
68 import org.thingsboard.server.dao.tenant.TenantProfileService; 69 import org.thingsboard.server.dao.tenant.TenantProfileService;
69 import org.thingsboard.server.dao.tenant.TenantService; 70 import org.thingsboard.server.dao.tenant.TenantService;
70 import org.thingsboard.server.dao.timeseries.TimeseriesService; 71 import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -80,9 +81,9 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService; @@ -80,9 +81,9 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService;
80 import org.thingsboard.server.service.executors.SharedEventLoopGroupService; 81 import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
81 import org.thingsboard.server.service.mail.MailExecutorService; 82 import org.thingsboard.server.service.mail.MailExecutorService;
82 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 83 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
83 -import org.thingsboard.server.dao.tenant.TbTenantProfileCache;  
84 import org.thingsboard.server.service.queue.TbClusterService; 84 import org.thingsboard.server.service.queue.TbClusterService;
85 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; 85 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
  86 +import org.thingsboard.server.service.rpc.TbRpcService;
86 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; 87 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
87 import org.thingsboard.server.service.script.JsExecutorService; 88 import org.thingsboard.server.service.script.JsExecutorService;
88 import org.thingsboard.server.service.script.JsInvokeService; 89 import org.thingsboard.server.service.script.JsInvokeService;
@@ -303,23 +304,33 @@ public class ActorSystemContext { @@ -303,23 +304,33 @@ public class ActorSystemContext {
303 304
304 @Lazy 305 @Lazy
305 @Autowired(required = false) 306 @Autowired(required = false)
306 - @Getter private EdgeService edgeService; 307 + @Getter
  308 + private EdgeService edgeService;
307 309
308 @Lazy 310 @Lazy
309 @Autowired(required = false) 311 @Autowired(required = false)
310 - @Getter private EdgeEventService edgeEventService; 312 + @Getter
  313 + private EdgeEventService edgeEventService;
311 314
312 @Lazy 315 @Lazy
313 @Autowired(required = false) 316 @Autowired(required = false)
314 - @Getter private EdgeRpcService edgeRpcService; 317 + @Getter
  318 + private EdgeRpcService edgeRpcService;
315 319
316 @Lazy 320 @Lazy
317 @Autowired(required = false) 321 @Autowired(required = false)
318 - @Getter private ResourceService resourceService; 322 + @Getter
  323 + private ResourceService resourceService;
319 324
320 @Lazy 325 @Lazy
321 @Autowired(required = false) 326 @Autowired(required = false)
322 - @Getter private OtaPackageService otaPackageService; 327 + @Getter
  328 + private OtaPackageService otaPackageService;
  329 +
  330 + @Lazy
  331 + @Autowired(required = false)
  332 + @Getter
  333 + private TbRpcService tbRpcService;
323 334
324 @Value("${actors.session.max_concurrent_sessions_per_device:1}") 335 @Value("${actors.session.max_concurrent_sessions_per_device:1}")
325 @Getter 336 @Getter
@@ -46,7 +46,7 @@ public class DeviceActor extends ContextAwareActor { @@ -46,7 +46,7 @@ public class DeviceActor extends ContextAwareActor {
46 super.init(ctx); 46 super.init(ctx);
47 log.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId); 47 log.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId);
48 try { 48 try {
49 - processor.initSessionTimeout(ctx); 49 + processor.init(ctx);
50 log.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId); 50 log.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId);
51 } catch (Exception e) { 51 } catch (Exception e) {
52 log.warn("[{}][{}] Unknown failure", processor.tenantId, processor.deviceId, e); 52 log.warn("[{}][{}] Unknown failure", processor.tenantId, processor.deviceId, e);
@@ -23,6 +23,7 @@ import com.google.common.util.concurrent.MoreExecutors; @@ -23,6 +23,7 @@ import com.google.common.util.concurrent.MoreExecutors;
23 import com.google.protobuf.InvalidProtocolBufferException; 23 import com.google.protobuf.InvalidProtocolBufferException;
24 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
25 import org.apache.commons.collections.CollectionUtils; 25 import org.apache.commons.collections.CollectionUtils;
  26 +import org.thingsboard.common.util.JacksonUtil;
26 import org.thingsboard.rule.engine.api.RpcError; 27 import org.thingsboard.rule.engine.api.RpcError;
27 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; 28 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
28 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg; 29 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
@@ -38,12 +39,17 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType; @@ -38,12 +39,17 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType;
38 import org.thingsboard.server.common.data.edge.EdgeEventType; 39 import org.thingsboard.server.common.data.edge.EdgeEventType;
39 import org.thingsboard.server.common.data.id.DeviceId; 40 import org.thingsboard.server.common.data.id.DeviceId;
40 import org.thingsboard.server.common.data.id.EdgeId; 41 import org.thingsboard.server.common.data.id.EdgeId;
  42 +import org.thingsboard.server.common.data.id.RpcId;
41 import org.thingsboard.server.common.data.id.TenantId; 43 import org.thingsboard.server.common.data.id.TenantId;
42 import org.thingsboard.server.common.data.kv.AttributeKey; 44 import org.thingsboard.server.common.data.kv.AttributeKey;
43 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 45 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
44 import org.thingsboard.server.common.data.kv.KvEntry; 46 import org.thingsboard.server.common.data.kv.KvEntry;
  47 +import org.thingsboard.server.common.data.page.PageData;
  48 +import org.thingsboard.server.common.data.page.PageLink;
45 import org.thingsboard.server.common.data.relation.EntityRelation; 49 import org.thingsboard.server.common.data.relation.EntityRelation;
46 import org.thingsboard.server.common.data.relation.RelationTypeGroup; 50 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
  51 +import org.thingsboard.server.common.data.rpc.Rpc;
  52 +import org.thingsboard.server.common.data.rpc.RpcStatus;
47 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; 53 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
48 import org.thingsboard.server.common.data.security.DeviceCredentials; 54 import org.thingsboard.server.common.data.security.DeviceCredentials;
49 import org.thingsboard.server.common.data.security.DeviceCredentialsType; 55 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
@@ -52,8 +58,8 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -52,8 +58,8 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
52 import org.thingsboard.server.common.msg.queue.TbCallback; 58 import org.thingsboard.server.common.msg.queue.TbCallback;
53 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; 59 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
54 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; 60 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
55 -import org.thingsboard.server.gen.transport.TransportProtos;  
56 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; 61 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
  62 +import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
57 import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; 63 import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
58 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; 64 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
59 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; 65 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
@@ -68,10 +74,12 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionType; @@ -68,10 +74,12 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionType;
68 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; 74 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
69 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg; 75 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
70 import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto; 76 import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto;
  77 +import org.thingsboard.server.gen.transport.TransportProtos.ToDevicePersistedRpcResponseMsg;
71 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; 78 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
72 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; 79 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
73 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; 80 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
74 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; 81 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
  82 +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
75 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; 83 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
76 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; 84 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
77 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 85 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
@@ -162,20 +170,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -162,20 +170,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
162 170
163 void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) { 171 void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) {
164 ToDeviceRpcRequest request = msg.getMsg(); 172 ToDeviceRpcRequest request = msg.getMsg();
165 - ToDeviceRpcRequestBody body = request.getBody();  
166 - ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder()  
167 - .setRequestId(rpcSeq++)  
168 - .setMethodName(body.getMethod())  
169 - .setParams(body.getParams())  
170 - .setExpirationTime(request.getExpirationTime())  
171 - .setRequestIdMSB(request.getId().getMostSignificantBits())  
172 - .setRequestIdLSB(request.getId().getLeastSignificantBits())  
173 - .build(); 173 + ToDeviceRpcRequestMsg rpcRequest = creteToDeviceRpcRequestMsg(request);
174 174
175 long timeout = request.getExpirationTime() - System.currentTimeMillis(); 175 long timeout = request.getExpirationTime() - System.currentTimeMillis();
  176 + boolean persisted = request.isPersisted();
  177 +
176 if (timeout <= 0) { 178 if (timeout <= 0) {
177 log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime()); 179 log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime());
  180 + if (persisted) {
  181 + createRpc(request, RpcStatus.TIMEOUT);
  182 + }
178 return; 183 return;
  184 + } else if (persisted) {
  185 + createRpc(request, RpcStatus.QUEUED);
179 } 186 }
180 187
181 boolean sent; 188 boolean sent;
@@ -192,10 +199,16 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -192,10 +199,16 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
192 syncSessionSet.add(key); 199 syncSessionSet.add(key);
193 } 200 }
194 }); 201 });
195 - log.trace("46) Rpc syncSessionSet [{}] subscription after sent [{}]",syncSessionSet, rpcSubscriptions); 202 + log.trace("46) Rpc syncSessionSet [{}] subscription after sent [{}]", syncSessionSet, rpcSubscriptions);
196 syncSessionSet.forEach(rpcSubscriptions::remove); 203 syncSessionSet.forEach(rpcSubscriptions::remove);
197 } 204 }
198 205
  206 + if (persisted && !(sent || request.isOneway())) {
  207 + ObjectNode response = JacksonUtil.newObjectNode();
  208 + response.put("rpcId", request.getId().toString());
  209 + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), JacksonUtil.toString(response), null));
  210 + }
  211 +
199 if (request.isOneway() && sent) { 212 if (request.isOneway() && sent) {
200 log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); 213 log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
201 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null)); 214 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null));
@@ -209,6 +222,32 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -209,6 +222,32 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
209 } 222 }
210 } 223 }
211 224
  225 + private Rpc createRpc(ToDeviceRpcRequest request, RpcStatus status) {
  226 + Rpc rpc = new Rpc(new RpcId(request.getId()));
  227 + rpc.setCreatedTime(System.currentTimeMillis());
  228 + rpc.setTenantId(tenantId);
  229 + rpc.setDeviceId(deviceId);
  230 + rpc.setExpirationTime(request.getExpirationTime());
  231 + rpc.setRequest(JacksonUtil.valueToTree(request));
  232 + rpc.setStatus(status);
  233 + systemContext.getTbRpcService().save(tenantId, rpc);
  234 + return systemContext.getTbRpcService().save(tenantId, rpc);
  235 + }
  236 +
  237 + private ToDeviceRpcRequestMsg creteToDeviceRpcRequestMsg(ToDeviceRpcRequest request) {
  238 + ToDeviceRpcRequestBody body = request.getBody();
  239 + return ToDeviceRpcRequestMsg.newBuilder()
  240 + .setRequestId(rpcSeq++)
  241 + .setMethodName(body.getMethod())
  242 + .setParams(body.getParams())
  243 + .setExpirationTime(request.getExpirationTime())
  244 + .setRequestIdMSB(request.getId().getMostSignificantBits())
  245 + .setRequestIdLSB(request.getId().getLeastSignificantBits())
  246 + .setOneway(request.isOneway())
  247 + .setPersisted(request.isPersisted())
  248 + .build();
  249 + }
  250 +
212 void processRpcResponsesFromEdge(TbActorCtx context, FromDeviceRpcResponseActorMsg responseMsg) { 251 void processRpcResponsesFromEdge(TbActorCtx context, FromDeviceRpcResponseActorMsg responseMsg) {
213 log.debug("[{}] Processing rpc command response from edge session", deviceId); 252 log.debug("[{}] Processing rpc command response from edge session", deviceId);
214 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); 253 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
@@ -230,6 +269,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -230,6 +269,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
230 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId()); 269 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
231 if (requestMd != null) { 270 if (requestMd != null) {
232 log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); 271 log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
  272 + systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.TIMEOUT, null);
233 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), 273 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
234 null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); 274 null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));
235 } 275 }
@@ -271,7 +311,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -271,7 +311,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
271 .setExpirationTime(request.getExpirationTime()) 311 .setExpirationTime(request.getExpirationTime())
272 .setRequestIdMSB(request.getId().getMostSignificantBits()) 312 .setRequestIdMSB(request.getId().getMostSignificantBits())
273 .setRequestIdLSB(request.getId().getLeastSignificantBits()) 313 .setRequestIdLSB(request.getId().getLeastSignificantBits())
  314 + .setOneway(request.isOneway())
  315 + .setPersisted(request.isPersisted())
274 .build(); 316 .build();
  317 +
275 sendToTransport(rpcRequest, sessionId, nodeId); 318 sendToTransport(rpcRequest, sessionId, nodeId);
276 }; 319 };
277 } 320 }
@@ -279,31 +322,39 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -279,31 +322,39 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
279 void process(TbActorCtx context, TransportToDeviceActorMsgWrapper wrapper) { 322 void process(TbActorCtx context, TransportToDeviceActorMsgWrapper wrapper) {
280 TransportToDeviceActorMsg msg = wrapper.getMsg(); 323 TransportToDeviceActorMsg msg = wrapper.getMsg();
281 TbCallback callback = wrapper.getCallback(); 324 TbCallback callback = wrapper.getCallback();
  325 + var sessionInfo = msg.getSessionInfo();
  326 +
282 if (msg.hasSessionEvent()) { 327 if (msg.hasSessionEvent()) {
283 - processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent()); 328 + processSessionStateMsgs(sessionInfo, msg.getSessionEvent());
284 } 329 }
285 if (msg.hasSubscribeToAttributes()) { 330 if (msg.hasSubscribeToAttributes()) {
286 - processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToAttributes()); 331 + processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToAttributes());
287 } 332 }
288 if (msg.hasSubscribeToRPC()) { 333 if (msg.hasSubscribeToRPC()) {
289 - processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToRPC()); 334 + processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToRPC());
  335 + }
  336 + if (msg.hasSendPendingRPC()) {
  337 + sendPendingRequests(context, getSessionId(sessionInfo), sessionInfo);
290 } 338 }
291 if (msg.hasGetAttributes()) { 339 if (msg.hasGetAttributes()) {
292 - handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes()); 340 + handleGetAttributesRequest(context, sessionInfo, msg.getGetAttributes());
293 } 341 }
294 if (msg.hasToDeviceRPCCallResponse()) { 342 if (msg.hasToDeviceRPCCallResponse()) {
295 - processRpcResponses(context, msg.getSessionInfo(), msg.getToDeviceRPCCallResponse()); 343 + processRpcResponses(context, sessionInfo, msg.getToDeviceRPCCallResponse());
296 } 344 }
297 if (msg.hasSubscriptionInfo()) { 345 if (msg.hasSubscriptionInfo()) {
298 - handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo()); 346 + handleSessionActivity(context, sessionInfo, msg.getSubscriptionInfo());
299 } 347 }
300 if (msg.hasClaimDevice()) { 348 if (msg.hasClaimDevice()) {
301 - handleClaimDeviceMsg(context, msg.getSessionInfo(), msg.getClaimDevice()); 349 + handleClaimDeviceMsg(context, sessionInfo, msg.getClaimDevice());
  350 + }
  351 + if (msg.hasPersistedRpcResponseMsg()) {
  352 + processPersistedRpcResponses(context, sessionInfo, msg.getPersistedRpcResponseMsg());
302 } 353 }
303 callback.onSuccess(); 354 callback.onSuccess();
304 } 355 }
305 356
306 - private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, TransportProtos.ClaimDeviceMsg msg) { 357 + private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, ClaimDeviceMsg msg) {
307 DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); 358 DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
308 systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs()); 359 systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs());
309 } 360 }
@@ -442,11 +493,22 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -442,11 +493,22 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
442 if (success) { 493 if (success) {
443 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), 494 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
444 responseMsg.getPayload(), null)); 495 responseMsg.getPayload(), null));
  496 + if (requestMd.getMsg().getMsg().isPersisted()) {
  497 + systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.SUCCESSFUL, JacksonUtil.toJsonNode(responseMsg.getPayload()));
  498 + }
445 } else { 499 } else {
446 log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId()); 500 log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
  501 + if (requestMd.getMsg().getMsg().isPersisted()) {
  502 + systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.FAILED, JacksonUtil.toJsonNode(responseMsg.getPayload()));
  503 + }
447 } 504 }
448 } 505 }
449 506
  507 + private void processPersistedRpcResponses(TbActorCtx context, SessionInfoProto sessionInfo, ToDevicePersistedRpcResponseMsg responseMsg) {
  508 + UUID rpcId = new UUID(responseMsg.getRequestIdMSB(), responseMsg.getRequestIdLSB());
  509 + systemContext.getTbRpcService().save(tenantId, new RpcId(rpcId), RpcStatus.valueOf(responseMsg.getStatus()), null);
  510 + }
  511 +
450 private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) { 512 private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) {
451 UUID sessionId = getSessionId(sessionInfo); 513 UUID sessionId = getSessionId(sessionInfo);
452 if (subscribeCmd.getUnsubscribe()) { 514 if (subscribeCmd.getUnsubscribe()) {
@@ -565,7 +627,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -565,7 +627,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
565 627
566 void notifyTransportAboutProfileUpdate(UUID sessionId, SessionInfoMetaData sessionMd, DeviceCredentials deviceCredentials) { 628 void notifyTransportAboutProfileUpdate(UUID sessionId, SessionInfoMetaData sessionMd, DeviceCredentials deviceCredentials) {
567 log.info("2) LwM2Mtype: "); 629 log.info("2) LwM2Mtype: ");
568 - TransportProtos.ToTransportUpdateCredentialsProto.Builder notification = TransportProtos.ToTransportUpdateCredentialsProto.newBuilder(); 630 + ToTransportUpdateCredentialsProto.Builder notification = ToTransportUpdateCredentialsProto.newBuilder();
569 notification.addCredentialsId(deviceCredentials.getCredentialsId()); 631 notification.addCredentialsId(deviceCredentials.getCredentialsId());
570 notification.addCredentialsValue(deviceCredentials.getCredentialsValue()); 632 notification.addCredentialsValue(deviceCredentials.getCredentialsValue());
571 ToTransportMsg msg = ToTransportMsg.newBuilder() 633 ToTransportMsg msg = ToTransportMsg.newBuilder()
@@ -640,7 +702,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -640,7 +702,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
640 ListenableFuture<EdgeEvent> future = systemContext.getEdgeEventService().saveAsync(edgeEvent); 702 ListenableFuture<EdgeEvent> future = systemContext.getEdgeEventService().saveAsync(edgeEvent);
641 Futures.addCallback(future, new FutureCallback<EdgeEvent>() { 703 Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
642 @Override 704 @Override
643 - public void onSuccess( EdgeEvent result) { 705 + public void onSuccess(EdgeEvent result) {
644 systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId); 706 systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
645 } 707 }
646 708
@@ -756,8 +818,26 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -756,8 +818,26 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
756 .addAllSessions(sessionsList).build().toByteArray()); 818 .addAllSessions(sessionsList).build().toByteArray());
757 } 819 }
758 820
759 - void initSessionTimeout(TbActorCtx ctx) { 821 + void init(TbActorCtx ctx) {
760 schedulePeriodicMsgWithDelay(ctx, SessionTimeoutCheckMsg.instance(), systemContext.getSessionReportTimeout(), systemContext.getSessionReportTimeout()); 822 schedulePeriodicMsgWithDelay(ctx, SessionTimeoutCheckMsg.instance(), systemContext.getSessionReportTimeout(), systemContext.getSessionReportTimeout());
  823 + PageLink pageLink = new PageLink(1024);
  824 + PageData<Rpc> pageData;
  825 + do {
  826 + pageData = systemContext.getTbRpcService().findAllByDeviceIdAndStatus(tenantId, deviceId, RpcStatus.QUEUED, pageLink);
  827 + pageData.getData().forEach(rpc -> {
  828 + ToDeviceRpcRequest msg = JacksonUtil.convertValue(rpc.getRequest(), ToDeviceRpcRequest.class);
  829 + long timeout = rpc.getExpirationTime() - System.currentTimeMillis();
  830 + if (timeout <= 0) {
  831 + rpc.setStatus(RpcStatus.TIMEOUT);
  832 + systemContext.getTbRpcService().save(tenantId, rpc);
  833 + } else {
  834 + registerPendingRpcRequest(ctx, new ToDeviceRpcRequestActorMsg(systemContext.getServiceId(), msg), false, creteToDeviceRpcRequestMsg(msg), timeout);
  835 + }
  836 + });
  837 + if (pageData.hasNext()) {
  838 + pageLink = pageLink.nextPageLink();
  839 + }
  840 + } while (pageData.hasNext());
761 } 841 }
762 842
763 void checkSessionsTimeout() { 843 void checkSessionsTimeout() {
@@ -69,6 +69,7 @@ import org.thingsboard.server.common.data.id.EntityId; @@ -69,6 +69,7 @@ import org.thingsboard.server.common.data.id.EntityId;
69 import org.thingsboard.server.common.data.id.EntityIdFactory; 69 import org.thingsboard.server.common.data.id.EntityIdFactory;
70 import org.thingsboard.server.common.data.id.EntityViewId; 70 import org.thingsboard.server.common.data.id.EntityViewId;
71 import org.thingsboard.server.common.data.id.OtaPackageId; 71 import org.thingsboard.server.common.data.id.OtaPackageId;
  72 +import org.thingsboard.server.common.data.id.RpcId;
72 import org.thingsboard.server.common.data.id.TbResourceId; 73 import org.thingsboard.server.common.data.id.TbResourceId;
73 import org.thingsboard.server.common.data.id.RuleChainId; 74 import org.thingsboard.server.common.data.id.RuleChainId;
74 import org.thingsboard.server.common.data.id.RuleNodeId; 75 import org.thingsboard.server.common.data.id.RuleNodeId;
@@ -83,6 +84,7 @@ import org.thingsboard.server.common.data.page.TimePageLink; @@ -83,6 +84,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
83 import org.thingsboard.server.common.data.plugin.ComponentDescriptor; 84 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
84 import org.thingsboard.server.common.data.plugin.ComponentType; 85 import org.thingsboard.server.common.data.plugin.ComponentType;
85 import org.thingsboard.server.common.data.relation.EntityRelation; 86 import org.thingsboard.server.common.data.relation.EntityRelation;
  87 +import org.thingsboard.server.common.data.rpc.Rpc;
86 import org.thingsboard.server.common.data.rule.RuleChain; 88 import org.thingsboard.server.common.data.rule.RuleChain;
87 import org.thingsboard.server.common.data.rule.RuleChainType; 89 import org.thingsboard.server.common.data.rule.RuleChainType;
88 import org.thingsboard.server.common.data.rule.RuleNode; 90 import org.thingsboard.server.common.data.rule.RuleNode;
@@ -106,6 +108,7 @@ import org.thingsboard.server.dao.model.ModelConstants; @@ -106,6 +108,7 @@ import org.thingsboard.server.dao.model.ModelConstants;
106 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; 108 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
107 import org.thingsboard.server.dao.oauth2.OAuth2Service; 109 import org.thingsboard.server.dao.oauth2.OAuth2Service;
108 import org.thingsboard.server.dao.relation.RelationService; 110 import org.thingsboard.server.dao.relation.RelationService;
  111 +import org.thingsboard.server.dao.rpc.RpcService;
109 import org.thingsboard.server.dao.rule.RuleChainService; 112 import org.thingsboard.server.dao.rule.RuleChainService;
110 import org.thingsboard.server.dao.tenant.TbTenantProfileCache; 113 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
111 import org.thingsboard.server.dao.tenant.TenantProfileService; 114 import org.thingsboard.server.dao.tenant.TenantProfileService;
@@ -246,6 +249,9 @@ public abstract class BaseController { @@ -246,6 +249,9 @@ public abstract class BaseController {
246 protected OtaPackageStateService otaPackageStateService; 249 protected OtaPackageStateService otaPackageStateService;
247 250
248 @Autowired 251 @Autowired
  252 + protected RpcService rpcService;
  253 +
  254 + @Autowired
249 protected TbQueueProducerProvider producerProvider; 255 protected TbQueueProducerProvider producerProvider;
250 256
251 @Autowired 257 @Autowired
@@ -786,6 +792,18 @@ public abstract class BaseController { @@ -786,6 +792,18 @@ public abstract class BaseController {
786 } 792 }
787 } 793 }
788 794
  795 + Rpc checkRpcId(RpcId rpcId, Operation operation) throws ThingsboardException {
  796 + try {
  797 + validateId(rpcId, "Incorrect rpcId " + rpcId);
  798 + Rpc rpc = rpcService.findById(getCurrentUser().getTenantId(), rpcId);
  799 + checkNotNull(rpc);
  800 + accessControlService.checkPermission(getCurrentUser(), Resource.RPC, operation, rpcId, rpc);
  801 + return rpc;
  802 + } catch (Exception e) {
  803 + throw handleException(e, false);
  804 + }
  805 + }
  806 +
789 @SuppressWarnings("unchecked") 807 @SuppressWarnings("unchecked")
790 protected <I extends EntityId> I emptyId(EntityType entityType) { 808 protected <I extends EntityId> I emptyId(EntityType entityType) {
791 return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); 809 return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
@@ -29,6 +29,7 @@ import org.springframework.web.bind.annotation.PathVariable; @@ -29,6 +29,7 @@ import org.springframework.web.bind.annotation.PathVariable;
29 import org.springframework.web.bind.annotation.RequestBody; 29 import org.springframework.web.bind.annotation.RequestBody;
30 import org.springframework.web.bind.annotation.RequestMapping; 30 import org.springframework.web.bind.annotation.RequestMapping;
31 import org.springframework.web.bind.annotation.RequestMethod; 31 import org.springframework.web.bind.annotation.RequestMethod;
  32 +import org.springframework.web.bind.annotation.RequestParam;
32 import org.springframework.web.bind.annotation.ResponseBody; 33 import org.springframework.web.bind.annotation.ResponseBody;
33 import org.springframework.web.bind.annotation.RestController; 34 import org.springframework.web.bind.annotation.RestController;
34 import org.springframework.web.context.request.async.DeferredResult; 35 import org.springframework.web.context.request.async.DeferredResult;
@@ -38,8 +39,13 @@ import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; @@ -38,8 +39,13 @@ import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
38 import org.thingsboard.server.common.data.exception.ThingsboardException; 39 import org.thingsboard.server.common.data.exception.ThingsboardException;
39 import org.thingsboard.server.common.data.id.DeviceId; 40 import org.thingsboard.server.common.data.id.DeviceId;
40 import org.thingsboard.server.common.data.id.EntityId; 41 import org.thingsboard.server.common.data.id.EntityId;
  42 +import org.thingsboard.server.common.data.id.RpcId;
41 import org.thingsboard.server.common.data.id.TenantId; 43 import org.thingsboard.server.common.data.id.TenantId;
42 import org.thingsboard.server.common.data.id.UUIDBased; 44 import org.thingsboard.server.common.data.id.UUIDBased;
  45 +import org.thingsboard.server.common.data.page.PageData;
  46 +import org.thingsboard.server.common.data.page.PageLink;
  47 +import org.thingsboard.server.common.data.rpc.Rpc;
  48 +import org.thingsboard.server.common.data.rpc.RpcStatus;
43 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; 49 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
44 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; 50 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
45 import org.thingsboard.server.queue.util.TbCoreComponent; 51 import org.thingsboard.server.queue.util.TbCoreComponent;
@@ -93,6 +99,52 @@ public class RpcController extends BaseController { @@ -93,6 +99,52 @@ public class RpcController extends BaseController {
93 return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody); 99 return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody);
94 } 100 }
95 101
  102 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  103 + @RequestMapping(value = "/persisted/{rpcId}", method = RequestMethod.GET)
  104 + @ResponseBody
  105 + public Rpc getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
  106 + checkParameter("RpcId", strRpc);
  107 + try {
  108 + RpcId rpcId = new RpcId(UUID.fromString(strRpc));
  109 + return checkRpcId(rpcId, Operation.READ);
  110 + } catch (Exception e) {
  111 + throw handleException(e);
  112 + }
  113 + }
  114 +
  115 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  116 + @RequestMapping(value = "/persisted/{deviceId}", method = RequestMethod.GET)
  117 + @ResponseBody
  118 + public PageData<Rpc> getPersistedRpcByDevice(@PathVariable("deviceId") String strDeviceId,
  119 + @RequestParam int pageSize,
  120 + @RequestParam int page,
  121 + @RequestParam RpcStatus rpcStatus,
  122 + @RequestParam(required = false) String textSearch,
  123 + @RequestParam(required = false) String sortProperty,
  124 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  125 + checkParameter("DeviceId", strDeviceId);
  126 + try {
  127 + TenantId tenantId = getCurrentUser().getTenantId();
  128 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  129 + DeviceId deviceId = new DeviceId(UUID.fromString(strDeviceId));
  130 + return checkNotNull(rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink));
  131 + } catch (Exception e) {
  132 + throw handleException(e);
  133 + }
  134 + }
  135 +
  136 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  137 + @RequestMapping(value = "/persisted/{rpcId}", method = RequestMethod.DELETE)
  138 + @ResponseBody
  139 + public void deleteResource(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
  140 + checkParameter("RpcId", strRpc);
  141 + try {
  142 + rpcService.deleteRpc(getTenantId(), new RpcId(UUID.fromString(strRpc)));
  143 + } catch (Exception e) {
  144 + throw handleException(e);
  145 + }
  146 + }
  147 +
96 private DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException { 148 private DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException {
97 try { 149 try {
98 JsonNode rpcRequestBody = jsonMapper.readTree(requestBody); 150 JsonNode rpcRequestBody = jsonMapper.readTree(requestBody);
@@ -103,6 +155,7 @@ public class RpcController extends BaseController { @@ -103,6 +155,7 @@ public class RpcController extends BaseController {
103 long timeout = rpcRequestBody.has("timeout") ? rpcRequestBody.get("timeout").asLong() : defaultTimeout; 155 long timeout = rpcRequestBody.has("timeout") ? rpcRequestBody.get("timeout").asLong() : defaultTimeout;
104 long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout); 156 long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout);
105 UUID rpcRequestUUID = rpcRequestBody.has("requestUUID") ? UUID.fromString(rpcRequestBody.get("requestUUID").asText()) : UUID.randomUUID(); 157 UUID rpcRequestUUID = rpcRequestBody.has("requestUUID") ? UUID.fromString(rpcRequestBody.get("requestUUID").asText()) : UUID.randomUUID();
  158 + boolean persisted = rpcRequestBody.has("persisted") && rpcRequestBody.get("persisted").asBoolean();
106 accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() { 159 accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() {
107 @Override 160 @Override
108 public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) { 161 public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
@@ -111,7 +164,8 @@ public class RpcController extends BaseController { @@ -111,7 +164,8 @@ public class RpcController extends BaseController {
111 deviceId, 164 deviceId,
112 oneWay, 165 oneWay,
113 expTime, 166 expTime,
114 - body 167 + body,
  168 + persisted
115 ); 169 );
116 deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse), currentUser); 170 deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse), currentUser);
117 } 171 }
@@ -157,6 +157,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { @@ -157,6 +157,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
157 metaData.putValue("originServiceId", serviceId); 157 metaData.putValue("originServiceId", serviceId);
158 metaData.putValue("expirationTime", Long.toString(msg.getExpirationTime())); 158 metaData.putValue("expirationTime", Long.toString(msg.getExpirationTime()));
159 metaData.putValue("oneway", Boolean.toString(msg.isOneway())); 159 metaData.putValue("oneway", Boolean.toString(msg.isOneway()));
  160 + metaData.putValue("persisted", Boolean.toString(msg.isPersisted()));
160 161
161 Device device = deviceService.findDeviceById(msg.getTenantId(), msg.getDeviceId()); 162 Device device = deviceService.findDeviceById(msg.getTenantId(), msg.getDeviceId());
162 if (device != null) { 163 if (device != null) {
@@ -100,7 +100,7 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi @@ -100,7 +100,7 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi
100 @Override 100 @Override
101 public void sendRpcRequestToDevice(RuleEngineDeviceRpcRequest src, Consumer<RuleEngineDeviceRpcResponse> consumer) { 101 public void sendRpcRequestToDevice(RuleEngineDeviceRpcRequest src, Consumer<RuleEngineDeviceRpcResponse> consumer) {
102 ToDeviceRpcRequest request = new ToDeviceRpcRequest(src.getRequestUUID(), src.getTenantId(), src.getDeviceId(), 102 ToDeviceRpcRequest request = new ToDeviceRpcRequest(src.getRequestUUID(), src.getTenantId(), src.getDeviceId(),
103 - src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody())); 103 + src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()), src.isPersisted());
104 forwardRpcRequestToDeviceActor(request, response -> { 104 forwardRpcRequestToDeviceActor(request, response -> {
105 if (src.isRestApiCall()) { 105 if (src.isRestApiCall()) {
106 sendRpcResponseToTbCore(src.getOriginServiceId(), response); 106 sendRpcResponseToTbCore(src.getOriginServiceId(), response);
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.rpc;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import lombok.RequiredArgsConstructor;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.stereotype.Service;
  22 +import org.thingsboard.common.util.JacksonUtil;
  23 +import org.thingsboard.server.common.data.id.DeviceId;
  24 +import org.thingsboard.server.common.data.id.RpcId;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.page.PageData;
  27 +import org.thingsboard.server.common.data.page.PageLink;
  28 +import org.thingsboard.server.common.data.rpc.Rpc;
  29 +import org.thingsboard.server.common.data.rpc.RpcStatus;
  30 +import org.thingsboard.server.common.msg.TbMsg;
  31 +import org.thingsboard.server.common.msg.TbMsgMetaData;
  32 +import org.thingsboard.server.dao.rpc.RpcService;
  33 +import org.thingsboard.server.queue.util.TbCoreComponent;
  34 +import org.thingsboard.server.service.queue.TbClusterService;
  35 +
  36 +@TbCoreComponent
  37 +@Service
  38 +@RequiredArgsConstructor
  39 +@Slf4j
  40 +public class TbRpcService {
  41 + private final RpcService rpcService;
  42 + private final TbClusterService tbClusterService;
  43 +
  44 + public Rpc save(TenantId tenantId, Rpc rpc) {
  45 + Rpc saved = rpcService.save(rpc);
  46 + pushRpcMsgToRuleEngine(tenantId, saved);
  47 + return saved;
  48 + }
  49 +
  50 + public void save(TenantId tenantId, RpcId rpcId, RpcStatus newStatus, JsonNode response) {
  51 + Rpc foundRpc = rpcService.findById(tenantId, rpcId);
  52 + if (foundRpc != null) {
  53 + foundRpc.setStatus(newStatus);
  54 + if (response != null) {
  55 + foundRpc.setResponse(response);
  56 + }
  57 + Rpc saved = rpcService.save(foundRpc);
  58 + pushRpcMsgToRuleEngine(tenantId, saved);
  59 + } else {
  60 + log.warn("[{}] Failed to update RPC status because RPC was already deleted", rpcId);
  61 + }
  62 + }
  63 +
  64 + private void pushRpcMsgToRuleEngine(TenantId tenantId, Rpc rpc) {
  65 + TbMsg msg = TbMsg.newMsg("RPC_" + rpc.getStatus().name(), rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc));
  66 + tbClusterService.pushMsgToRuleEngine(tenantId, rpc.getId(), msg, null);
  67 + }
  68 +
  69 + public Rpc findRpcById(TenantId tenantId, RpcId rpcId) {
  70 + return rpcService.findById(tenantId, rpcId);
  71 + }
  72 +
  73 + public PageData<Rpc> findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) {
  74 + return rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink);
  75 + }
  76 +
  77 +}
@@ -47,11 +47,13 @@ import org.thingsboard.server.common.data.id.EntityId; @@ -47,11 +47,13 @@ import org.thingsboard.server.common.data.id.EntityId;
47 import org.thingsboard.server.common.data.id.EntityIdFactory; 47 import org.thingsboard.server.common.data.id.EntityIdFactory;
48 import org.thingsboard.server.common.data.id.EntityViewId; 48 import org.thingsboard.server.common.data.id.EntityViewId;
49 import org.thingsboard.server.common.data.id.OtaPackageId; 49 import org.thingsboard.server.common.data.id.OtaPackageId;
  50 +import org.thingsboard.server.common.data.id.RpcId;
50 import org.thingsboard.server.common.data.id.RuleChainId; 51 import org.thingsboard.server.common.data.id.RuleChainId;
51 import org.thingsboard.server.common.data.id.RuleNodeId; 52 import org.thingsboard.server.common.data.id.RuleNodeId;
52 import org.thingsboard.server.common.data.id.TbResourceId; 53 import org.thingsboard.server.common.data.id.TbResourceId;
53 import org.thingsboard.server.common.data.id.TenantId; 54 import org.thingsboard.server.common.data.id.TenantId;
54 import org.thingsboard.server.common.data.id.UserId; 55 import org.thingsboard.server.common.data.id.UserId;
  56 +import org.thingsboard.server.common.data.rpc.Rpc;
55 import org.thingsboard.server.common.data.rule.RuleChain; 57 import org.thingsboard.server.common.data.rule.RuleChain;
56 import org.thingsboard.server.common.data.rule.RuleNode; 58 import org.thingsboard.server.common.data.rule.RuleNode;
57 import org.thingsboard.server.controller.HttpValidationCallback; 59 import org.thingsboard.server.controller.HttpValidationCallback;
@@ -65,6 +67,7 @@ import org.thingsboard.server.dao.entityview.EntityViewService; @@ -65,6 +67,7 @@ import org.thingsboard.server.dao.entityview.EntityViewService;
65 import org.thingsboard.server.dao.exception.IncorrectParameterException; 67 import org.thingsboard.server.dao.exception.IncorrectParameterException;
66 import org.thingsboard.server.dao.ota.OtaPackageService; 68 import org.thingsboard.server.dao.ota.OtaPackageService;
67 import org.thingsboard.server.dao.resource.ResourceService; 69 import org.thingsboard.server.dao.resource.ResourceService;
  70 +import org.thingsboard.server.dao.rpc.RpcService;
68 import org.thingsboard.server.dao.rule.RuleChainService; 71 import org.thingsboard.server.dao.rule.RuleChainService;
69 import org.thingsboard.server.dao.tenant.TenantService; 72 import org.thingsboard.server.dao.tenant.TenantService;
70 import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; 73 import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
@@ -137,6 +140,9 @@ public class AccessValidator { @@ -137,6 +140,9 @@ public class AccessValidator {
137 @Autowired 140 @Autowired
138 protected OtaPackageService otaPackageService; 141 protected OtaPackageService otaPackageService;
139 142
  143 + @Autowired
  144 + protected RpcService rpcService;
  145 +
140 private ExecutorService executor; 146 private ExecutorService executor;
141 147
142 @PostConstruct 148 @PostConstruct
@@ -235,6 +241,9 @@ public class AccessValidator { @@ -235,6 +241,9 @@ public class AccessValidator {
235 case OTA_PACKAGE: 241 case OTA_PACKAGE:
236 validateOtaPackage(currentUser, operation, entityId, callback); 242 validateOtaPackage(currentUser, operation, entityId, callback);
237 return; 243 return;
  244 + case RPC:
  245 + validateRpc(currentUser, operation, entityId, callback);
  246 + return;
238 default: 247 default:
239 //TODO: add support of other entities 248 //TODO: add support of other entities
240 throw new IllegalStateException("Not Implemented!"); 249 throw new IllegalStateException("Not Implemented!");
@@ -261,6 +270,22 @@ public class AccessValidator { @@ -261,6 +270,22 @@ public class AccessValidator {
261 } 270 }
262 } 271 }
263 272
  273 + private void validateRpc(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
  274 + ListenableFuture<Rpc> rpcFurure = rpcService.findRpcByIdAsync(currentUser.getTenantId(), new RpcId(entityId.getId()));
  275 + Futures.addCallback(rpcFurure, getCallback(callback, rpc -> {
  276 + if (rpc == null) {
  277 + return ValidationResult.entityNotFound("Rpc with requested id wasn't found!");
  278 + } else {
  279 + try {
  280 + accessControlService.checkPermission(currentUser, Resource.RPC, operation, entityId, rpc);
  281 + } catch (ThingsboardException e) {
  282 + return ValidationResult.accessDenied(e.getMessage());
  283 + }
  284 + return ValidationResult.ok(rpc);
  285 + }
  286 + }), executor);
  287 + }
  288 +
264 private void validateDeviceProfile(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) { 289 private void validateDeviceProfile(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
265 if (currentUser.isSystemAdmin()) { 290 if (currentUser.isSystemAdmin()) {
266 callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); 291 callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
@@ -41,6 +41,7 @@ public class CustomerUserPermissions extends AbstractPermissions { @@ -41,6 +41,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
41 put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker); 41 put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker);
42 put(Resource.WIDGET_TYPE, widgetsPermissionChecker); 42 put(Resource.WIDGET_TYPE, widgetsPermissionChecker);
43 put(Resource.EDGE, customerEntityPermissionChecker); 43 put(Resource.EDGE, customerEntityPermissionChecker);
  44 + put(Resource.RPC, rpcPermissionChecker);
44 } 45 }
45 46
46 private static final PermissionChecker customerEntityPermissionChecker = 47 private static final PermissionChecker customerEntityPermissionChecker =
@@ -138,4 +139,22 @@ public class CustomerUserPermissions extends AbstractPermissions { @@ -138,4 +139,22 @@ public class CustomerUserPermissions extends AbstractPermissions {
138 } 139 }
139 140
140 }; 141 };
  142 +
  143 + private static final PermissionChecker rpcPermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ) {
  144 +
  145 + @Override
  146 + @SuppressWarnings("unchecked")
  147 + public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
  148 + if (!super.hasPermission(user, operation, entityId, entity)) {
  149 + return false;
  150 + }
  151 + if (entity.getTenantId() == null || entity.getTenantId().isNullUid()) {
  152 + return true;
  153 + }
  154 + if (!user.getTenantId().equals(entity.getTenantId())) {
  155 + return false;
  156 + }
  157 + return true;
  158 + }
  159 + };
141 } 160 }
@@ -39,7 +39,8 @@ public enum Resource { @@ -39,7 +39,8 @@ public enum Resource {
39 API_USAGE_STATE(EntityType.API_USAGE_STATE), 39 API_USAGE_STATE(EntityType.API_USAGE_STATE),
40 TB_RESOURCE(EntityType.TB_RESOURCE), 40 TB_RESOURCE(EntityType.TB_RESOURCE),
41 OTA_PACKAGE(EntityType.OTA_PACKAGE), 41 OTA_PACKAGE(EntityType.OTA_PACKAGE),
42 - EDGE(EntityType.EDGE); 42 + EDGE(EntityType.EDGE),
  43 + RPC(EntityType.RPC);
43 44
44 private final EntityType entityType; 45 private final EntityType entityType;
45 46
@@ -44,6 +44,7 @@ public class TenantAdminPermissions extends AbstractPermissions { @@ -44,6 +44,7 @@ public class TenantAdminPermissions extends AbstractPermissions {
44 put(Resource.TB_RESOURCE, tbResourcePermissionChecker); 44 put(Resource.TB_RESOURCE, tbResourcePermissionChecker);
45 put(Resource.OTA_PACKAGE, tenantEntityPermissionChecker); 45 put(Resource.OTA_PACKAGE, tenantEntityPermissionChecker);
46 put(Resource.EDGE, tenantEntityPermissionChecker); 46 put(Resource.EDGE, tenantEntityPermissionChecker);
  47 + put(Resource.RPC, tenantEntityPermissionChecker);
47 } 48 }
48 49
49 public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { 50 public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() {
@@ -22,6 +22,7 @@ import com.google.common.util.concurrent.Futures; @@ -22,6 +22,7 @@ import com.google.common.util.concurrent.Futures;
22 import com.google.common.util.concurrent.ListenableFuture; 22 import com.google.common.util.concurrent.ListenableFuture;
23 import com.google.common.util.concurrent.MoreExecutors; 23 import com.google.common.util.concurrent.MoreExecutors;
24 import com.google.protobuf.ByteString; 24 import com.google.protobuf.ByteString;
  25 +import lombok.RequiredArgsConstructor;
25 import lombok.extern.slf4j.Slf4j; 26 import lombok.extern.slf4j.Slf4j;
26 import org.springframework.stereotype.Service; 27 import org.springframework.stereotype.Service;
27 import org.springframework.util.StringUtils; 28 import org.springframework.util.StringUtils;
@@ -41,13 +42,13 @@ import org.thingsboard.server.common.data.TenantProfile; @@ -41,13 +42,13 @@ import org.thingsboard.server.common.data.TenantProfile;
41 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; 42 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
42 import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData; 43 import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData;
43 import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; 44 import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials;
44 -import org.thingsboard.server.common.data.ota.OtaPackageType;  
45 -import org.thingsboard.server.common.data.ota.OtaPackageUtil;  
46 import org.thingsboard.server.common.data.id.CustomerId; 45 import org.thingsboard.server.common.data.id.CustomerId;
47 import org.thingsboard.server.common.data.id.DeviceId; 46 import org.thingsboard.server.common.data.id.DeviceId;
48 import org.thingsboard.server.common.data.id.DeviceProfileId; 47 import org.thingsboard.server.common.data.id.DeviceProfileId;
49 import org.thingsboard.server.common.data.id.OtaPackageId; 48 import org.thingsboard.server.common.data.id.OtaPackageId;
50 import org.thingsboard.server.common.data.id.TenantId; 49 import org.thingsboard.server.common.data.id.TenantId;
  50 +import org.thingsboard.server.common.data.ota.OtaPackageType;
  51 +import org.thingsboard.server.common.data.ota.OtaPackageUtil;
51 import org.thingsboard.server.common.data.page.PageData; 52 import org.thingsboard.server.common.data.page.PageData;
52 import org.thingsboard.server.common.data.page.PageLink; 53 import org.thingsboard.server.common.data.page.PageLink;
53 import org.thingsboard.server.common.data.relation.EntityRelation; 54 import org.thingsboard.server.common.data.relation.EntityRelation;
@@ -108,6 +109,7 @@ import java.util.stream.Collectors; @@ -108,6 +109,7 @@ import java.util.stream.Collectors;
108 @Slf4j 109 @Slf4j
109 @Service 110 @Service
110 @TbCoreComponent 111 @TbCoreComponent
  112 +@RequiredArgsConstructor
111 public class DefaultTransportApiService implements TransportApiService { 113 public class DefaultTransportApiService implements TransportApiService {
112 114
113 private static final ObjectMapper mapper = new ObjectMapper(); 115 private static final ObjectMapper mapper = new ObjectMapper();
@@ -129,28 +131,6 @@ public class DefaultTransportApiService implements TransportApiService { @@ -129,28 +131,6 @@ public class DefaultTransportApiService implements TransportApiService {
129 131
130 private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); 132 private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>();
131 133
132 - public DefaultTransportApiService(TbDeviceProfileCache deviceProfileCache,  
133 - TbTenantProfileCache tenantProfileCache, TbApiUsageStateService apiUsageStateService, DeviceService deviceService,  
134 - RelationService relationService, DeviceCredentialsService deviceCredentialsService,  
135 - DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService,  
136 - TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService,  
137 - DeviceProvisionService deviceProvisionService, TbResourceService resourceService, OtaPackageService otaPackageService, OtaPackageDataCache otaPackageDataCache) {  
138 - this.deviceProfileCache = deviceProfileCache;  
139 - this.tenantProfileCache = tenantProfileCache;  
140 - this.apiUsageStateService = apiUsageStateService;  
141 - this.deviceService = deviceService;  
142 - this.relationService = relationService;  
143 - this.deviceCredentialsService = deviceCredentialsService;  
144 - this.deviceStateService = deviceStateService;  
145 - this.dbCallbackExecutorService = dbCallbackExecutorService;  
146 - this.tbClusterService = tbClusterService;  
147 - this.dataDecodingEncodingService = dataDecodingEncodingService;  
148 - this.deviceProvisionService = deviceProvisionService;  
149 - this.resourceService = resourceService;  
150 - this.otaPackageService = otaPackageService;  
151 - this.otaPackageDataCache = otaPackageDataCache;  
152 - }  
153 -  
154 @Override 134 @Override
155 public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) { 135 public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) {
156 TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue(); 136 TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue();
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.ttl.rpc;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.beans.factory.annotation.Value;
  21 +import org.springframework.scheduling.annotation.Scheduled;
  22 +import org.springframework.stereotype.Service;
  23 +import org.thingsboard.server.common.data.id.TenantId;
  24 +import org.thingsboard.server.common.data.page.PageData;
  25 +import org.thingsboard.server.common.data.page.PageLink;
  26 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
  27 +import org.thingsboard.server.common.msg.queue.ServiceType;
  28 +import org.thingsboard.server.dao.rpc.RpcDao;
  29 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
  30 +import org.thingsboard.server.dao.tenant.TenantDao;
  31 +import org.thingsboard.server.queue.discovery.PartitionService;
  32 +import org.thingsboard.server.queue.util.TbCoreComponent;
  33 +
  34 +import java.util.Date;
  35 +import java.util.Optional;
  36 +import java.util.concurrent.TimeUnit;
  37 +
  38 +@TbCoreComponent
  39 +@Service
  40 +@Slf4j
  41 +@RequiredArgsConstructor
  42 +public class RpcCleanUpService {
  43 + @Value("${sql.ttl.rpc.enabled}")
  44 + private boolean ttlTaskExecutionEnabled;
  45 +
  46 + private final TenantDao tenantDao;
  47 + private final PartitionService partitionService;
  48 + private final TbTenantProfileCache tenantProfileCache;
  49 + private final RpcDao rpcDao;
  50 +
  51 + @Scheduled(initialDelayString = "#{T(org.apache.commons.lang3.RandomUtils).nextLong(0, ${sql.ttl.rpc.checking_interval})}", fixedDelayString = "${sql.ttl.rpc.checking_interval}")
  52 + public void cleanUp() {
  53 + if (ttlTaskExecutionEnabled) {
  54 + PageLink tenantsBatchRequest = new PageLink(10_000, 0);
  55 + PageData<TenantId> tenantsIds;
  56 + do {
  57 + tenantsIds = tenantDao.findTenantsIds(tenantsBatchRequest);
  58 + for (TenantId tenantId : tenantsIds.getData()) {
  59 + if (!partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) {
  60 + continue;
  61 + }
  62 +
  63 + Optional<DefaultTenantProfileConfiguration> tenantProfileConfiguration = tenantProfileCache.get(tenantId).getProfileConfiguration();
  64 + if (tenantProfileConfiguration.isEmpty() || tenantProfileConfiguration.get().getRpcTtlDays() == 0) {
  65 + continue;
  66 + }
  67 +
  68 + long ttl = TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getRpcTtlDays());
  69 + long expirationTime = System.currentTimeMillis() - ttl;
  70 +
  71 + long totalRemoved = rpcDao.deleteOutdatedRpcByTenantId(tenantId, expirationTime);
  72 +
  73 + if (totalRemoved > 0) {
  74 + log.info("Removed {} outdated rpc(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime));
  75 + }
  76 + }
  77 +
  78 + tenantsBatchRequest = tenantsBatchRequest.nextPageLink();
  79 + } while (tenantsIds.hasNext());
  80 + }
  81 + }
  82 +
  83 +}
@@ -276,6 +276,9 @@ sql: @@ -276,6 +276,9 @@ sql:
276 alarms: 276 alarms:
277 checking_interval: "${SQL_ALARMS_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours 277 checking_interval: "${SQL_ALARMS_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours
278 removal_batch_size: "${SQL_ALARMS_TTL_REMOVAL_BATCH_SIZE:3000}" # To delete outdated alarms not all at once but in batches 278 removal_batch_size: "${SQL_ALARMS_TTL_REMOVAL_BATCH_SIZE:3000}" # To delete outdated alarms not all at once but in batches
  279 + rpc:
  280 + enabled: "${SQL_TTL_RPC_ENABLED:true}"
  281 + checking_interval: "${SQL_RPC_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours
279 282
280 # Actor system parameters 283 # Actor system parameters
281 actors: 284 actors:
@@ -75,13 +75,11 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { @@ -75,13 +75,11 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
75 return device; 75 return device;
76 } 76 }
77 77
78 - //TODO: use different endpoints to isolate tests.  
79 - @Ignore()  
80 @Test 78 @Test
81 public void testConnectAndObserveTelemetry() throws Exception { 79 public void testConnectAndObserveTelemetry() throws Exception {
82 createDeviceProfile(TRANSPORT_CONFIGURATION); 80 createDeviceProfile(TRANSPORT_CONFIGURATION);
83 X509ClientCredentials credentials = new X509ClientCredentials(); 81 X509ClientCredentials credentials = new X509ClientCredentials();
84 - credentials.setEndpoint(endpoint+1); 82 + credentials.setEndpoint(endpoint);
85 Device device = createDevice(credentials); 83 Device device = createDevice(credentials);
86 84
87 SingleEntityFilter sef = new SingleEntityFilter(); 85 SingleEntityFilter sef = new SingleEntityFilter();
@@ -99,7 +97,7 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { @@ -99,7 +97,7 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
99 wsClient.waitForReply(); 97 wsClient.waitForReply();
100 98
101 wsClient.registerWaitForUpdate(); 99 wsClient.registerWaitForUpdate();
102 - LwM2MTestClient client = new LwM2MTestClient(executor, endpoint+1); 100 + LwM2MTestClient client = new LwM2MTestClient(executor, endpoint);
103 Security security = x509(serverUri, 123, clientX509Cert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded()); 101 Security security = x509(serverUri, 123, clientX509Cert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded());
104 client.init(security, coapConfig); 102 client.init(security, coapConfig);
105 String msg = wsClient.waitForUpdate(); 103 String msg = wsClient.waitForUpdate();
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 <logger name="org.springframework.boot.test" level="WARN"/> 14 <logger name="org.springframework.boot.test" level="WARN"/>
15 <logger name="org.apache.cassandra" level="WARN"/> 15 <logger name="org.apache.cassandra" level="WARN"/>
16 <logger name="org.cassandraunit" level="INFO"/> 16 <logger name="org.cassandraunit" level="INFO"/>
17 - <logger name="org.eclipse.leshan" level="TRACE"/> 17 + <logger name="org.eclipse.leshan" level="INFO"/>
18 18
19 19
20 <root level="WARN"> 20 <root level="WARN">
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.rpc;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.id.DeviceId;
  20 +import org.thingsboard.server.common.data.id.RpcId;
  21 +import org.thingsboard.server.common.data.id.TenantId;
  22 +import org.thingsboard.server.common.data.page.PageData;
  23 +import org.thingsboard.server.common.data.page.PageLink;
  24 +import org.thingsboard.server.common.data.rpc.Rpc;
  25 +import org.thingsboard.server.common.data.rpc.RpcStatus;
  26 +
  27 +public interface RpcService {
  28 + Rpc save(Rpc rpc);
  29 +
  30 + void deleteRpc(TenantId tenantId, RpcId id);
  31 +
  32 + void deleteAllRpcByTenantId(TenantId tenantId);
  33 +
  34 + Rpc findById(TenantId tenantId, RpcId id);
  35 +
  36 + ListenableFuture<Rpc> findRpcByIdAsync(TenantId tenantId, RpcId id);
  37 +
  38 + PageData<Rpc> findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink);
  39 +}
@@ -76,6 +76,12 @@ public class DataConstants { @@ -76,6 +76,12 @@ public class DataConstants {
76 76
77 public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE"; 77 public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE";
78 78
  79 + public static final String RPC_QUEUED = "RPC_QUEUED";
  80 + public static final String RPC_DELIVERED = "RPC_DELIVERED";
  81 + public static final String RPC_SUCCESSFUL = "RPC_SUCCESSFUL";
  82 + public static final String RPC_TIMEOUT = "RPC_TIMEOUT";
  83 + public static final String RPC_FAILED = "RPC_FAILED";
  84 +
79 public static final String DEFAULT_SECRET_KEY = ""; 85 public static final String DEFAULT_SECRET_KEY = "";
80 public static final String SECRET_KEY_FIELD_NAME = "secretKey"; 86 public static final String SECRET_KEY_FIELD_NAME = "secretKey";
81 public static final String DURATION_MS_FIELD_NAME = "durationMs"; 87 public static final String DURATION_MS_FIELD_NAME = "durationMs";
@@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data;
19 * @author Andrew Shvayka 19 * @author Andrew Shvayka
20 */ 20 */
21 public enum EntityType { 21 public enum EntityType {
22 - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE; 22 + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE, RPC;
23 } 23 }
@@ -75,6 +75,8 @@ public class EntityIdFactory { @@ -75,6 +75,8 @@ public class EntityIdFactory {
75 return new OtaPackageId(uuid); 75 return new OtaPackageId(uuid);
76 case EDGE: 76 case EDGE:
77 return new EdgeId(uuid); 77 return new EdgeId(uuid);
  78 + case RPC:
  79 + return new RpcId(uuid);
78 } 80 }
79 throw new IllegalArgumentException("EntityType " + type + " is not supported!"); 81 throw new IllegalArgumentException("EntityType " + type + " is not supported!");
80 } 82 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.id;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonCreator;
  19 +import com.fasterxml.jackson.annotation.JsonIgnore;
  20 +import com.fasterxml.jackson.annotation.JsonProperty;
  21 +import org.thingsboard.server.common.data.EntityType;
  22 +
  23 +import java.util.UUID;
  24 +
  25 +public final class RpcId extends UUIDBased implements EntityId {
  26 +
  27 + private static final long serialVersionUID = 1L;
  28 +
  29 + @JsonCreator
  30 + public RpcId(@JsonProperty("id") UUID id) {
  31 + super(id);
  32 + }
  33 +
  34 + @JsonIgnore
  35 + @Override
  36 + public EntityType getEntityType() {
  37 + return EntityType.RPC;
  38 + }
  39 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.rpc;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import lombok.Data;
  20 +import lombok.EqualsAndHashCode;
  21 +import org.thingsboard.server.common.data.BaseData;
  22 +import org.thingsboard.server.common.data.HasTenantId;
  23 +import org.thingsboard.server.common.data.id.DeviceId;
  24 +import org.thingsboard.server.common.data.id.RpcId;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +
  27 +@Data
  28 +@EqualsAndHashCode(callSuper = true)
  29 +public class Rpc extends BaseData<RpcId> implements HasTenantId {
  30 + private TenantId tenantId;
  31 + private DeviceId deviceId;
  32 + private long expirationTime;
  33 + private JsonNode request;
  34 + private JsonNode response;
  35 + private RpcStatus status;
  36 +
  37 + public Rpc() {
  38 + super();
  39 + }
  40 +
  41 + public Rpc(RpcId id) {
  42 + super(id);
  43 + }
  44 +
  45 + public Rpc(Rpc rpc) {
  46 + super(rpc);
  47 + this.tenantId = rpc.getTenantId();
  48 + this.deviceId = rpc.getDeviceId();
  49 + this.expirationTime = rpc.getExpirationTime();
  50 + this.request = rpc.getRequest();
  51 + this.response = rpc.getResponse();
  52 + this.status = rpc.getStatus();
  53 + }
  54 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.rpc;
  17 +
  18 +public enum RpcStatus {
  19 + QUEUED, DELIVERED, SUCCESSFUL, TIMEOUT, FAILED
  20 +}
@@ -56,6 +56,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura @@ -56,6 +56,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
56 56
57 private int defaultStorageTtlDays; 57 private int defaultStorageTtlDays;
58 private int alarmsTtlDays; 58 private int alarmsTtlDays;
  59 + private int rpcTtlDays;
59 60
60 private double warnThreshold; 61 private double warnThreshold;
61 62
@@ -34,5 +34,6 @@ public class ToDeviceRpcRequest implements Serializable { @@ -34,5 +34,6 @@ public class ToDeviceRpcRequest implements Serializable {
34 private final boolean oneway; 34 private final boolean oneway;
35 private final long expirationTime; 35 private final long expirationTime;
36 private final ToDeviceRpcRequestBody body; 36 private final ToDeviceRpcRequestBody body;
  37 + private final boolean persisted;
37 } 38 }
38 39
@@ -318,6 +318,9 @@ message SubscribeToRPCMsg { @@ -318,6 +318,9 @@ message SubscribeToRPCMsg {
318 SessionType sessionType = 2; 318 SessionType sessionType = 2;
319 } 319 }
320 320
  321 +message SendPendingRPCMsg {
  322 +}
  323 +
321 message ToDeviceRpcRequestMsg { 324 message ToDeviceRpcRequestMsg {
322 int32 requestId = 1; 325 int32 requestId = 1;
323 string methodName = 2; 326 string methodName = 2;
@@ -325,6 +328,8 @@ message ToDeviceRpcRequestMsg { @@ -325,6 +328,8 @@ message ToDeviceRpcRequestMsg {
325 int64 expirationTime = 4; 328 int64 expirationTime = 4;
326 int64 requestIdMSB = 5; 329 int64 requestIdMSB = 5;
327 int64 requestIdLSB = 6; 330 int64 requestIdLSB = 6;
  331 + bool oneway = 7;
  332 + bool persisted = 8;
328 } 333 }
329 334
330 message ToDeviceRpcResponseMsg { 335 message ToDeviceRpcResponseMsg {
@@ -332,6 +337,13 @@ message ToDeviceRpcResponseMsg { @@ -332,6 +337,13 @@ message ToDeviceRpcResponseMsg {
332 string payload = 2; 337 string payload = 2;
333 } 338 }
334 339
  340 +message ToDevicePersistedRpcResponseMsg {
  341 + int32 requestId = 1;
  342 + int64 requestIdMSB = 2;
  343 + int64 requestIdLSB = 3;
  344 + string status = 4;
  345 +}
  346 +
335 message ToServerRpcRequestMsg { 347 message ToServerRpcRequestMsg {
336 int32 requestId = 1; 348 int32 requestId = 1;
337 string methodName = 2; 349 string methodName = 2;
@@ -435,6 +447,8 @@ message TransportToDeviceActorMsg { @@ -435,6 +447,8 @@ message TransportToDeviceActorMsg {
435 SubscriptionInfoProto subscriptionInfo = 7; 447 SubscriptionInfoProto subscriptionInfo = 7;
436 ClaimDeviceMsg claimDevice = 8; 448 ClaimDeviceMsg claimDevice = 8;
437 ProvisionDeviceRequestMsg provisionDevice = 9; 449 ProvisionDeviceRequestMsg provisionDevice = 9;
  450 + ToDevicePersistedRpcResponseMsg persistedRpcResponseMsg = 10;
  451 + SendPendingRPCMsg sendPendingRPC = 11;
438 } 452 }
439 453
440 message TransportToRuleEngineMsg { 454 message TransportToRuleEngineMsg {
@@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC @@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
44 import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; 44 import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;
45 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; 45 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
46 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; 46 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
  47 +import org.thingsboard.server.common.data.rpc.RpcStatus;
47 import org.thingsboard.server.common.data.security.DeviceTokenCredentials; 48 import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
48 import org.thingsboard.server.common.msg.session.FeatureType; 49 import org.thingsboard.server.common.msg.session.FeatureType;
49 import org.thingsboard.server.common.msg.session.SessionMsgType; 50 import org.thingsboard.server.common.msg.session.SessionMsgType;
@@ -332,14 +333,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -332,14 +333,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
332 break; 333 break;
333 case TO_SERVER_RPC_REQUEST: 334 case TO_SERVER_RPC_REQUEST:
334 transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor, 335 transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor,
335 - transportConfigurationContainer.getRpcRequestDynamicMessageBuilder()), timeout); 336 + transportConfigurationContainer.getRpcRequestDynamicMessageBuilder(), sessionInfo), timeout);
336 transportService.process(sessionInfo, 337 transportService.process(sessionInfo,
337 coapTransportAdaptor.convertToServerRpcRequest(sessionId, request), 338 coapTransportAdaptor.convertToServerRpcRequest(sessionId, request),
338 new CoapNoOpCallback(exchange)); 339 new CoapNoOpCallback(exchange));
339 break; 340 break;
340 case GET_ATTRIBUTES_REQUEST: 341 case GET_ATTRIBUTES_REQUEST:
341 transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor, 342 transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor,
342 - transportConfigurationContainer.getRpcRequestDynamicMessageBuilder()), timeout); 343 + transportConfigurationContainer.getRpcRequestDynamicMessageBuilder(), sessionInfo), timeout);
343 transportService.process(sessionInfo, 344 transportService.process(sessionInfo,
344 coapTransportAdaptor.convertToGetAttributes(sessionId, request), 345 coapTransportAdaptor.convertToGetAttributes(sessionId, request),
345 new CoapNoOpCallback(exchange)); 346 new CoapNoOpCallback(exchange));
@@ -362,12 +363,12 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -362,12 +363,12 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
362 363
363 private void registerAsyncCoapSession(CoapExchange exchange, TransportProtos.SessionInfoProto sessionInfo, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder, String token) { 364 private void registerAsyncCoapSession(CoapExchange exchange, TransportProtos.SessionInfoProto sessionInfo, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder, String token) {
364 tokenToSessionInfoMap.putIfAbsent(token, sessionInfo); 365 tokenToSessionInfoMap.putIfAbsent(token, sessionInfo);
365 - transportService.registerAsyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder)); 366 + transportService.registerAsyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder, sessionInfo));
366 transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); 367 transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null);
367 } 368 }
368 369
369 - private CoapSessionListener getCoapSessionListener(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) {  
370 - return new CoapSessionListener(this, exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder); 370 + private CoapSessionListener getCoapSessionListener(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder, TransportProtos.SessionInfoProto sessionInfo) {
  371 + return new CoapSessionListener(this, exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder, sessionInfo);
371 } 372 }
372 373
373 private String getTokenFromRequest(Request request) { 374 private String getTokenFromRequest(Request request) {
@@ -455,12 +456,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -455,12 +456,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
455 private final CoapExchange exchange; 456 private final CoapExchange exchange;
456 private final CoapTransportAdaptor coapTransportAdaptor; 457 private final CoapTransportAdaptor coapTransportAdaptor;
457 private final DynamicMessage.Builder rpcRequestDynamicMessageBuilder; 458 private final DynamicMessage.Builder rpcRequestDynamicMessageBuilder;
  459 + private final TransportProtos.SessionInfoProto sessionInfo;
458 460
459 - CoapSessionListener(CoapTransportResource coapTransportResource, CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) { 461 + CoapSessionListener(CoapTransportResource coapTransportResource, CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder, TransportProtos.SessionInfoProto sessionInfo) {
460 this.coapTransportResource = coapTransportResource; 462 this.coapTransportResource = coapTransportResource;
461 this.exchange = exchange; 463 this.exchange = exchange;
462 this.coapTransportAdaptor = coapTransportAdaptor; 464 this.coapTransportAdaptor = coapTransportAdaptor;
463 this.rpcRequestDynamicMessageBuilder = rpcRequestDynamicMessageBuilder; 465 this.rpcRequestDynamicMessageBuilder = rpcRequestDynamicMessageBuilder;
  466 + this.sessionInfo = sessionInfo;
464 } 467 }
465 468
466 @Override 469 @Override
@@ -503,11 +506,31 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -503,11 +506,31 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
503 506
504 @Override 507 @Override
505 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg msg) { 508 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg msg) {
  509 + boolean successful;
506 try { 510 try {
507 exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg, rpcRequestDynamicMessageBuilder)); 511 exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg, rpcRequestDynamicMessageBuilder));
  512 + successful = true;
508 } catch (AdaptorException e) { 513 } catch (AdaptorException e) {
509 log.trace("Failed to reply due to error", e); 514 log.trace("Failed to reply due to error", e);
510 exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); 515 exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
  516 + successful = false;
  517 + }
  518 + if (msg.getPersisted()) {
  519 + RpcStatus status;
  520 + if (!successful) {
  521 + status = RpcStatus.FAILED;
  522 + } else if (msg.getOneway()) {
  523 + status = RpcStatus.SUCCESSFUL;
  524 + } else {
  525 + status = RpcStatus.DELIVERED;
  526 + }
  527 + TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
  528 + .setRequestId(msg.getRequestId())
  529 + .setRequestIdLSB(msg.getRequestIdLSB())
  530 + .setRequestIdMSB(msg.getRequestIdMSB())
  531 + .setStatus(status.name())
  532 + .build();
  533 + coapTransportResource.transportService.process(sessionInfo, responseMsg, TransportServiceCallback.EMPTY);
511 } 534 }
512 } 535 }
513 536
@@ -18,11 +18,12 @@ package org.thingsboard.server.transport.coap; @@ -18,11 +18,12 @@ package org.thingsboard.server.transport.coap;
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.eclipse.californium.core.CoapResource; 19 import org.eclipse.californium.core.CoapResource;
20 import org.eclipse.californium.core.CoapServer; 20 import org.eclipse.californium.core.CoapServer;
  21 +import org.eclipse.californium.core.network.config.NetworkConfig;
21 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
22 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
23 -import org.thingsboard.server.common.data.TbTransportService;  
24 import org.thingsboard.server.coapserver.CoapServerService; 24 import org.thingsboard.server.coapserver.CoapServerService;
25 import org.thingsboard.server.coapserver.TbCoapServerComponent; 25 import org.thingsboard.server.coapserver.TbCoapServerComponent;
  26 +import org.thingsboard.server.common.data.TbTransportService;
26 import org.thingsboard.server.common.data.ota.OtaPackageType; 27 import org.thingsboard.server.common.data.ota.OtaPackageType;
27 import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource; 28 import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource;
28 29
@@ -30,6 +31,8 @@ import javax.annotation.PostConstruct; @@ -30,6 +31,8 @@ import javax.annotation.PostConstruct;
30 import javax.annotation.PreDestroy; 31 import javax.annotation.PreDestroy;
31 import java.net.UnknownHostException; 32 import java.net.UnknownHostException;
32 33
  34 +import static org.eclipse.californium.core.network.config.NetworkConfigDefaults.DEFAULT_BLOCKWISE_STATUS_LIFETIME;
  35 +
33 @Service("CoapTransportService") 36 @Service("CoapTransportService")
34 @TbCoapServerComponent 37 @TbCoapServerComponent
35 @Slf4j 38 @Slf4j
@@ -52,6 +55,14 @@ public class CoapTransportService implements TbTransportService { @@ -52,6 +55,14 @@ public class CoapTransportService implements TbTransportService {
52 public void init() throws UnknownHostException { 55 public void init() throws UnknownHostException {
53 log.info("Starting CoAP transport..."); 56 log.info("Starting CoAP transport...");
54 coapServer = coapServerService.getCoapServer(); 57 coapServer = coapServerService.getCoapServer();
  58 + coapServer.getConfig().setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true);
  59 + coapServer.getConfig().setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true);
  60 + coapServer.getConfig().setLong(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, DEFAULT_BLOCKWISE_STATUS_LIFETIME);
  61 + coapServer.getConfig().setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024);
  62 + coapServer.getConfig().setString(NetworkConfig.Keys.RESPONSE_MATCHING, "RELAXED");
  63 + coapServer.getConfig().setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024);
  64 + coapServer.getConfig().setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024);
  65 + coapServer.getConfig().setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 10);
55 CoapResource api = new CoapResource(API); 66 CoapResource api = new CoapResource(API);
56 api.add(new CoapTransportResource(coapTransportContext, coapServerService, V1)); 67 api.add(new CoapTransportResource(coapTransportContext, coapServerService, V1));
57 68
@@ -24,6 +24,7 @@ import org.eclipse.californium.core.observe.ObserveRelation; @@ -24,6 +24,7 @@ import org.eclipse.californium.core.observe.ObserveRelation;
24 import org.eclipse.californium.core.server.resources.CoapExchange; 24 import org.eclipse.californium.core.server.resources.CoapExchange;
25 import org.eclipse.californium.core.server.resources.Resource; 25 import org.eclipse.californium.core.server.resources.Resource;
26 import org.eclipse.californium.core.server.resources.ResourceObserver; 26 import org.eclipse.californium.core.server.resources.ResourceObserver;
  27 +import org.thingsboard.common.util.ThingsBoardExecutors;
27 import org.thingsboard.server.common.data.DeviceTransportType; 28 import org.thingsboard.server.common.data.DeviceTransportType;
28 import org.thingsboard.server.common.data.StringUtils; 29 import org.thingsboard.server.common.data.StringUtils;
29 import org.thingsboard.server.common.data.ota.OtaPackageType; 30 import org.thingsboard.server.common.data.ota.OtaPackageType;
@@ -34,6 +35,7 @@ import org.thingsboard.server.gen.transport.TransportProtos; @@ -34,6 +35,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
34 import java.util.List; 35 import java.util.List;
35 import java.util.Optional; 36 import java.util.Optional;
36 import java.util.UUID; 37 import java.util.UUID;
  38 +import java.util.concurrent.ExecutorService;
37 39
38 @Slf4j 40 @Slf4j
39 public class OtaPackageTransportResource extends AbstractCoapTransportResource { 41 public class OtaPackageTransportResource extends AbstractCoapTransportResource {
@@ -43,9 +45,9 @@ public class OtaPackageTransportResource extends AbstractCoapTransportResource { @@ -43,9 +45,9 @@ public class OtaPackageTransportResource extends AbstractCoapTransportResource {
43 45
44 public OtaPackageTransportResource(CoapTransportContext ctx, OtaPackageType otaPackageType) { 46 public OtaPackageTransportResource(CoapTransportContext ctx, OtaPackageType otaPackageType) {
45 super(ctx, otaPackageType.getKeyPrefix()); 47 super(ctx, otaPackageType.getKeyPrefix());
46 - this.setObservable(true);  
47 - this.addObserver(new OtaPackageTransportResource.CoapResourceObserver());  
48 this.otaPackageType = otaPackageType; 48 this.otaPackageType = otaPackageType;
  49 +
  50 + this.setObservable(true);
49 } 51 }
50 52
51 @Override 53 @Override
@@ -138,43 +140,10 @@ public class OtaPackageTransportResource extends AbstractCoapTransportResource { @@ -138,43 +140,10 @@ public class OtaPackageTransportResource extends AbstractCoapTransportResource {
138 response.setPayload(data); 140 response.setPayload(data);
139 if (exchange.getRequestOptions().getBlock2() != null) { 141 if (exchange.getRequestOptions().getBlock2() != null) {
140 int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); 142 int chunkSize = exchange.getRequestOptions().getBlock2().getSzx();
141 - boolean lastFlag = data.length > chunkSize;  
142 - response.getOptions().setUriPath(exchange.getRequestOptions().getUriPathString()); 143 + boolean lastFlag = data.length <= chunkSize;
143 response.getOptions().setBlock2(chunkSize, lastFlag, 0); 144 response.getOptions().setBlock2(chunkSize, lastFlag, 0);
144 } 145 }
145 - exchange.respond(response);  
146 - }  
147 - }  
148 -  
149 - public class CoapResourceObserver implements ResourceObserver {  
150 - @Override  
151 - public void changedName(String old) {  
152 -  
153 - }  
154 -  
155 - @Override  
156 - public void changedPath(String old) {  
157 -  
158 - }  
159 -  
160 - @Override  
161 - public void addedChild(Resource child) {  
162 -  
163 - }  
164 -  
165 - @Override  
166 - public void removedChild(Resource child) {  
167 -  
168 - }  
169 -  
170 - @Override  
171 - public void addedObserveRelation(ObserveRelation relation) {  
172 -  
173 - }  
174 -  
175 - @Override  
176 - public void removedObserveRelation(ObserveRelation relation) {  
177 - 146 + transportContext.getExecutor().submit(() -> exchange.respond(response));
178 } 147 }
179 } 148 }
180 149
@@ -17,6 +17,7 @@ package org.thingsboard.server.transport.http; @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.http;
17 17
18 import com.google.gson.JsonObject; 18 import com.google.gson.JsonObject;
19 import com.google.gson.JsonParser; 19 import com.google.gson.JsonParser;
  20 +import lombok.RequiredArgsConstructor;
20 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
21 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 23 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
@@ -34,9 +35,10 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -34,9 +35,10 @@ import org.springframework.web.bind.annotation.RequestParam;
34 import org.springframework.web.bind.annotation.RestController; 35 import org.springframework.web.bind.annotation.RestController;
35 import org.springframework.web.context.request.async.DeferredResult; 36 import org.springframework.web.context.request.async.DeferredResult;
36 import org.thingsboard.server.common.data.DeviceTransportType; 37 import org.thingsboard.server.common.data.DeviceTransportType;
37 -import org.thingsboard.server.common.data.ota.OtaPackageType;  
38 import org.thingsboard.server.common.data.TbTransportService; 38 import org.thingsboard.server.common.data.TbTransportService;
39 import org.thingsboard.server.common.data.id.DeviceId; 39 import org.thingsboard.server.common.data.id.DeviceId;
  40 +import org.thingsboard.server.common.data.ota.OtaPackageType;
  41 +import org.thingsboard.server.common.data.rpc.RpcStatus;
40 import org.thingsboard.server.common.transport.SessionMsgListener; 42 import org.thingsboard.server.common.transport.SessionMsgListener;
41 import org.thingsboard.server.common.transport.TransportContext; 43 import org.thingsboard.server.common.transport.TransportContext;
42 import org.thingsboard.server.common.transport.TransportService; 44 import org.thingsboard.server.common.transport.TransportService;
@@ -95,7 +97,9 @@ public class DeviceApiController implements TbTransportService { @@ -95,7 +97,9 @@ public class DeviceApiController implements TbTransportService {
95 request.addAllSharedAttributeNames(sharedKeySet); 97 request.addAllSharedAttributeNames(sharedKeySet);
96 } 98 }
97 TransportService transportService = transportContext.getTransportService(); 99 TransportService transportService = transportContext.getTransportService();
98 - transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter), transportContext.getDefaultTimeout()); 100 + transportService.registerSyncSession(sessionInfo,
  101 + new HttpSessionListener(responseWriter, transportContext.getTransportService(), sessionInfo),
  102 + transportContext.getDefaultTimeout());
99 transportService.process(sessionInfo, request.build(), new SessionCloseOnErrorCallback(transportService, sessionInfo)); 103 transportService.process(sessionInfo, request.build(), new SessionCloseOnErrorCallback(transportService, sessionInfo));
100 })); 104 }));
101 return responseWriter; 105 return responseWriter;
@@ -151,7 +155,8 @@ public class DeviceApiController implements TbTransportService { @@ -151,7 +155,8 @@ public class DeviceApiController implements TbTransportService {
151 transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), 155 transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
152 new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { 156 new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
153 TransportService transportService = transportContext.getTransportService(); 157 TransportService transportService = transportContext.getTransportService();
154 - transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter), 158 + transportService.registerSyncSession(sessionInfo,
  159 + new HttpSessionListener(responseWriter, transportContext.getTransportService(), sessionInfo),
155 timeout == 0 ? transportContext.getDefaultTimeout() : timeout); 160 timeout == 0 ? transportContext.getDefaultTimeout() : timeout);
156 transportService.process(sessionInfo, SubscribeToRPCMsg.getDefaultInstance(), 161 transportService.process(sessionInfo, SubscribeToRPCMsg.getDefaultInstance(),
157 new SessionCloseOnErrorCallback(transportService, sessionInfo)); 162 new SessionCloseOnErrorCallback(transportService, sessionInfo));
@@ -181,7 +186,9 @@ public class DeviceApiController implements TbTransportService { @@ -181,7 +186,9 @@ public class DeviceApiController implements TbTransportService {
181 new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { 186 new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
182 JsonObject request = new JsonParser().parse(json).getAsJsonObject(); 187 JsonObject request = new JsonParser().parse(json).getAsJsonObject();
183 TransportService transportService = transportContext.getTransportService(); 188 TransportService transportService = transportContext.getTransportService();
184 - transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter), transportContext.getDefaultTimeout()); 189 + transportService.registerSyncSession(sessionInfo,
  190 + new HttpSessionListener(responseWriter, transportContext.getTransportService(), sessionInfo),
  191 + transportContext.getDefaultTimeout());
185 transportService.process(sessionInfo, ToServerRpcRequestMsg.newBuilder().setRequestId(0) 192 transportService.process(sessionInfo, ToServerRpcRequestMsg.newBuilder().setRequestId(0)
186 .setMethodName(request.get("method").getAsString()) 193 .setMethodName(request.get("method").getAsString())
187 .setParams(request.get("params").toString()).build(), 194 .setParams(request.get("params").toString()).build(),
@@ -198,7 +205,8 @@ public class DeviceApiController implements TbTransportService { @@ -198,7 +205,8 @@ public class DeviceApiController implements TbTransportService {
198 transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), 205 transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
199 new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { 206 new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
200 TransportService transportService = transportContext.getTransportService(); 207 TransportService transportService = transportContext.getTransportService();
201 - transportService.registerSyncSession(sessionInfo, new HttpSessionListener(responseWriter), 208 + transportService.registerSyncSession(sessionInfo,
  209 + new HttpSessionListener(responseWriter, transportContext.getTransportService(), sessionInfo),
202 timeout == 0 ? transportContext.getDefaultTimeout() : timeout); 210 timeout == 0 ? transportContext.getDefaultTimeout() : timeout);
203 transportService.process(sessionInfo, SubscribeToAttributeUpdatesMsg.getDefaultInstance(), 211 transportService.process(sessionInfo, SubscribeToAttributeUpdatesMsg.getDefaultInstance(),
204 new SessionCloseOnErrorCallback(transportService, sessionInfo)); 212 new SessionCloseOnErrorCallback(transportService, sessionInfo));
@@ -372,13 +380,12 @@ public class DeviceApiController implements TbTransportService { @@ -372,13 +380,12 @@ public class DeviceApiController implements TbTransportService {
372 } 380 }
373 } 381 }
374 382
  383 + @RequiredArgsConstructor
375 private static class HttpSessionListener implements SessionMsgListener { 384 private static class HttpSessionListener implements SessionMsgListener {
376 385
377 private final DeferredResult<ResponseEntity> responseWriter; 386 private final DeferredResult<ResponseEntity> responseWriter;
378 -  
379 - HttpSessionListener(DeferredResult<ResponseEntity> responseWriter) {  
380 - this.responseWriter = responseWriter;  
381 - } 387 + private final TransportService transportService;
  388 + private final SessionInfoProto sessionInfo;
382 389
383 @Override 390 @Override
384 public void onGetAttributesResponse(GetAttributeResponseMsg msg) { 391 public void onGetAttributesResponse(GetAttributeResponseMsg msg) {
@@ -399,6 +406,21 @@ public class DeviceApiController implements TbTransportService { @@ -399,6 +406,21 @@ public class DeviceApiController implements TbTransportService {
399 @Override 406 @Override
400 public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg msg) { 407 public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg msg) {
401 responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK)); 408 responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK));
  409 + if (msg.getPersisted()) {
  410 + RpcStatus status;
  411 + if (msg.getOneway()) {
  412 + status = RpcStatus.SUCCESSFUL;
  413 + } else {
  414 + status = RpcStatus.DELIVERED;
  415 + }
  416 + TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
  417 + .setRequestId(msg.getRequestId())
  418 + .setRequestIdLSB(msg.getRequestIdLSB())
  419 + .setRequestIdMSB(msg.getRequestIdMSB())
  420 + .setStatus(status.name())
  421 + .build();
  422 + transportService.process(sessionInfo, responseMsg, TransportServiceCallback.EMPTY);
  423 + }
402 } 424 }
403 425
404 @Override 426 @Override
@@ -29,12 +29,11 @@ import org.springframework.stereotype.Service; @@ -29,12 +29,11 @@ import org.springframework.stereotype.Service;
29 import org.thingsboard.common.util.JacksonUtil; 29 import org.thingsboard.common.util.JacksonUtil;
30 import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; 30 import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration;
31 import org.thingsboard.server.gen.transport.TransportProtos; 31 import org.thingsboard.server.gen.transport.TransportProtos;
32 -import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;  
33 import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; 32 import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
  33 +import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
34 import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; 34 import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener;
35 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; 35 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
36 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; 36 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
37 -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;  
38 37
39 import java.io.IOException; 38 import java.io.IOException;
40 import java.security.GeneralSecurityException; 39 import java.security.GeneralSecurityException;
@@ -46,6 +45,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L @@ -46,6 +45,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L
46 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; 45 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO;
47 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; 46 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY;
48 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getBootstrapParametersFromThingsboard; 47 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getBootstrapParametersFromThingsboard;
  48 +import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP;
49 49
50 @Slf4j 50 @Slf4j
51 @Service("LwM2MBootstrapSecurityStore") 51 @Service("LwM2MBootstrapSecurityStore")
@@ -68,7 +68,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @@ -68,7 +68,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
68 68
69 @Override 69 @Override
70 public Iterator<SecurityInfo> getAllByEndpoint(String endPoint) { 70 public Iterator<SecurityInfo> getAllByEndpoint(String endPoint) {
71 - TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP); 71 + TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, BOOTSTRAP);
72 if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { 72 if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
73 /* add value to store from BootstrapJson */ 73 /* add value to store from BootstrapJson */
74 this.setBootstrapConfigScurityInfo(store); 74 this.setBootstrapConfigScurityInfo(store);
@@ -92,7 +92,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @@ -92,7 +92,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
92 92
93 @Override 93 @Override
94 public SecurityInfo getByIdentity(String identity) { 94 public SecurityInfo getByIdentity(String identity) {
95 - TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP); 95 + TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, BOOTSTRAP);
96 if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { 96 if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
97 /* add value to store from BootstrapJson */ 97 /* add value to store from BootstrapJson */
98 this.setBootstrapConfigScurityInfo(store); 98 this.setBootstrapConfigScurityInfo(store);
@@ -155,7 +155,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { @@ -155,7 +155,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
155 LwM2MServerBootstrap profileLwm2mServer = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getLwm2mServer()), LwM2MServerBootstrap.class); 155 LwM2MServerBootstrap profileLwm2mServer = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getLwm2mServer()), LwM2MServerBootstrap.class);
156 UUID sessionUUiD = UUID.randomUUID(); 156 UUID sessionUUiD = UUID.randomUUID();
157 TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); 157 TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits());
158 - context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, null, null, sessionInfo)); 158 + context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, null, null, sessionInfo, context.getTransportService()));
159 if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { 159 if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {
160 lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); 160 lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
161 lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); 161 lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
@@ -142,7 +142,7 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig { @@ -142,7 +142,7 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig {
142 URI uri = null; 142 URI uri = null;
143 try { 143 try {
144 uri = Resources.getResource(keyStorePathFile).toURI(); 144 uri = Resources.getResource(keyStorePathFile).toURI();
145 - log.error("URI: {}", uri); 145 + log.info("URI: {}", uri);
146 File keyStoreFile = new File(uri); 146 File keyStoreFile = new File(uri);
147 InputStream inKeyStore = new FileInputStream(keyStoreFile); 147 InputStream inKeyStore = new FileInputStream(keyStoreFile);
148 keyStoreValue = KeyStore.getInstance(keyStoreType); 148 keyStoreValue = KeyStore.getInstance(keyStoreType);
@@ -33,7 +33,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; @@ -33,7 +33,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
33 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; 33 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
34 import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; 34 import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
35 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; 35 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
36 -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; 36 +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer;
37 37
38 import java.io.IOException; 38 import java.io.IOException;
39 import java.security.GeneralSecurityException; 39 import java.security.GeneralSecurityException;
@@ -45,6 +45,7 @@ import static org.eclipse.leshan.core.SecurityMode.NO_SEC; @@ -45,6 +45,7 @@ import static org.eclipse.leshan.core.SecurityMode.NO_SEC;
45 import static org.eclipse.leshan.core.SecurityMode.PSK; 45 import static org.eclipse.leshan.core.SecurityMode.PSK;
46 import static org.eclipse.leshan.core.SecurityMode.RPK; 46 import static org.eclipse.leshan.core.SecurityMode.RPK;
47 import static org.eclipse.leshan.core.SecurityMode.X509; 47 import static org.eclipse.leshan.core.SecurityMode.X509;
  48 +import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.BOOTSTRAP;
48 49
49 @Slf4j 50 @Slf4j
50 @Component 51 @Component
@@ -55,13 +56,15 @@ public class LwM2mCredentialsSecurityInfoValidator { @@ -55,13 +56,15 @@ public class LwM2mCredentialsSecurityInfoValidator {
55 private final LwM2mTransportContext context; 56 private final LwM2mTransportContext context;
56 private final LwM2MTransportServerConfig config; 57 private final LwM2MTransportServerConfig config;
57 58
58 - public TbLwM2MSecurityInfo getEndpointSecurityInfoByCredentialsId(String credentialsId, LwM2mTransportUtil.LwM2mTypeServer keyValue) { 59 + public TbLwM2MSecurityInfo getEndpointSecurityInfoByCredentialsId(String credentialsId, LwM2mTypeServer keyValue) {
59 CountDownLatch latch = new CountDownLatch(1); 60 CountDownLatch latch = new CountDownLatch(1);
60 final TbLwM2MSecurityInfo[] resultSecurityStore = new TbLwM2MSecurityInfo[1]; 61 final TbLwM2MSecurityInfo[] resultSecurityStore = new TbLwM2MSecurityInfo[1];
  62 + log.trace("Validating credentials [{}]", credentialsId);
61 context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(credentialsId).build(), 63 context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(credentialsId).build(),
62 new TransportServiceCallback<>() { 64 new TransportServiceCallback<>() {
63 @Override 65 @Override
64 public void onSuccess(ValidateDeviceCredentialsResponse msg) { 66 public void onSuccess(ValidateDeviceCredentialsResponse msg) {
  67 + log.trace("Validated credentials: [{}] [{}]", credentialsId, msg);
65 String credentialsBody = msg.getCredentials(); 68 String credentialsBody = msg.getCredentials();
66 resultSecurityStore[0] = createSecurityInfo(credentialsId, credentialsBody, keyValue); 69 resultSecurityStore[0] = createSecurityInfo(credentialsId, credentialsBody, keyValue);
67 resultSecurityStore[0].setMsg(msg); 70 resultSecurityStore[0].setMsg(msg);
@@ -91,11 +94,11 @@ public class LwM2mCredentialsSecurityInfoValidator { @@ -91,11 +94,11 @@ public class LwM2mCredentialsSecurityInfoValidator {
91 * @param keyValue - 94 * @param keyValue -
92 * @return SecurityInfo 95 * @return SecurityInfo
93 */ 96 */
94 - private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTransportUtil.LwM2mTypeServer keyValue) { 97 + private TbLwM2MSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTypeServer keyValue) {
95 TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo(); 98 TbLwM2MSecurityInfo result = new TbLwM2MSecurityInfo();
96 LwM2MCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MCredentials.class); 99 LwM2MCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MCredentials.class);
97 if (credentials != null) { 100 if (credentials != null) {
98 - if (keyValue.equals(LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP)) { 101 + if (keyValue.equals(BOOTSTRAP)) {
99 result.setBootstrapCredentialConfig(credentials.getBootstrap()); 102 result.setBootstrapCredentialConfig(credentials.getBootstrap());
100 if (LwM2MSecurityMode.PSK.equals(credentials.getClient().getSecurityConfigClientMode())) { 103 if (LwM2MSecurityMode.PSK.equals(credentials.getClient().getSecurityConfigClientMode())) {
101 PSKClientCredentials pskClientConfig = (PSKClientCredentials) credentials.getClient(); 104 PSKClientCredentials pskClientConfig = (PSKClientCredentials) credentials.getClient();
@@ -42,9 +42,8 @@ import org.thingsboard.server.common.transport.util.SslUtil; @@ -42,9 +42,8 @@ import org.thingsboard.server.common.transport.util.SslUtil;
42 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; 42 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
43 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; 43 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
44 import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; 44 import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
45 -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;  
46 -import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore;  
47 import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; 45 import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
  46 +import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore;
48 47
49 import javax.annotation.PostConstruct; 48 import javax.annotation.PostConstruct;
50 import javax.security.auth.x500.X500Principal; 49 import javax.security.auth.x500.X500Principal;
@@ -57,6 +56,8 @@ import java.security.cert.X509Certificate; @@ -57,6 +56,8 @@ import java.security.cert.X509Certificate;
57 import java.util.Arrays; 56 import java.util.Arrays;
58 import java.util.List; 57 import java.util.List;
59 58
  59 +import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT;
  60 +
60 @Slf4j 61 @Slf4j
61 @Component 62 @Component
62 @TbLwM2mTransportComponent 63 @TbLwM2mTransportComponent
@@ -66,7 +67,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer @@ -66,7 +67,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
66 private final TbLwM2MDtlsSessionStore sessionStorage; 67 private final TbLwM2MDtlsSessionStore sessionStorage;
67 private final LwM2MTransportServerConfig config; 68 private final LwM2MTransportServerConfig config;
68 private final LwM2mCredentialsSecurityInfoValidator securityInfoValidator; 69 private final LwM2mCredentialsSecurityInfoValidator securityInfoValidator;
69 - private final TbEditableSecurityStore securityStore; 70 + private final TbMainSecurityStore securityStore;
70 71
71 @SuppressWarnings("deprecation") 72 @SuppressWarnings("deprecation")
72 private StaticCertificateVerifier staticCertificateVerifier; 73 private StaticCertificateVerifier staticCertificateVerifier;
@@ -117,7 +118,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer @@ -117,7 +118,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
117 118
118 String strCert = SslUtil.getCertificateString(cert); 119 String strCert = SslUtil.getCertificateString(cert);
119 String sha3Hash = EncryptionUtil.getSha3Hash(strCert); 120 String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
120 - TbLwM2MSecurityInfo securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, LwM2mTransportUtil.LwM2mTypeServer.CLIENT); 121 + TbLwM2MSecurityInfo securityInfo = securityInfoValidator.getEndpointSecurityInfoByCredentialsId(sha3Hash, CLIENT);
121 ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null; 122 ValidateDeviceCredentialsResponse msg = securityInfo != null ? securityInfo.getMsg() : null;
122 if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) { 123 if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) {
123 LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class); 124 LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class);
@@ -133,7 +134,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer @@ -133,7 +134,7 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
133 if (msg.hasDeviceInfo() && deviceProfile != null) { 134 if (msg.hasDeviceInfo() && deviceProfile != null) {
134 sessionStorage.put(endpoint, new TbX509DtlsSessionInfo(cert.getSubjectX500Principal().getName(), msg)); 135 sessionStorage.put(endpoint, new TbX509DtlsSessionInfo(cert.getSubjectX500Principal().getName(), msg));
135 try { 136 try {
136 - securityStore.put(securityInfo); 137 + securityStore.putX509(securityInfo);
137 } catch (NonUniqueSecurityInfoException e) { 138 } catch (NonUniqueSecurityInfoException e) {
138 log.trace("Failed to add security info: {}", securityInfo, e); 139 log.trace("Failed to add security info: {}", securityInfo, e);
139 } 140 }
@@ -65,7 +65,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE @@ -65,7 +65,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE
65 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; 65 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256;
66 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; 66 import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8;
67 import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig; 67 import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig;
68 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE; 68 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_UPDATE_COAP_RECOURSE;
69 69
70 @Slf4j 70 @Slf4j
71 @Component 71 @Component
@@ -23,7 +23,10 @@ import org.jetbrains.annotations.NotNull; @@ -23,7 +23,10 @@ import org.jetbrains.annotations.NotNull;
23 import org.thingsboard.server.common.data.Device; 23 import org.thingsboard.server.common.data.Device;
24 import org.thingsboard.server.common.data.DeviceProfile; 24 import org.thingsboard.server.common.data.DeviceProfile;
25 import org.thingsboard.server.common.data.ResourceType; 25 import org.thingsboard.server.common.data.ResourceType;
  26 +import org.thingsboard.server.common.data.rpc.RpcStatus;
26 import org.thingsboard.server.common.transport.SessionMsgListener; 27 import org.thingsboard.server.common.transport.SessionMsgListener;
  28 +import org.thingsboard.server.common.transport.TransportService;
  29 +import org.thingsboard.server.common.transport.TransportServiceCallback;
27 import org.thingsboard.server.gen.transport.TransportProtos; 30 import org.thingsboard.server.gen.transport.TransportProtos;
28 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; 31 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
29 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; 32 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
@@ -45,6 +48,7 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s @@ -45,6 +48,7 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s
45 private final LwM2MAttributesService attributesService; 48 private final LwM2MAttributesService attributesService;
46 private final LwM2MRpcRequestHandler rpcHandler; 49 private final LwM2MRpcRequestHandler rpcHandler;
47 private final TransportProtos.SessionInfoProto sessionInfo; 50 private final TransportProtos.SessionInfoProto sessionInfo;
  51 + private final TransportService transportService;
48 52
49 @Override 53 @Override
50 public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) { 54 public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) {
@@ -78,7 +82,22 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s @@ -78,7 +82,22 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s
78 82
79 @Override 83 @Override
80 public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) { 84 public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
81 - this.rpcHandler.onToDeviceRpcRequest(toDeviceRequest,this.sessionInfo); 85 + this.rpcHandler.onToDeviceRpcRequest(toDeviceRequest, this.sessionInfo);
  86 + if (toDeviceRequest.getPersisted()) {
  87 + RpcStatus status;
  88 + if (toDeviceRequest.getOneway()) {
  89 + status = RpcStatus.SUCCESSFUL;
  90 + } else {
  91 + status = RpcStatus.DELIVERED;
  92 + }
  93 + TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
  94 + .setRequestId(toDeviceRequest.getRequestId())
  95 + .setRequestIdLSB(toDeviceRequest.getRequestIdLSB())
  96 + .setRequestIdMSB(toDeviceRequest.getRequestIdMSB())
  97 + .setStatus(status.name())
  98 + .build();
  99 + transportService.process(sessionInfo, responseMsg, TransportServiceCallback.EMPTY);
  100 + }
82 } 101 }
83 102
84 @Override 103 @Override
@@ -25,16 +25,14 @@ import org.eclipse.californium.core.server.resources.CoapExchange; @@ -25,16 +25,14 @@ import org.eclipse.californium.core.server.resources.CoapExchange;
25 import org.eclipse.californium.core.server.resources.Resource; 25 import org.eclipse.californium.core.server.resources.Resource;
26 import org.eclipse.californium.core.server.resources.ResourceObserver; 26 import org.eclipse.californium.core.server.resources.ResourceObserver;
27 import org.thingsboard.server.cache.ota.OtaPackageDataCache; 27 import org.thingsboard.server.cache.ota.OtaPackageDataCache;
28 -import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler;  
29 -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;  
30 28
31 import java.util.UUID; 29 import java.util.UUID;
32 import java.util.concurrent.ConcurrentHashMap; 30 import java.util.concurrent.ConcurrentHashMap;
33 import java.util.concurrent.ConcurrentMap; 31 import java.util.concurrent.ConcurrentMap;
34 import java.util.concurrent.atomic.AtomicInteger; 32 import java.util.concurrent.atomic.AtomicInteger;
35 33
36 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE;  
37 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SOFTWARE_UPDATE_COAP_RECOURSE; 34 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FIRMWARE_UPDATE_COAP_RECOURSE;
  35 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SOFTWARE_UPDATE_COAP_RECOURSE;
38 36
39 @Slf4j 37 @Slf4j
40 public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { 38 public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource {
@@ -143,7 +141,7 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { @@ -143,7 +141,7 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource {
143 response.setPayload(fwData); 141 response.setPayload(fwData);
144 if (exchange.getRequestOptions().getBlock2() != null) { 142 if (exchange.getRequestOptions().getBlock2() != null) {
145 int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); 143 int chunkSize = exchange.getRequestOptions().getBlock2().getSzx();
146 - boolean lastFlag = fwData.length > chunkSize; 144 + boolean lastFlag = fwData.length <= chunkSize;
147 response.getOptions().setBlock2(chunkSize, lastFlag, 0); 145 response.getOptions().setBlock2(chunkSize, lastFlag, 0);
148 log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, lastFlag); 146 log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, lastFlag);
149 } 147 }
@@ -43,13 +43,13 @@ import org.thingsboard.server.common.data.DeviceTransportType; @@ -43,13 +43,13 @@ import org.thingsboard.server.common.data.DeviceTransportType;
43 import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; 43 import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration;
44 import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; 44 import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
45 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; 45 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
46 -import org.thingsboard.server.common.data.ota.OtaPackageKey;  
47 -import org.thingsboard.server.common.data.ota.OtaPackageType;  
48 -import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus;  
49 -import org.thingsboard.server.common.data.ota.OtaPackageUtil;  
50 import org.thingsboard.server.common.transport.TransportServiceCallback; 46 import org.thingsboard.server.common.transport.TransportServiceCallback;
51 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; 47 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
52 import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; 48 import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
  49 +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult;
  50 +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateState;
  51 +import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateResult;
  52 +import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateState;
53 import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; 53 import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler;
54 54
55 import java.util.ArrayList; 55 import java.util.ArrayList;
@@ -77,277 +77,20 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING; @@ -77,277 +77,20 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING;
77 import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME; 77 import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME;
78 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; 78 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
79 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; 79 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
80 -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED;  
81 -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING;  
82 -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED;  
83 -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED;  
84 -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;  
85 -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED; 80 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_RESULT_ID;
  81 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_STATE_ID;
  82 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_RESULT_ID;
  83 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.SW_UPDATE_STATE_ID;
86 84
87 @Slf4j 85 @Slf4j
88 public class LwM2mTransportUtil { 86 public class LwM2mTransportUtil {
89 87
90 - public static final String EVENT_AWAKE = "AWAKE";  
91 - public static final String RESPONSE_REQUEST_CHANNEL = "RESP_REQ";  
92 - public static final String RESPONSE_CHANNEL = "RESP";  
93 - public static final String OBSERVE_CHANNEL = "OBSERVE";  
94 -  
95 - public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0";  
96 - public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings";  
97 - public static final String BOOTSTRAP = "bootstrap";  
98 - public static final String SERVERS = "servers";  
99 - public static final String LWM2M_SERVER = "lwm2mServer";  
100 - public static final String BOOTSTRAP_SERVER = "bootstrapServer";  
101 - public static final String OBSERVE_ATTRIBUTE_TELEMETRY = "observeAttr";  
102 - public static final String ATTRIBUTE = "attribute";  
103 - public static final String TELEMETRY = "telemetry";  
104 - public static final String KEY_NAME = "keyName";  
105 - public static final String OBSERVE_LWM2M = "observe";  
106 - public static final String ATTRIBUTE_LWM2M = "attributeLwm2m";  
107 -  
108 - private static final String REQUEST = "/request";  
109 - private static final String ATTRIBUTES = "/" + ATTRIBUTE;  
110 - public static final String TELEMETRIES = "/" + TELEMETRY;  
111 - public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST;  
112 - public static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/";  
113 -  
114 - public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms 88 + public static final String LWM2M_VERSION_DEFAULT = "1.0";
115 89
116 public static final String LOG_LWM2M_TELEMETRY = "logLwm2m"; 90 public static final String LOG_LWM2M_TELEMETRY = "logLwm2m";
117 public static final String LOG_LWM2M_INFO = "info"; 91 public static final String LOG_LWM2M_INFO = "info";
118 public static final String LOG_LWM2M_ERROR = "error"; 92 public static final String LOG_LWM2M_ERROR = "error";
119 public static final String LOG_LWM2M_WARN = "warn"; 93 public static final String LOG_LWM2M_WARN = "warn";
120 - public static final String LOG_LWM2M_VALUE = "value";  
121 -  
122 - public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized";  
123 - public static final String LWM2M_VERSION_DEFAULT = "1.0";  
124 -  
125 - // Firmware  
126 - public static final String FIRMWARE_UPDATE_COAP_RECOURSE = "tbfw";  
127 - public static final String FW_UPDATE = "Firmware update";  
128 - public static final Integer FW_5_ID = 5;  
129 - public static final Integer FW_19_ID = 19;  
130 -  
131 - // Package W  
132 - public static final String FW_PACKAGE_5_ID = "/5/0/0";  
133 - public static final String FW_PACKAGE_19_ID = "/19/0/0";  
134 - // Package URI  
135 - public static final String FW_PACKAGE_URI_ID = "/5/0/1";  
136 - // State R  
137 - public static final String FW_STATE_ID = "/5/0/3";  
138 - // Update Result R  
139 - public static final String FW_RESULT_ID = "/5/0/5";  
140 -  
141 - public static final String FW_DELIVERY_METHOD = "/5/0/9";  
142 -  
143 - // PkgName R  
144 - public static final String FW_NAME_ID = "/5/0/6";  
145 - // PkgVersion R  
146 - public static final String FW_5_VER_ID = "/5/0/7";  
147 -  
148 - /**  
149 - * Quectel@Hi15RM1-HLB_V1.0@BC68JAR01A10,V150R100C20B300SP7,V150R100C20B300SP7@8  
150 - * BC68JAR01A10  
151 - * # Request prodct type number  
152 - * ATI  
153 - * Quectel  
154 - * BC68  
155 - * Revision:BC68JAR01A10  
156 - */  
157 - public static final String FW_3_VER_ID = "/3/0/3";  
158 - // Update E  
159 - public static final String FW_UPDATE_ID = "/5/0/2";  
160 -  
161 - // Software  
162 - public static final String SOFTWARE_UPDATE_COAP_RECOURSE = "softwareUpdateCoapRecourse";  
163 - public static final String SW_UPDATE = "Software update";  
164 - public static final Integer SW_ID = 9;  
165 - // Package W  
166 - public static final String SW_PACKAGE_ID = "/9/0/2";  
167 - // Package URI  
168 - public static final String SW_PACKAGE_URI_ID = "/9/0/3";  
169 - // Update State R  
170 - public static final String SW_UPDATE_STATE_ID = "/9/0/7";  
171 - // Update Result R  
172 - public static final String SW_RESULT_ID = "/9/0/9";  
173 - // PkgName R  
174 - public static final String SW_NAME_ID = "/9/0/0";  
175 - // PkgVersion R  
176 - public static final String SW_VER_ID = "/9/0/1";  
177 - // Install E  
178 - public static final String SW_INSTALL_ID = "/9/0/4";  
179 - // Uninstall E  
180 - public static final String SW_UN_INSTALL_ID = "/9/0/6";  
181 -  
182 - public enum LwM2mTypeServer {  
183 - BOOTSTRAP(0, "bootstrap"),  
184 - CLIENT(1, "client");  
185 -  
186 - public int code;  
187 - public String type;  
188 -  
189 - LwM2mTypeServer(int code, String type) {  
190 - this.code = code;  
191 - this.type = type;  
192 - }  
193 -  
194 - public static LwM2mTypeServer fromLwM2mTypeServer(String type) {  
195 - for (LwM2mTypeServer sm : LwM2mTypeServer.values()) {  
196 - if (sm.type.equals(type)) {  
197 - return sm;  
198 - }  
199 - }  
200 - throw new IllegalArgumentException(String.format("Unsupported typeServer type : %d", type));  
201 - }  
202 - }  
203 -  
204 - public static Optional<OtaPackageUpdateStatus> toOtaPackageUpdateStatus(UpdateResultFw updateResultFw) {  
205 - switch (updateResultFw) {  
206 - case INITIAL:  
207 - return Optional.empty();  
208 - case UPDATE_SUCCESSFULLY:  
209 - return Optional.of(UPDATED);  
210 - case NOT_ENOUGH:  
211 - case OUT_OFF_MEMORY:  
212 - case CONNECTION_LOST:  
213 - case INTEGRITY_CHECK_FAILURE:  
214 - case UNSUPPORTED_TYPE:  
215 - case INVALID_URI:  
216 - case UPDATE_FAILED:  
217 - case UNSUPPORTED_PROTOCOL:  
218 - return Optional.of(FAILED);  
219 - default:  
220 - throw new CodecException("Invalid value stateFw %s for FirmwareUpdateStatus.", updateResultFw.name());  
221 - }  
222 - }  
223 -  
224 - public static Optional<OtaPackageUpdateStatus> toOtaPackageUpdateStatus(UpdateStateFw updateStateFw) {  
225 - switch (updateStateFw) {  
226 - case IDLE:  
227 - return Optional.empty();  
228 - case DOWNLOADING:  
229 - return Optional.of(DOWNLOADING);  
230 - case DOWNLOADED:  
231 - return Optional.of(DOWNLOADED);  
232 - case UPDATING:  
233 - return Optional.of(UPDATING);  
234 - default:  
235 - throw new CodecException("Invalid value stateFw %d for FirmwareUpdateStatus.", updateStateFw);  
236 - }  
237 - }  
238 -  
239 - /**  
240 - * SW Update State R  
241 - * 0: INITIAL Before downloading. (see 5.1.2.1)  
242 - * 1: DOWNLOAD STARTED The downloading process has started and is on-going. (see 5.1.2.2)  
243 - * 2: DOWNLOADED The package has been completely downloaded (see 5.1.2.3)  
244 - * 3: DELIVERED In that state, the package has been correctly downloaded and is ready to be installed. (see 5.1.2.4)  
245 - * If executing the Install Resource failed, the state remains at DELIVERED.  
246 - * If executing the Install Resource was successful, the state changes from DELIVERED to INSTALLED.  
247 - * After executing the UnInstall Resource, the state changes to INITIAL.  
248 - * 4: INSTALLED  
249 - */  
250 - public enum UpdateStateSw {  
251 - INITIAL(0, "Initial"),  
252 - DOWNLOAD_STARTED(1, "DownloadStarted"),  
253 - DOWNLOADED(2, "Downloaded"),  
254 - DELIVERED(3, "Delivered"),  
255 - INSTALLED(4, "Installed");  
256 -  
257 - public int code;  
258 - public String type;  
259 -  
260 - UpdateStateSw(int code, String type) {  
261 - this.code = code;  
262 - this.type = type;  
263 - }  
264 -  
265 - public static UpdateStateSw fromUpdateStateSwByType(String type) {  
266 - for (UpdateStateSw to : UpdateStateSw.values()) {  
267 - if (to.type.equals(type)) {  
268 - return to;  
269 - }  
270 - }  
271 - throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", type));  
272 - }  
273 -  
274 - public static UpdateStateSw fromUpdateStateSwByCode(int code) {  
275 - for (UpdateStateSw to : UpdateStateSw.values()) {  
276 - if (to.code == code) {  
277 - return to;  
278 - }  
279 - }  
280 - throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", code));  
281 - }  
282 - }  
283 -  
284 - /**  
285 - * SW Update Result  
286 - * Contains the result of downloading or installing/uninstalling the software  
287 - * 0: Initial value.  
288 - * - Prior to download any new package in the Device, Update Result MUST be reset to this initial value.  
289 - * - One side effect of executing the Uninstall resource is to reset Update Result to this initial value "0".  
290 - * 1: Downloading.  
291 - * - The package downloading process is on-going.  
292 - * 2: Software successfully installed.  
293 - * 3: Successfully Downloaded and package integrity verified  
294 - * (( 4-49, for expansion, of other scenarios))  
295 - * ** Failed  
296 - * 50: Not enough storage for the new software package.  
297 - * 51: Out of memory during downloading process.  
298 - * 52: Connection lost during downloading process.  
299 - * 53: Package integrity check failure.  
300 - * 54: Unsupported package type.  
301 - * 56: Invalid URI  
302 - * 57: Device defined update error  
303 - * 58: Software installation failure  
304 - * 59: Uninstallation Failure during forUpdate(arg=0)  
305 - * 60-200 : (for expansion, selection to be in blocks depending on new introduction of features)  
306 - * This Resource MAY be reported by sending Observe operation.  
307 - */  
308 - public enum UpdateResultSw {  
309 - INITIAL(0, "Initial value", false),  
310 - DOWNLOADING(1, "Downloading", false),  
311 - SUCCESSFULLY_INSTALLED(2, "Software successfully installed", false),  
312 - SUCCESSFULLY_DOWNLOADED_VERIFIED(3, "Successfully Downloaded and package integrity verified", false),  
313 - NOT_ENOUGH_STORAGE(50, "Not enough storage for the new software package", true),  
314 - OUT_OFF_MEMORY(51, "Out of memory during downloading process", true),  
315 - CONNECTION_LOST(52, "Connection lost during downloading process", false),  
316 - PACKAGE_CHECK_FAILURE(53, "Package integrity check failure.", false),  
317 - UNSUPPORTED_PACKAGE_TYPE(54, "Unsupported package type", false),  
318 - INVALID_URI(56, "Invalid URI", true),  
319 - UPDATE_ERROR(57, "Device defined update error", true),  
320 - INSTALL_FAILURE(58, "Software installation failure", true),  
321 - UN_INSTALL_FAILURE(59, "Uninstallation Failure during forUpdate(arg=0)", true);  
322 -  
323 - public int code;  
324 - public String type;  
325 - public boolean isAgain;  
326 -  
327 - UpdateResultSw(int code, String type, boolean isAgain) {  
328 - this.code = code;  
329 - this.type = type;  
330 - this.isAgain = isAgain;  
331 - }  
332 -  
333 - public static UpdateResultSw fromUpdateResultSwByType(String type) {  
334 - for (UpdateResultSw to : UpdateResultSw.values()) {  
335 - if (to.type.equals(type)) {  
336 - return to;  
337 - }  
338 - }  
339 - throw new IllegalArgumentException(String.format("Unsupported SW Update Result type : %s", type));  
340 - }  
341 -  
342 - public static UpdateResultSw fromUpdateResultSwByCode(int code) {  
343 - for (UpdateResultSw to : UpdateResultSw.values()) {  
344 - if (to.code == code) {  
345 - return to;  
346 - }  
347 - }  
348 - throw new IllegalArgumentException(String.format("Unsupported SW Update Result code : %s", code));  
349 - }  
350 - }  
351 94
352 public enum LwM2MClientStrategy { 95 public enum LwM2MClientStrategy {
353 CLIENT_STRATEGY_1(1, "Read only resources marked as observation"), 96 CLIENT_STRATEGY_1(1, "Read only resources marked as observation"),
@@ -380,45 +123,7 @@ public class LwM2mTransportUtil { @@ -380,45 +123,7 @@ public class LwM2mTransportUtil {
380 } 123 }
381 } 124 }
382 125
383 - /**  
384 - * FirmwareUpdateStatus {  
385 - * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED  
386 - */  
387 - public static OtaPackageUpdateStatus EqualsSwSateToFirmwareUpdateStatus(UpdateStateSw updateStateSw, UpdateResultSw updateResultSw) {  
388 - switch (updateResultSw) {  
389 - case INITIAL:  
390 - switch (updateStateSw) {  
391 - case INITIAL:  
392 - case DOWNLOAD_STARTED:  
393 - return DOWNLOADING;  
394 - case DOWNLOADED:  
395 - return DOWNLOADED;  
396 - case DELIVERED:  
397 - return VERIFIED;  
398 - }  
399 - case DOWNLOADING:  
400 - return DOWNLOADING;  
401 - case SUCCESSFULLY_INSTALLED:  
402 - return UPDATED;  
403 - case SUCCESSFULLY_DOWNLOADED_VERIFIED:  
404 - return VERIFIED;  
405 - case NOT_ENOUGH_STORAGE:  
406 - case OUT_OFF_MEMORY:  
407 - case CONNECTION_LOST:  
408 - case PACKAGE_CHECK_FAILURE:  
409 - case UNSUPPORTED_PACKAGE_TYPE:  
410 - case INVALID_URI:  
411 - case UPDATE_ERROR:  
412 - case INSTALL_FAILURE:  
413 - case UN_INSTALL_FAILURE:  
414 - return FAILED;  
415 - default:  
416 - throw new CodecException("Invalid value stateFw %s %s for FirmwareUpdateStatus.", updateStateSw.name(), updateResultSw.name());  
417 - }  
418 - }  
419 -  
420 -  
421 - public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath 126 + public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath
422 resourcePath) throws CodecException { 127 resourcePath) throws CodecException {
423 switch (type) { 128 switch (type) {
424 case BOOLEAN: 129 case BOOLEAN:
@@ -443,19 +148,19 @@ public class LwM2mTransportUtil { @@ -443,19 +148,19 @@ public class LwM2mTransportUtil {
443 if (path != null) { 148 if (path != null) {
444 if (FW_STATE_ID.equals(path)) { 149 if (FW_STATE_ID.equals(path)) {
445 lwM2mOtaConvert.setCurrentType(STRING); 150 lwM2mOtaConvert.setCurrentType(STRING);
446 - lwM2mOtaConvert.setValue(UpdateStateFw.fromStateFwByCode(((Long) value).intValue()).type); 151 + lwM2mOtaConvert.setValue(FirmwareUpdateState.fromStateFwByCode(((Long) value).intValue()).type);
447 return lwM2mOtaConvert; 152 return lwM2mOtaConvert;
448 } else if (FW_RESULT_ID.equals(path)) { 153 } else if (FW_RESULT_ID.equals(path)) {
449 lwM2mOtaConvert.setCurrentType(STRING); 154 lwM2mOtaConvert.setCurrentType(STRING);
450 - lwM2mOtaConvert.setValue(UpdateResultFw.fromUpdateResultFwByCode(((Long) value).intValue()).getType()); 155 + lwM2mOtaConvert.setValue(FirmwareUpdateResult.fromUpdateResultFwByCode(((Long) value).intValue()).getType());
451 return lwM2mOtaConvert; 156 return lwM2mOtaConvert;
452 } else if (SW_UPDATE_STATE_ID.equals(path)) { 157 } else if (SW_UPDATE_STATE_ID.equals(path)) {
453 lwM2mOtaConvert.setCurrentType(STRING); 158 lwM2mOtaConvert.setCurrentType(STRING);
454 - lwM2mOtaConvert.setValue(UpdateStateSw.fromUpdateStateSwByCode(((Long) value).intValue()).type); 159 + lwM2mOtaConvert.setValue(SoftwareUpdateState.fromUpdateStateSwByCode(((Long) value).intValue()).type);
455 return lwM2mOtaConvert; 160 return lwM2mOtaConvert;
456 } else if (SW_RESULT_ID.equals(path)) { 161 } else if (SW_RESULT_ID.equals(path)) {
457 lwM2mOtaConvert.setCurrentType(STRING); 162 lwM2mOtaConvert.setCurrentType(STRING);
458 - lwM2mOtaConvert.setValue(UpdateResultSw.fromUpdateResultSwByCode(((Long) value).intValue()).type); 163 + lwM2mOtaConvert.setValue(SoftwareUpdateResult.fromUpdateResultSwByCode(((Long) value).intValue()).type);
459 return lwM2mOtaConvert; 164 return lwM2mOtaConvert;
460 } 165 }
461 } 166 }
@@ -477,18 +182,6 @@ public class LwM2mTransportUtil { @@ -477,18 +182,6 @@ public class LwM2mTransportUtil {
477 return null; 182 return null;
478 } 183 }
479 184
480 -// public static LwM2mClientProfile getNewProfileParameters(JsonObject profilesConfigData, TenantId tenantId) {  
481 -// LwM2mClientProfile lwM2MClientProfile = new LwM2mClientProfile();  
482 -// lwM2MClientProfile.setTenantId(tenantId);  
483 -// lwM2MClientProfile.setPostClientLwM2mSettings(profilesConfigData.get(CLIENT_LWM2M_SETTINGS).getAsJsonObject());  
484 -// lwM2MClientProfile.setPostKeyNameProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).getAsJsonObject());  
485 -// lwM2MClientProfile.setPostAttributeProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).getAsJsonArray());  
486 -// lwM2MClientProfile.setPostTelemetryProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).getAsJsonArray());  
487 -// lwM2MClientProfile.setPostObserveProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE_LWM2M).getAsJsonArray());  
488 -// lwM2MClientProfile.setPostAttributeLwm2mProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).getAsJsonObject());  
489 -// return lwM2MClientProfile;  
490 -// }  
491 -  
492 public static Lwm2mDeviceProfileTransportConfiguration toLwM2MClientProfile(DeviceProfile deviceProfile) { 185 public static Lwm2mDeviceProfileTransportConfiguration toLwM2MClientProfile(DeviceProfile deviceProfile) {
493 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); 186 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
494 if (transportConfiguration.getType().equals(DeviceTransportType.LWM2M)) { 187 if (transportConfiguration.getType().equals(DeviceTransportType.LWM2M)) {
@@ -603,7 +296,6 @@ public class LwM2mTransportUtil { @@ -603,7 +296,6 @@ public class LwM2mTransportUtil {
603 if (keyArray.length > 1 && keyArray[1].split(LWM2M_SEPARATOR_KEY).length == 2) { 296 if (keyArray.length > 1 && keyArray[1].split(LWM2M_SEPARATOR_KEY).length == 2) {
604 return pathIdVer; 297 return pathIdVer;
605 } else { 298 } else {
606 - LwM2mPath pathObjId = new LwM2mPath(pathIdVer);  
607 return convertObjectIdToVersionedId(pathIdVer, registration); 299 return convertObjectIdToVersionedId(pathIdVer, registration);
608 } 300 }
609 } 301 }
@@ -743,19 +435,6 @@ public class LwM2mTransportUtil { @@ -743,19 +435,6 @@ public class LwM2mTransportUtil {
743 } 435 }
744 } 436 }
745 437
746 - public static boolean isFwSwWords(String pathName) {  
747 - return OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName)  
748 - || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName)  
749 - || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM).equals(pathName)  
750 - || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName)  
751 - || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.SIZE).equals(pathName)  
752 - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName)  
753 - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName)  
754 - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM).equals(pathName)  
755 - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName)  
756 - || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.SIZE).equals(pathName);  
757 - }  
758 -  
759 /** 438 /**
760 * @param lwM2MClient - 439 * @param lwM2MClient -
761 * @param path - 440 * @param path -
@@ -33,12 +33,10 @@ import org.eclipse.leshan.server.security.SecurityInfo; @@ -33,12 +33,10 @@ import org.eclipse.leshan.server.security.SecurityInfo;
33 import org.thingsboard.server.common.data.Device; 33 import org.thingsboard.server.common.data.Device;
34 import org.thingsboard.server.common.data.DeviceProfile; 34 import org.thingsboard.server.common.data.DeviceProfile;
35 import org.thingsboard.server.common.data.id.TenantId; 35 import org.thingsboard.server.common.data.id.TenantId;
36 -import org.thingsboard.server.common.data.ota.OtaPackageType;  
37 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; 36 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
38 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; 37 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
39 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; 38 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
40 import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; 39 import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest;
41 -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;  
42 40
43 import java.util.Collection; 41 import java.util.Collection;
44 import java.util.Map; 42 import java.util.Map;
@@ -53,7 +51,7 @@ import java.util.concurrent.locks.ReentrantLock; @@ -53,7 +51,7 @@ import java.util.concurrent.locks.ReentrantLock;
53 import java.util.stream.Collectors; 51 import java.util.stream.Collectors;
54 52
55 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; 53 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
56 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TRANSPORT_DEFAULT_LWM2M_VERSION; 54 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_VERSION_DEFAULT;
57 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; 55 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId;
58 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsResourceTypeGetSimpleName; 56 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsResourceTypeGetSimpleName;
59 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; 57 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId;
@@ -307,7 +305,7 @@ public class LwM2mClient implements Cloneable { @@ -307,7 +305,7 @@ public class LwM2mClient implements Cloneable {
307 LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(path)); 305 LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(path));
308 String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); 306 String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
309 String verRez = getVerFromPathIdVerOrId(path); 307 String verRez = getVerFromPathIdVerOrId(path);
310 - return verRez == null ? TRANSPORT_DEFAULT_LWM2M_VERSION.equals(verSupportedObject) : verRez.equals(verSupportedObject); 308 + return verRez == null ? LWM2M_VERSION_DEFAULT.equals(verSupportedObject) : verRez.equals(verSupportedObject);
311 } 309 }
312 310
313 /** 311 /**
@@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.client; @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.client;
17 17
18 import lombok.RequiredArgsConstructor; 18 import lombok.RequiredArgsConstructor;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.eclipse.leshan.core.SecurityMode;
20 import org.eclipse.leshan.core.model.ResourceModel; 21 import org.eclipse.leshan.core.model.ResourceModel;
21 import org.eclipse.leshan.core.node.LwM2mPath; 22 import org.eclipse.leshan.core.node.LwM2mPath;
22 import org.eclipse.leshan.server.registration.Registration; 23 import org.eclipse.leshan.server.registration.Registration;
@@ -30,7 +31,7 @@ import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; @@ -30,7 +31,7 @@ import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
30 import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; 31 import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
31 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; 32 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
32 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; 33 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
33 -import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore; 34 +import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore;
34 35
35 import java.util.Arrays; 36 import java.util.Arrays;
36 import java.util.Collection; 37 import java.util.Collection;
@@ -54,7 +55,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @@ -54,7 +55,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
54 55
55 private final LwM2mTransportContext context; 56 private final LwM2mTransportContext context;
56 private final LwM2MTransportServerConfig config; 57 private final LwM2MTransportServerConfig config;
57 - private final TbEditableSecurityStore securityStore; 58 + private final TbMainSecurityStore securityStore;
58 private final Map<String, LwM2mClient> lwM2mClientsByEndpoint = new ConcurrentHashMap<>(); 59 private final Map<String, LwM2mClient> lwM2mClientsByEndpoint = new ConcurrentHashMap<>();
59 private final Map<String, LwM2mClient> lwM2mClientsByRegistrationId = new ConcurrentHashMap<>(); 60 private final Map<String, LwM2mClient> lwM2mClientsByRegistrationId = new ConcurrentHashMap<>();
60 private final Map<UUID, Lwm2mDeviceProfileTransportConfiguration> profiles = new ConcurrentHashMap<>(); 61 private final Map<UUID, Lwm2mDeviceProfileTransportConfiguration> profiles = new ConcurrentHashMap<>();
@@ -75,6 +76,9 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @@ -75,6 +76,9 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
75 oldSession = lwM2MClient.getSession(); 76 oldSession = lwM2MClient.getSession();
76 TbLwM2MSecurityInfo securityInfo = securityStore.getTbLwM2MSecurityInfoByEndpoint(lwM2MClient.getEndpoint()); 77 TbLwM2MSecurityInfo securityInfo = securityStore.getTbLwM2MSecurityInfoByEndpoint(lwM2MClient.getEndpoint());
77 if (securityInfo.getSecurityMode() != null) { 78 if (securityInfo.getSecurityMode() != null) {
  79 + if (SecurityMode.X509.equals(securityInfo.getSecurityMode())) {
  80 + securityStore.registerX509(registration.getEndpoint(), registration.getId());
  81 + }
78 if (securityInfo.getDeviceProfile() != null) { 82 if (securityInfo.getDeviceProfile() != null) {
79 profileUpdate(securityInfo.getDeviceProfile()); 83 profileUpdate(securityInfo.getDeviceProfile());
80 if (securityInfo.getSecurityInfo() != null) { 84 if (securityInfo.getSecurityInfo() != null) {
@@ -124,7 +128,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @@ -124,7 +128,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
124 if (currentRegistration.getId().equals(registration.getId())) { 128 if (currentRegistration.getId().equals(registration.getId())) {
125 lwM2MClient.setState(LwM2MClientState.UNREGISTERED); 129 lwM2MClient.setState(LwM2MClientState.UNREGISTERED);
126 lwM2mClientsByEndpoint.remove(lwM2MClient.getEndpoint()); 130 lwM2mClientsByEndpoint.remove(lwM2MClient.getEndpoint());
127 - this.securityStore.remove(lwM2MClient.getEndpoint()); 131 + this.securityStore.remove(lwM2MClient.getEndpoint(), registration.getId());
128 UUID profileId = lwM2MClient.getProfileId(); 132 UUID profileId = lwM2MClient.getProfileId();
129 if (profileId != null) { 133 if (profileId != null) {
130 Optional<LwM2mClient> otherClients = lwM2mClientsByRegistrationId.values().stream().filter(e -> e.getProfileId().equals(profileId)).findFirst(); 134 Optional<LwM2mClient> otherClients = lwM2mClientsByRegistrationId.values().stream().filter(e -> e.getProfileId().equals(profileId)).findFirst();
@@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.ota; @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.ota;
17 17
18 import lombok.RequiredArgsConstructor; 18 import lombok.RequiredArgsConstructor;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.eclipse.leshan.core.node.codec.CodecException;
20 import org.eclipse.leshan.core.request.ContentFormat; 21 import org.eclipse.leshan.core.request.ContentFormat;
21 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
22 import org.springframework.context.annotation.Lazy; 23 import org.springframework.context.annotation.Lazy;
@@ -24,6 +25,7 @@ import org.springframework.stereotype.Service; @@ -24,6 +25,7 @@ import org.springframework.stereotype.Service;
24 import org.thingsboard.common.util.DonAsynchron; 25 import org.thingsboard.common.util.DonAsynchron;
25 import org.thingsboard.server.cache.ota.OtaPackageDataCache; 26 import org.thingsboard.server.cache.ota.OtaPackageDataCache;
26 import org.thingsboard.server.common.data.StringUtils; 27 import org.thingsboard.server.common.data.StringUtils;
  28 +import org.thingsboard.server.common.data.device.data.lwm2m.OtherConfiguration;
27 import org.thingsboard.server.common.data.ota.OtaPackageKey; 29 import org.thingsboard.server.common.data.ota.OtaPackageKey;
28 import org.thingsboard.server.common.data.ota.OtaPackageType; 30 import org.thingsboard.server.common.data.ota.OtaPackageType;
29 import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; 31 import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus;
@@ -32,11 +34,7 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; @@ -32,11 +34,7 @@ import org.thingsboard.server.common.transport.TransportServiceCallback;
32 import org.thingsboard.server.gen.transport.TransportProtos; 34 import org.thingsboard.server.gen.transport.TransportProtos;
33 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; 35 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
34 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; 36 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
35 -import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy;  
36 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; 37 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
37 -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;  
38 -import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw;  
39 -import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw;  
40 import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; 38 import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService;
41 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; 39 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
42 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; 40 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
@@ -47,6 +45,13 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequ @@ -47,6 +45,13 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequ
47 import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; 45 import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest;
48 import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; 46 import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback;
49 import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; 47 import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
  48 +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.LwM2MFirmwareUpdateStrategy;
  49 +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareDeliveryMethod;
  50 +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult;
  51 +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateState;
  52 +import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MSoftwareUpdateStrategy;
  53 +import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateResult;
  54 +import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateState;
50 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; 55 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
51 56
52 import javax.annotation.PostConstruct; 57 import javax.annotation.PostConstruct;
@@ -59,8 +64,13 @@ import java.util.UUID; @@ -59,8 +64,13 @@ import java.util.UUID;
59 import java.util.concurrent.ConcurrentHashMap; 64 import java.util.concurrent.ConcurrentHashMap;
60 65
61 import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; 66 import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE;
  67 +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED;
  68 +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING;
  69 +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED;
  70 +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED;
  71 +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;
  72 +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED;
62 import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; 73 import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey;
63 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE;  
64 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; 74 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY;
65 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; 75 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId;
66 76
@@ -77,13 +87,30 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -77,13 +87,30 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
77 public static final String SOFTWARE_TITLE = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE); 87 public static final String SOFTWARE_TITLE = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE);
78 public static final String SOFTWARE_URL = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.URL); 88 public static final String SOFTWARE_URL = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.URL);
79 89
  90 + public static final String FIRMWARE_UPDATE_COAP_RECOURSE = "tbfw";
  91 + public static final String SOFTWARE_UPDATE_COAP_RECOURSE = "tbsw";
80 private static final String FW_PACKAGE_5_ID = "/5/0/0"; 92 private static final String FW_PACKAGE_5_ID = "/5/0/0";
81 private static final String FW_URL_ID = "/5/0/1"; 93 private static final String FW_URL_ID = "/5/0/1";
82 private static final String FW_EXECUTE_ID = "/5/0/2"; 94 private static final String FW_EXECUTE_ID = "/5/0/2";
83 - private static final String FW_NAME_ID = "/5/0/6";  
84 - private static final String FW_VER_ID = "/5/0/7"; 95 + public static final String FW_STATE_ID = "/5/0/3";
  96 + public static final String FW_RESULT_ID = "/5/0/5";
  97 + public static final String FW_NAME_ID = "/5/0/6";
  98 + public static final String FW_5_VER_ID = "/5/0/7";
  99 + /**
  100 + * Quectel@Hi15RM1-HLB_V1.0@BC68JAR01A10,V150R100C20B300SP7,V150R100C20B300SP7@8
  101 + * Revision:BC68JAR01A10
  102 + */
  103 + public static final String FW_3_VER_ID = "/3/0/3";
  104 + public static final String FW_DELIVERY_METHOD = "/5/0/9";
  105 +
85 private static final String SW_NAME_ID = "/9/0/0"; 106 private static final String SW_NAME_ID = "/9/0/0";
86 private static final String SW_VER_ID = "/9/0/1"; 107 private static final String SW_VER_ID = "/9/0/1";
  108 + public static final String SW_PACKAGE_ID = "/9/0/2";
  109 + public static final String SW_PACKAGE_URI_ID = "/9/0/3";
  110 + public static final String SW_INSTALL_ID = "/9/0/4";
  111 + public static final String SW_UPDATE_STATE_ID = "/9/0/7";
  112 + public static final String SW_RESULT_ID = "/9/0/9";
  113 + public static final String SW_UN_INSTALL_ID = "/9/0/6";
87 114
88 private final Map<String, LwM2MClientOtaInfo> fwStates = new ConcurrentHashMap<>(); 115 private final Map<String, LwM2MClientOtaInfo> fwStates = new ConcurrentHashMap<>();
89 private final Map<String, LwM2MClientOtaInfo> swStates = new ConcurrentHashMap<>(); 116 private final Map<String, LwM2MClientOtaInfo> swStates = new ConcurrentHashMap<>();
@@ -175,6 +202,24 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -175,6 +202,24 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
175 } 202 }
176 203
177 @Override 204 @Override
  205 + public void onCurrentFirmwareStrategyUpdate(LwM2mClient client, OtherConfiguration configuration) {
  206 + log.debug("[{}] Current fw strategy: {}", client.getEndpoint(), configuration.getFwUpdateStrategy());
  207 + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client);
  208 + fwInfo.setFwStrategy(LwM2MFirmwareUpdateStrategy.fromStrategyFwByCode(configuration.getFwUpdateStrategy()));
  209 + fwInfo.setBaseUrl(configuration.getFwUpdateRecourse());
  210 + startFirmwareUpdateIfNeeded(client, fwInfo);
  211 + }
  212 +
  213 + @Override
  214 + public void onCurrentSoftwareStrategyUpdate(LwM2mClient client, OtherConfiguration configuration) {
  215 + log.debug("[{}] Current sw strategy: {}", client.getEndpoint(), configuration.getSwUpdateStrategy());
  216 + LwM2MClientOtaInfo swInfo = getOrInitSwInfo(client);
  217 + swInfo.setSwStrategy(LwM2MSoftwareUpdateStrategy.fromStrategySwByCode(configuration.getSwUpdateStrategy()));
  218 + swInfo.setBaseUrl(configuration.getSwUpdateRecourse());
  219 + startSoftwareUpdateIfNeeded(client, swInfo);
  220 + }
  221 +
  222 + @Override
178 public void onCurrentFirmwareVersion3Update(LwM2mClient client, String version) { 223 public void onCurrentFirmwareVersion3Update(LwM2mClient client, String version) {
179 log.debug("[{}] Current fw version: {}", client.getEndpoint(), version); 224 log.debug("[{}] Current fw version: {}", client.getEndpoint(), version);
180 LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); 225 LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client);
@@ -192,12 +237,12 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -192,12 +237,12 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
192 public void onCurrentFirmwareStateUpdate(LwM2mClient client, Long stateCode) { 237 public void onCurrentFirmwareStateUpdate(LwM2mClient client, Long stateCode) {
193 log.debug("[{}] Current fw state: {}", client.getEndpoint(), stateCode); 238 log.debug("[{}] Current fw state: {}", client.getEndpoint(), stateCode);
194 LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); 239 LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client);
195 - UpdateStateFw state = UpdateStateFw.fromStateFwByCode(stateCode.intValue());  
196 - if (UpdateStateFw.DOWNLOADED.equals(state)) { 240 + FirmwareUpdateState state = FirmwareUpdateState.fromStateFwByCode(stateCode.intValue());
  241 + if (FirmwareUpdateState.DOWNLOADED.equals(state)) {
197 executeFwUpdate(client); 242 executeFwUpdate(client);
198 } 243 }
199 fwInfo.setUpdateState(state); 244 fwInfo.setUpdateState(state);
200 - Optional<OtaPackageUpdateStatus> status = LwM2mTransportUtil.toOtaPackageUpdateStatus(state); 245 + Optional<OtaPackageUpdateStatus> status = this.toOtaPackageUpdateStatus(state);
201 status.ifPresent(otaStatus -> sendStateUpdateToTelemetry(client, fwInfo, 246 status.ifPresent(otaStatus -> sendStateUpdateToTelemetry(client, fwInfo,
202 otaStatus, "Firmware Update State: " + state.name())); 247 otaStatus, "Firmware Update State: " + state.name()));
203 } 248 }
@@ -206,8 +251,8 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -206,8 +251,8 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
206 public void onCurrentFirmwareResultUpdate(LwM2mClient client, Long code) { 251 public void onCurrentFirmwareResultUpdate(LwM2mClient client, Long code) {
207 log.debug("[{}] Current fw result: {}", client.getEndpoint(), code); 252 log.debug("[{}] Current fw result: {}", client.getEndpoint(), code);
208 LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); 253 LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client);
209 - UpdateResultFw result = UpdateResultFw.fromUpdateResultFwByCode(code.intValue());  
210 - Optional<OtaPackageUpdateStatus> status = LwM2mTransportUtil.toOtaPackageUpdateStatus(result); 254 + FirmwareUpdateResult result = FirmwareUpdateResult.fromUpdateResultFwByCode(code.intValue());
  255 + Optional<OtaPackageUpdateStatus> status = this.toOtaPackageUpdateStatus(result);
211 status.ifPresent(otaStatus -> sendStateUpdateToTelemetry(client, fwInfo, 256 status.ifPresent(otaStatus -> sendStateUpdateToTelemetry(client, fwInfo,
212 otaStatus, "Firmware Update Result: " + result.name())); 257 otaStatus, "Firmware Update Result: " + result.name()));
213 if (result.isAgain() && fwInfo.getRetryAttempts() <= 2) { 258 if (result.isAgain() && fwInfo.getRetryAttempts() <= 2) {
@@ -250,6 +295,10 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -250,6 +295,10 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
250 } 295 }
251 } 296 }
252 297
  298 + private void startSoftwareUpdateIfNeeded(LwM2mClient client, LwM2MClientOtaInfo swInfo) {
  299 +
  300 + }
  301 +
253 private void startFirmwareUpdateUsingUrl(LwM2mClient client, String url) { 302 private void startFirmwareUpdateUsingUrl(LwM2mClient client, String url) {
254 String targetIdVer = convertObjectIdToVersionedId(FW_URL_ID, client.getRegistration()); 303 String targetIdVer = convertObjectIdToVersionedId(FW_URL_ID, client.getRegistration());
255 TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(url).timeout(config.getTimeout()).build(); 304 TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(url).timeout(config.getTimeout()).build();
@@ -276,10 +325,10 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -276,10 +325,10 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
276 if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { 325 if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) {
277 UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); 326 UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB());
278 LwM2MFirmwareUpdateStrategy strategy; 327 LwM2MFirmwareUpdateStrategy strategy;
279 - if (fwInfo.getDeliveryMethod() == null || fwInfo.getDeliveryMethod() == 2) {  
280 - strategy = fwInfo.getStrategy(); 328 + if (fwInfo.getDeliveryMethod() == null || fwInfo.getDeliveryMethod() == FirmwareDeliveryMethod.BOTH.code) {
  329 + strategy = fwInfo.getFwStrategy();
281 } else { 330 } else {
282 - strategy = fwInfo.getDeliveryMethod() == 0 ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; 331 + strategy = fwInfo.getDeliveryMethod() == FirmwareDeliveryMethod.PULL.code ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY;
283 } 332 }
284 switch (strategy) { 333 switch (strategy) {
285 case OBJ_5_BINARY: 334 case OBJ_5_BINARY:
@@ -328,9 +377,9 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -328,9 +377,9 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
328 return Optional.empty(); 377 return Optional.empty();
329 } 378 }
330 379
331 - private LwM2MClientOtaInfo getOrInitFwInfo(LwM2mClient client) { 380 + public LwM2MClientOtaInfo getOrInitFwInfo(LwM2mClient client) {
332 //TODO: fetch state from the cache or DB. 381 //TODO: fetch state from the cache or DB.
333 - return fwStates.computeIfAbsent(client.getEndpoint(), endpoint -> { 382 + return this.fwStates.computeIfAbsent(client.getEndpoint(), endpoint -> {
334 var profile = clientContext.getProfile(client.getProfileId()); 383 var profile = clientContext.getProfile(client.getProfileId());
335 return new LwM2MClientOtaInfo(endpoint, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy(), 384 return new LwM2MClientOtaInfo(endpoint, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy(),
336 profile.getClientLwM2mSettings().getFwUpdateRecourse()); 385 profile.getClientLwM2mSettings().getFwUpdateRecourse());
@@ -357,4 +406,76 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl @@ -357,4 +406,76 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
357 helper.sendParametersOnThingsboardTelemetry(result, client.getSession()); 406 helper.sendParametersOnThingsboardTelemetry(result, client.getSession());
358 } 407 }
359 408
  409 + private static Optional<OtaPackageUpdateStatus> toOtaPackageUpdateStatus(FirmwareUpdateResult fwUpdateResult) {
  410 + switch (fwUpdateResult) {
  411 + case INITIAL:
  412 + return Optional.empty();
  413 + case UPDATE_SUCCESSFULLY:
  414 + return Optional.of(UPDATED);
  415 + case NOT_ENOUGH:
  416 + case OUT_OFF_MEMORY:
  417 + case CONNECTION_LOST:
  418 + case INTEGRITY_CHECK_FAILURE:
  419 + case UNSUPPORTED_TYPE:
  420 + case INVALID_URI:
  421 + case UPDATE_FAILED:
  422 + case UNSUPPORTED_PROTOCOL:
  423 + return Optional.of(FAILED);
  424 + default:
  425 + throw new CodecException("Invalid value stateFw %s for FirmwareUpdateStatus.", fwUpdateResult.name());
  426 + }
  427 + }
  428 +
  429 + private static Optional<OtaPackageUpdateStatus> toOtaPackageUpdateStatus(FirmwareUpdateState firmwareUpdateState) {
  430 + switch (firmwareUpdateState) {
  431 + case IDLE:
  432 + return Optional.empty();
  433 + case DOWNLOADING:
  434 + return Optional.of(DOWNLOADING);
  435 + case DOWNLOADED:
  436 + return Optional.of(DOWNLOADED);
  437 + case UPDATING:
  438 + return Optional.of(UPDATING);
  439 + default:
  440 + throw new CodecException("Invalid value stateFw %d for FirmwareUpdateStatus.", firmwareUpdateState);
  441 + }
  442 + }
  443 +
  444 + /**
  445 + * FirmwareUpdateStatus {
  446 + * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
  447 + */
  448 + public static Optional<OtaPackageUpdateStatus> toSwSateResultUpdateStatus(SoftwareUpdateState softwareUpdateState, SoftwareUpdateResult softwareUpdateResult) {
  449 + switch (softwareUpdateResult) {
  450 + case INITIAL:
  451 + switch (softwareUpdateState) {
  452 + case INITIAL:
  453 + case DOWNLOAD_STARTED:
  454 + return Optional.of(DOWNLOADING);
  455 + case DOWNLOADED:
  456 + return Optional.of(DOWNLOADED);
  457 + case DELIVERED:
  458 + return Optional.of(VERIFIED);
  459 + }
  460 + case DOWNLOADING:
  461 + return Optional.of(DOWNLOADING);
  462 + case SUCCESSFULLY_INSTALLED:
  463 + return Optional.of(UPDATED);
  464 + case SUCCESSFULLY_DOWNLOADED_VERIFIED:
  465 + return Optional.of(VERIFIED);
  466 + case NOT_ENOUGH_STORAGE:
  467 + case OUT_OFF_MEMORY:
  468 + case CONNECTION_LOST:
  469 + case PACKAGE_CHECK_FAILURE:
  470 + case UNSUPPORTED_PACKAGE_TYPE:
  471 + case INVALID_URI:
  472 + case UPDATE_ERROR:
  473 + case INSTALL_FAILURE:
  474 + case UN_INSTALL_FAILURE:
  475 + return Optional.of(FAILED);
  476 + default:
  477 + throw new CodecException("Invalid value stateFw %s %s for FirmwareUpdateStatus.", softwareUpdateState.name(), softwareUpdateResult.name());
  478 + }
  479 + }
  480 +
360 } 481 }
@@ -18,9 +18,10 @@ package org.thingsboard.server.transport.lwm2m.server.ota; @@ -18,9 +18,10 @@ package org.thingsboard.server.transport.lwm2m.server.ota;
18 import lombok.Data; 18 import lombok.Data;
19 import org.thingsboard.server.common.data.StringUtils; 19 import org.thingsboard.server.common.data.StringUtils;
20 import org.thingsboard.server.common.data.ota.OtaPackageType; 20 import org.thingsboard.server.common.data.ota.OtaPackageType;
21 -import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy;  
22 -import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw;  
23 -import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; 21 +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.LwM2MFirmwareUpdateStrategy;
  22 +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult;
  23 +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateState;
  24 +import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MSoftwareUpdateStrategy;
24 25
25 import java.util.Optional; 26 import java.util.Optional;
26 27
@@ -44,9 +45,10 @@ public class LwM2MClientOtaInfo { @@ -44,9 +45,10 @@ public class LwM2MClientOtaInfo {
44 private Integer deliveryMethod; 45 private Integer deliveryMethod;
45 46
46 //TODO: use value from device if applicable; 47 //TODO: use value from device if applicable;
47 - private LwM2MFirmwareUpdateStrategy strategy;  
48 - private UpdateStateFw updateState;  
49 - private UpdateResultFw updateResult; 48 + private LwM2MFirmwareUpdateStrategy fwStrategy;
  49 + private LwM2MSoftwareUpdateStrategy swStrategy;
  50 + private FirmwareUpdateState updateState;
  51 + private FirmwareUpdateResult updateResult;
50 52
51 private String failedPackageId; 53 private String failedPackageId;
52 private int retryAttempts; 54 private int retryAttempts;
@@ -54,7 +56,7 @@ public class LwM2MClientOtaInfo { @@ -54,7 +56,7 @@ public class LwM2MClientOtaInfo {
54 public LwM2MClientOtaInfo(String endpoint, OtaPackageType type, Integer strategyCode, String baseUrl) { 56 public LwM2MClientOtaInfo(String endpoint, OtaPackageType type, Integer strategyCode, String baseUrl) {
55 this.endpoint = endpoint; 57 this.endpoint = endpoint;
56 this.type = type; 58 this.type = type;
57 - this.strategy = LwM2MFirmwareUpdateStrategy.fromStrategyFwByCode(strategyCode); 59 + this.fwStrategy = LwM2MFirmwareUpdateStrategy.fromStrategyFwByCode(strategyCode);
58 this.baseUrl = baseUrl; 60 this.baseUrl = baseUrl;
59 } 61 }
60 62
@@ -88,7 +90,7 @@ public class LwM2MClientOtaInfo { @@ -88,7 +90,7 @@ public class LwM2MClientOtaInfo {
88 return StringUtils.isNotEmpty(currentName) || StringUtils.isNotEmpty(currentVersion5) || StringUtils.isNotEmpty(currentVersion3); 90 return StringUtils.isNotEmpty(currentName) || StringUtils.isNotEmpty(currentVersion5) || StringUtils.isNotEmpty(currentVersion3);
89 } 91 }
90 92
91 - public void setUpdateResult(UpdateResultFw updateResult) { 93 + public void setUpdateResult(FirmwareUpdateResult updateResult) {
92 this.updateResult = updateResult; 94 this.updateResult = updateResult;
93 switch (updateResult) { 95 switch (updateResult) {
94 case INITIAL: 96 case INITIAL:
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.transport.lwm2m.server.ota; 16 package org.thingsboard.server.transport.lwm2m.server.ota;
17 17
  18 +import org.thingsboard.server.common.data.device.data.lwm2m.OtherConfiguration;
18 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; 19 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
19 20
20 import java.util.Optional; 21 import java.util.Optional;
@@ -31,6 +32,10 @@ public interface LwM2MOtaUpdateService { @@ -31,6 +32,10 @@ public interface LwM2MOtaUpdateService {
31 32
32 void onCurrentFirmwareNameUpdate(LwM2mClient client, String name); 33 void onCurrentFirmwareNameUpdate(LwM2mClient client, String name);
33 34
  35 + void onCurrentFirmwareStrategyUpdate(LwM2mClient client, OtherConfiguration configuration);
  36 +
  37 + void onCurrentSoftwareStrategyUpdate(LwM2mClient client, OtherConfiguration configuration);
  38 +
34 void onCurrentFirmwareVersion3Update(LwM2mClient client, String version); 39 void onCurrentFirmwareVersion3Update(LwM2mClient client, String version);
35 40
36 void onCurrentFirmwareVersion5Update(LwM2mClient client, String version); 41 void onCurrentFirmwareVersion5Update(LwM2mClient client, String version);
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.ota.firmware;
  17 +
  18 +public enum FirmwareDeliveryMethod {
  19 + PULL(0, "Pull only"),
  20 + PUSH(1, "Push only"),
  21 + BOTH(2, "Push or Push");
  22 +
  23 + public int code;
  24 + public String type;
  25 +
  26 + FirmwareDeliveryMethod(int code, String type) {
  27 + this.code = code;
  28 + this.type = type;
  29 + }
  30 +
  31 + public static FirmwareDeliveryMethod fromStateFwByType(String type) {
  32 + for (FirmwareDeliveryMethod to : FirmwareDeliveryMethod.values()) {
  33 + if (to.type.equals(type)) {
  34 + return to;
  35 + }
  36 + }
  37 + throw new IllegalArgumentException(String.format("Unsupported FW delivery type : %s", type));
  38 + }
  39 +
  40 + public static FirmwareDeliveryMethod fromStateFwByCode(int code) {
  41 + for (FirmwareDeliveryMethod to : FirmwareDeliveryMethod.values()) {
  42 + if (to.code == code) {
  43 + return to;
  44 + }
  45 + }
  46 + throw new IllegalArgumentException(String.format("Unsupported FW delivery code : %s", code));
  47 + }
  48 +}
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/FirmwareUpdateResult.java renamed from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateResultFw.java
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.transport.lwm2m.server; 16 +package org.thingsboard.server.transport.lwm2m.server.ota.firmware;
17 17
18 import lombok.Getter; 18 import lombok.Getter;
19 19
@@ -30,7 +30,7 @@ import lombok.Getter; @@ -30,7 +30,7 @@ import lombok.Getter;
30 * 8: Firmware update failed. 30 * 8: Firmware update failed.
31 * 9: Unsupported protocol. 31 * 9: Unsupported protocol.
32 */ 32 */
33 -public enum UpdateResultFw { 33 +public enum FirmwareUpdateResult {
34 INITIAL(0, "Initial value", false), 34 INITIAL(0, "Initial value", false),
35 UPDATE_SUCCESSFULLY(1, "Firmware updated successfully", false), 35 UPDATE_SUCCESSFULLY(1, "Firmware updated successfully", false),
36 NOT_ENOUGH(2, "Not enough flash memory for the new firmware package", false), 36 NOT_ENOUGH(2, "Not enough flash memory for the new firmware package", false),
@@ -49,14 +49,14 @@ public enum UpdateResultFw { @@ -49,14 +49,14 @@ public enum UpdateResultFw {
49 @Getter 49 @Getter
50 private boolean again; 50 private boolean again;
51 51
52 - UpdateResultFw(int code, String type, boolean isAgain) { 52 + FirmwareUpdateResult(int code, String type, boolean isAgain) {
53 this.code = code; 53 this.code = code;
54 this.type = type; 54 this.type = type;
55 this.again = isAgain; 55 this.again = isAgain;
56 } 56 }
57 57
58 - public static UpdateResultFw fromUpdateResultFwByType(String type) {  
59 - for (UpdateResultFw to : UpdateResultFw.values()) { 58 + public static FirmwareUpdateResult fromUpdateResultFwByType(String type) {
  59 + for (FirmwareUpdateResult to : FirmwareUpdateResult.values()) {
60 if (to.type.equals(type)) { 60 if (to.type.equals(type)) {
61 return to; 61 return to;
62 } 62 }
@@ -64,8 +64,8 @@ public enum UpdateResultFw { @@ -64,8 +64,8 @@ public enum UpdateResultFw {
64 throw new IllegalArgumentException(String.format("Unsupported FW Update Result type : %s", type)); 64 throw new IllegalArgumentException(String.format("Unsupported FW Update Result type : %s", type));
65 } 65 }
66 66
67 - public static UpdateResultFw fromUpdateResultFwByCode(int code) {  
68 - for (UpdateResultFw to : UpdateResultFw.values()) { 67 + public static FirmwareUpdateResult fromUpdateResultFwByCode(int code) {
  68 + for (FirmwareUpdateResult to : FirmwareUpdateResult.values()) {
69 if (to.code == code) { 69 if (to.code == code) {
70 return to; 70 return to;
71 } 71 }
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/FirmwareUpdateState.java renamed from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/UpdateStateFw.java
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.transport.lwm2m.server; 16 +package org.thingsboard.server.transport.lwm2m.server.ota.firmware;
17 17
18 /** 18 /**
19 * /** State R 19 * /** State R
@@ -22,7 +22,7 @@ package org.thingsboard.server.transport.lwm2m.server; @@ -22,7 +22,7 @@ package org.thingsboard.server.transport.lwm2m.server;
22 * 2: Downloaded 22 * 2: Downloaded
23 * 3: Updating 23 * 3: Updating
24 */ 24 */
25 -public enum UpdateStateFw { 25 +public enum FirmwareUpdateState {
26 IDLE(0, "Idle"), 26 IDLE(0, "Idle"),
27 DOWNLOADING(1, "Downloading"), 27 DOWNLOADING(1, "Downloading"),
28 DOWNLOADED(2, "Downloaded"), 28 DOWNLOADED(2, "Downloaded"),
@@ -31,13 +31,13 @@ public enum UpdateStateFw { @@ -31,13 +31,13 @@ public enum UpdateStateFw {
31 public int code; 31 public int code;
32 public String type; 32 public String type;
33 33
34 - UpdateStateFw(int code, String type) { 34 + FirmwareUpdateState(int code, String type) {
35 this.code = code; 35 this.code = code;
36 this.type = type; 36 this.type = type;
37 } 37 }
38 38
39 - public static UpdateStateFw fromStateFwByType(String type) {  
40 - for (UpdateStateFw to : UpdateStateFw.values()) { 39 + public static FirmwareUpdateState fromStateFwByType(String type) {
  40 + for (FirmwareUpdateState to : FirmwareUpdateState.values()) {
41 if (to.type.equals(type)) { 41 if (to.type.equals(type)) {
42 return to; 42 return to;
43 } 43 }
@@ -45,8 +45,8 @@ public enum UpdateStateFw { @@ -45,8 +45,8 @@ public enum UpdateStateFw {
45 throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type)); 45 throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type));
46 } 46 }
47 47
48 - public static UpdateStateFw fromStateFwByCode(int code) {  
49 - for (UpdateStateFw to : UpdateStateFw.values()) { 48 + public static FirmwareUpdateState fromStateFwByCode(int code) {
  49 + for (FirmwareUpdateState to : FirmwareUpdateState.values()) {
50 if (to.code == code) { 50 if (to.code == code) {
51 return to; 51 return to;
52 } 52 }
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/firmware/LwM2MFirmwareUpdateStrategy.java renamed from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MFirmwareUpdateStrategy.java
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.transport.lwm2m.server; 16 +package org.thingsboard.server.transport.lwm2m.server.ota.firmware;
17 17
18 public enum LwM2MFirmwareUpdateStrategy { 18 public enum LwM2MFirmwareUpdateStrategy {
19 OBJ_5_BINARY(1, "ObjectId 5, Binary"), 19 OBJ_5_BINARY(1, "ObjectId 5, Binary"),
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/software/LwM2MSoftwareUpdateStrategy.java renamed from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2MSoftwareUpdateStrategy.java
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.transport.lwm2m.server; 16 +package org.thingsboard.server.transport.lwm2m.server.ota.software;
17 17
18 public enum LwM2MSoftwareUpdateStrategy { 18 public enum LwM2MSoftwareUpdateStrategy {
19 BINARY(1, "ObjectId 9, Binary"), 19 BINARY(1, "ObjectId 9, Binary"),
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.ota.software;
  17 +
  18 +/**
  19 + * SW Update Result
  20 + * Contains the result of downloading or installing/uninstalling the software
  21 + * 0: Initial value.
  22 + * - Prior to download any new package in the Device, Update Result MUST be reset to this initial value.
  23 + * - One side effect of executing the Uninstall resource is to reset Update Result to this initial value "0".
  24 + * 1: Downloading.
  25 + * - The package downloading process is on-going.
  26 + * 2: Software successfully installed.
  27 + * 3: Successfully Downloaded and package integrity verified
  28 + * (( 4-49, for expansion, of other scenarios))
  29 + * ** Failed
  30 + * 50: Not enough storage for the new software package.
  31 + * 51: Out of memory during downloading process.
  32 + * 52: Connection lost during downloading process.
  33 + * 53: Package integrity check failure.
  34 + * 54: Unsupported package type.
  35 + * 56: Invalid URI
  36 + * 57: Device defined update error
  37 + * 58: Software installation failure
  38 + * 59: Uninstallation Failure during forUpdate(arg=0)
  39 + * 60-200 : (for expansion, selection to be in blocks depending on new introduction of features)
  40 + * This Resource MAY be reported by sending Observe operation.
  41 + */
  42 +public enum SoftwareUpdateResult {
  43 + INITIAL(0, "Initial value", false),
  44 + DOWNLOADING(1, "Downloading", false),
  45 + SUCCESSFULLY_INSTALLED(2, "Software successfully installed", false),
  46 + SUCCESSFULLY_DOWNLOADED_VERIFIED(3, "Successfully Downloaded and package integrity verified", false),
  47 + NOT_ENOUGH_STORAGE(50, "Not enough storage for the new software package", true),
  48 + OUT_OFF_MEMORY(51, "Out of memory during downloading process", true),
  49 + CONNECTION_LOST(52, "Connection lost during downloading process", false),
  50 + PACKAGE_CHECK_FAILURE(53, "Package integrity check failure.", false),
  51 + UNSUPPORTED_PACKAGE_TYPE(54, "Unsupported package type", false),
  52 + INVALID_URI(56, "Invalid URI", true),
  53 + UPDATE_ERROR(57, "Device defined update error", true),
  54 + INSTALL_FAILURE(58, "Software installation failure", true),
  55 + UN_INSTALL_FAILURE(59, "Uninstallation Failure during forUpdate(arg=0)", true);
  56 +
  57 + public int code;
  58 + public String type;
  59 + public boolean isAgain;
  60 +
  61 + SoftwareUpdateResult(int code, String type, boolean isAgain) {
  62 + this.code = code;
  63 + this.type = type;
  64 + this.isAgain = isAgain;
  65 + }
  66 +
  67 + public static SoftwareUpdateResult fromUpdateResultSwByType(String type) {
  68 + for (SoftwareUpdateResult to : SoftwareUpdateResult.values()) {
  69 + if (to.type.equals(type)) {
  70 + return to;
  71 + }
  72 + }
  73 + throw new IllegalArgumentException(String.format("Unsupported SW Update Result type : %s", type));
  74 + }
  75 +
  76 + public static SoftwareUpdateResult fromUpdateResultSwByCode(int code) {
  77 + for (SoftwareUpdateResult to : SoftwareUpdateResult.values()) {
  78 + if (to.code == code) {
  79 + return to;
  80 + }
  81 + }
  82 + throw new IllegalArgumentException(String.format("Unsupported SW Update Result code : %s", code));
  83 + }
  84 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.ota.software;
  17 +
  18 +/**
  19 + * SW Update State R
  20 + * 0: INITIAL Before downloading. (see 5.1.2.1)
  21 + * 1: DOWNLOAD STARTED The downloading process has started and is on-going. (see 5.1.2.2)
  22 + * 2: DOWNLOADED The package has been completely downloaded (see 5.1.2.3)
  23 + * 3: DELIVERED In that state, the package has been correctly downloaded and is ready to be installed. (see 5.1.2.4)
  24 + * If executing the Install Resource failed, the state remains at DELIVERED.
  25 + * If executing the Install Resource was successful, the state changes from DELIVERED to INSTALLED.
  26 + * After executing the UnInstall Resource, the state changes to INITIAL.
  27 + * 4: INSTALLED
  28 + */
  29 +public enum SoftwareUpdateState {
  30 + INITIAL(0, "Initial"),
  31 + DOWNLOAD_STARTED(1, "DownloadStarted"),
  32 + DOWNLOADED(2, "Downloaded"),
  33 + DELIVERED(3, "Delivered"),
  34 + INSTALLED(4, "Installed");
  35 +
  36 + public int code;
  37 + public String type;
  38 +
  39 + SoftwareUpdateState(int code, String type) {
  40 + this.code = code;
  41 + this.type = type;
  42 + }
  43 +
  44 + public static SoftwareUpdateState fromUpdateStateSwByType(String type) {
  45 + for (SoftwareUpdateState to : SoftwareUpdateState.values()) {
  46 + if (to.type.equals(type)) {
  47 + return to;
  48 + }
  49 + }
  50 + throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", type));
  51 + }
  52 +
  53 + public static SoftwareUpdateState fromUpdateStateSwByCode(int code) {
  54 + for (SoftwareUpdateState to : SoftwareUpdateState.values()) {
  55 + if (to.code == code) {
  56 + return to;
  57 + }
  58 + }
  59 + throw new IllegalArgumentException(String.format("Unsupported SW State type : %s", code));
  60 + }
  61 +}
  62 +
@@ -16,26 +16,28 @@ @@ -16,26 +16,28 @@
16 package org.thingsboard.server.transport.lwm2m.server.store; 16 package org.thingsboard.server.transport.lwm2m.server.store;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 -import org.eclipse.leshan.server.security.EditableSecurityStore;  
20 import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException; 19 import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
21 import org.eclipse.leshan.server.security.SecurityInfo; 20 import org.eclipse.leshan.server.security.SecurityInfo;
22 -import org.eclipse.leshan.server.security.SecurityStore;  
23 -import org.eclipse.leshan.server.security.SecurityStoreListener;  
24 import org.jetbrains.annotations.Nullable; 21 import org.jetbrains.annotations.Nullable;
25 -import org.springframework.stereotype.Component;  
26 -import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;  
27 import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; 22 import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
28 import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; 23 import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
29 -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;  
30 -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;  
31 24
32 -import java.util.Collection; 25 +import java.util.HashSet;
  26 +import java.util.Map;
  27 +import java.util.Set;
  28 +import java.util.concurrent.ConcurrentHashMap;
  29 +import java.util.concurrent.ConcurrentMap;
  30 +import java.util.concurrent.locks.Lock;
  31 +import java.util.concurrent.locks.ReentrantLock;
  32 +
  33 +import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT;
33 34
34 @Slf4j 35 @Slf4j
35 -public class TbLwM2mSecurityStore implements TbEditableSecurityStore { 36 +public class TbLwM2mSecurityStore implements TbMainSecurityStore {
36 37
37 private final TbEditableSecurityStore securityStore; 38 private final TbEditableSecurityStore securityStore;
38 private final LwM2mCredentialsSecurityInfoValidator validator; 39 private final LwM2mCredentialsSecurityInfoValidator validator;
  40 + private final ConcurrentMap<String, Set<String>> endpointRegistrations = new ConcurrentHashMap<>();
39 41
40 public TbLwM2mSecurityStore(TbEditableSecurityStore securityStore, LwM2mCredentialsSecurityInfoValidator validator) { 42 public TbLwM2mSecurityStore(TbEditableSecurityStore securityStore, LwM2mCredentialsSecurityInfoValidator validator) {
41 this.securityStore = securityStore; 43 this.securityStore = securityStore;
@@ -67,25 +69,43 @@ public class TbLwM2mSecurityStore implements TbEditableSecurityStore { @@ -67,25 +69,43 @@ public class TbLwM2mSecurityStore implements TbEditableSecurityStore {
67 69
68 @Nullable 70 @Nullable
69 public SecurityInfo fetchAndPutSecurityInfo(String credentialsId) { 71 public SecurityInfo fetchAndPutSecurityInfo(String credentialsId) {
70 - TbLwM2MSecurityInfo securityInfo = validator.getEndpointSecurityInfoByCredentialsId(credentialsId, LwM2mTransportUtil.LwM2mTypeServer.CLIENT);  
71 - try {  
72 - if (securityInfo != null) { 72 + TbLwM2MSecurityInfo securityInfo = validator.getEndpointSecurityInfoByCredentialsId(credentialsId, CLIENT);
  73 + doPut(securityInfo);
  74 + return securityInfo != null ? securityInfo.getSecurityInfo() : null;
  75 + }
  76 +
  77 + private void doPut(TbLwM2MSecurityInfo securityInfo) {
  78 + if (securityInfo != null) {
  79 + try {
73 securityStore.put(securityInfo); 80 securityStore.put(securityInfo);
  81 + } catch (NonUniqueSecurityInfoException e) {
  82 + log.trace("Failed to add security info: {}", securityInfo, e);
74 } 83 }
75 - } catch (NonUniqueSecurityInfoException e) {  
76 - log.trace("Failed to add security info: {}", securityInfo, e);  
77 } 84 }
78 - return securityInfo != null ? securityInfo.getSecurityInfo() : null;  
79 } 85 }
80 86
81 @Override 87 @Override
82 - public void put(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException {  
83 - securityStore.put(tbSecurityInfo); 88 + public void putX509(TbLwM2MSecurityInfo securityInfo) throws NonUniqueSecurityInfoException {
  89 + securityStore.put(securityInfo);
84 } 90 }
85 91
86 @Override 92 @Override
87 - public void remove(String endpoint) {  
88 - //TODO: Make sure we delay removal of security store from endpoint due to reg/unreg race condition.  
89 -// securityStore.remove(endpoint); 93 + public void registerX509(String endpoint, String registrationId) {
  94 + endpointRegistrations.computeIfAbsent(endpoint, ep -> new HashSet<>()).add(registrationId);
  95 + }
  96 +
  97 + @Override
  98 + public void remove(String endpoint, String registrationId) {
  99 + Set<String> epRegistrationIds = endpointRegistrations.get(endpoint);
  100 + boolean shouldRemove;
  101 + if (epRegistrationIds == null) {
  102 + shouldRemove = true;
  103 + } else {
  104 + epRegistrationIds.remove(registrationId);
  105 + shouldRemove = epRegistrationIds.isEmpty();
  106 + }
  107 + if (shouldRemove) {
  108 + securityStore.remove(endpoint);
  109 + }
90 } 110 }
91 } 111 }
@@ -51,7 +51,7 @@ public class TbLwM2mStoreFactory { @@ -51,7 +51,7 @@ public class TbLwM2mStoreFactory {
51 } 51 }
52 52
53 @Bean 53 @Bean
54 - private TbEditableSecurityStore securityStore() { 54 + private TbMainSecurityStore securityStore() {
55 return new TbLwM2mSecurityStore(redisConfiguration.isPresent() && useRedis ? 55 return new TbLwM2mSecurityStore(redisConfiguration.isPresent() && useRedis ?
56 new TbLwM2mRedisSecurityStore(redisConfiguration.get().redisConnectionFactory()) : new TbInMemorySecurityStore(), validator); 56 new TbLwM2mRedisSecurityStore(redisConfiguration.get().redisConnectionFactory()) : new TbInMemorySecurityStore(), validator);
57 } 57 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.store;
  17 +
  18 +import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
  19 +import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo;
  20 +
  21 +public interface TbMainSecurityStore extends TbSecurityStore {
  22 +
  23 + void putX509(TbLwM2MSecurityInfo tbSecurityInfo) throws NonUniqueSecurityInfoException;
  24 +
  25 + void registerX509(String endpoint, String registrationId);
  26 +
  27 + void remove(String endpoint, String registrationId);
  28 +
  29 +}
@@ -37,11 +37,12 @@ import org.eclipse.leshan.server.registration.Registration; @@ -37,11 +37,12 @@ import org.eclipse.leshan.server.registration.Registration;
37 import org.springframework.context.annotation.Lazy; 37 import org.springframework.context.annotation.Lazy;
38 import org.springframework.stereotype.Service; 38 import org.springframework.stereotype.Service;
39 import org.thingsboard.common.util.DonAsynchron; 39 import org.thingsboard.common.util.DonAsynchron;
40 -import org.thingsboard.common.util.ThingsBoardExecutors;  
41 import org.thingsboard.server.cache.ota.OtaPackageDataCache; 40 import org.thingsboard.server.cache.ota.OtaPackageDataCache;
42 import org.thingsboard.server.common.data.Device; 41 import org.thingsboard.server.common.data.Device;
43 import org.thingsboard.server.common.data.DeviceProfile; 42 import org.thingsboard.server.common.data.DeviceProfile;
  43 +import org.thingsboard.server.common.data.StringUtils;
44 import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; 44 import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes;
  45 +import org.thingsboard.server.common.data.device.data.lwm2m.OtherConfiguration;
45 import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration; 46 import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration;
46 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; 47 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
47 import org.thingsboard.server.common.data.ota.OtaPackageUtil; 48 import org.thingsboard.server.common.data.ota.OtaPackageUtil;
@@ -82,6 +83,8 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttrib @@ -82,6 +83,8 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttrib
82 import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; 83 import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest;
83 import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; 84 import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
84 import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService; 85 import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService;
  86 +import org.thingsboard.server.transport.lwm2m.server.ota.firmware.LwM2MFirmwareUpdateStrategy;
  87 +import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MSoftwareUpdateStrategy;
85 import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; 88 import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler;
86 import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; 89 import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
87 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; 90 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
@@ -100,23 +103,22 @@ import java.util.Set; @@ -100,23 +103,22 @@ import java.util.Set;
100 import java.util.UUID; 103 import java.util.UUID;
101 import java.util.concurrent.ConcurrentHashMap; 104 import java.util.concurrent.ConcurrentHashMap;
102 import java.util.concurrent.CountDownLatch; 105 import java.util.concurrent.CountDownLatch;
103 -import java.util.concurrent.ExecutorService;  
104 import java.util.concurrent.TimeUnit; 106 import java.util.concurrent.TimeUnit;
105 import java.util.stream.Collectors; 107 import java.util.stream.Collectors;
106 108
107 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; 109 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
108 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID;  
109 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID;  
110 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_DELIVERY_METHOD;  
111 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID;  
112 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID;  
113 -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID;  
114 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; 110 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR;
115 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; 111 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO;
116 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; 112 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN;
117 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; 113 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId;
118 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; 114 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString;
119 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; 115 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId;
  116 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_3_VER_ID;
  117 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_5_VER_ID;
  118 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_DELIVERY_METHOD;
  119 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_NAME_ID;
  120 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_RESULT_ID;
  121 +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_STATE_ID;
120 122
121 123
122 @Slf4j 124 @Slf4j
@@ -130,16 +132,13 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl @@ -130,16 +132,13 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
130 private final LwM2mTransportContext context; 132 private final LwM2mTransportContext context;
131 private final LwM2MAttributesService attributesService; 133 private final LwM2MAttributesService attributesService;
132 private final LwM2MOtaUpdateService otaService; 134 private final LwM2MOtaUpdateService otaService;
133 - public final LwM2MTransportServerConfig config; 135 + private final LwM2MTransportServerConfig config;
134 private final LwM2MTelemetryLogService logService; 136 private final LwM2MTelemetryLogService logService;
135 - public final OtaPackageDataCache otaPackageDataCache;  
136 - public final LwM2mTransportServerHelper helper; 137 + private final LwM2mTransportServerHelper helper;
137 private final TbLwM2MDtlsSessionStore sessionStore; 138 private final TbLwM2MDtlsSessionStore sessionStore;
138 - public final LwM2mClientContext clientContext; 139 + private final LwM2mClientContext clientContext;
139 private final LwM2MRpcRequestHandler rpcHandler; 140 private final LwM2MRpcRequestHandler rpcHandler;
140 - public final LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler;  
141 -  
142 - public final Map<String, Integer> firmwareUpdateState; 141 + private final LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler;
143 142
144 public DefaultLwM2MUplinkMsgHandler(TransportService transportService, 143 public DefaultLwM2MUplinkMsgHandler(TransportService transportService,
145 LwM2MTransportServerConfig config, 144 LwM2MTransportServerConfig config,
@@ -150,7 +149,6 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl @@ -150,7 +149,6 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
150 @Lazy LwM2MAttributesService attributesService, 149 @Lazy LwM2MAttributesService attributesService,
151 @Lazy LwM2MRpcRequestHandler rpcHandler, 150 @Lazy LwM2MRpcRequestHandler rpcHandler,
152 @Lazy LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler, 151 @Lazy LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler,
153 - OtaPackageDataCache otaPackageDataCache,  
154 LwM2mTransportContext context, TbLwM2MDtlsSessionStore sessionStore) { 152 LwM2mTransportContext context, TbLwM2MDtlsSessionStore sessionStore) {
155 this.transportService = transportService; 153 this.transportService = transportService;
156 this.attributesService = attributesService; 154 this.attributesService = attributesService;
@@ -161,9 +159,7 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl @@ -161,9 +159,7 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
161 this.logService = logService; 159 this.logService = logService;
162 this.rpcHandler = rpcHandler; 160 this.rpcHandler = rpcHandler;
163 this.defaultLwM2MDownlinkMsgHandler = defaultLwM2MDownlinkMsgHandler; 161 this.defaultLwM2MDownlinkMsgHandler = defaultLwM2MDownlinkMsgHandler;
164 - this.otaPackageDataCache = otaPackageDataCache;  
165 this.context = context; 162 this.context = context;
166 - this.firmwareUpdateState = new ConcurrentHashMap<>();  
167 this.sessionStore = sessionStore; 163 this.sessionStore = sessionStore;
168 } 164 }
169 165
@@ -216,7 +212,7 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl @@ -216,7 +212,7 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
216 } 212 }
217 logService.log(lwM2MClient, LOG_LWM2M_INFO + ": Client registered with registration id: " + registration.getId()); 213 logService.log(lwM2MClient, LOG_LWM2M_INFO + ": Client registered with registration id: " + registration.getId());
218 SessionInfoProto sessionInfo = lwM2MClient.getSession(); 214 SessionInfoProto sessionInfo = lwM2MClient.getSession();
219 - transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, attributesService, rpcHandler, sessionInfo)); 215 + transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, attributesService, rpcHandler, sessionInfo, transportService));
220 log.warn("40) sessionId [{}] Registering rpc subscription after Registration client", new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); 216 log.warn("40) sessionId [{}] Registering rpc subscription after Registration client", new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
221 TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder() 217 TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder()
222 .setSessionInfo(sessionInfo) 218 .setSessionInfo(sessionInfo)
@@ -780,6 +776,21 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl @@ -780,6 +776,21 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
780 clients.forEach(client -> sendCancelObserveRequest(targetId, client)); 776 clients.forEach(client -> sendCancelObserveRequest(targetId, client));
781 } 777 }
782 } 778 }
  779 +
  780 + // update value in fwInfo
  781 + OtherConfiguration newLwM2mSettings = newProfile.getClientLwM2mSettings();
  782 + OtherConfiguration oldLwM2mSettings = oldProfile.getClientLwM2mSettings();
  783 + if (!newLwM2mSettings.getFwUpdateStrategy().equals(oldLwM2mSettings.getFwUpdateStrategy())
  784 + || (StringUtils.isNotEmpty(newLwM2mSettings.getFwUpdateRecourse()) &&
  785 + !newLwM2mSettings.getFwUpdateRecourse().equals(oldLwM2mSettings.getFwUpdateRecourse()))) {
  786 + clients.forEach(lwM2MClient -> otaService.onCurrentFirmwareStrategyUpdate(lwM2MClient, newLwM2mSettings));
  787 + }
  788 +
  789 + if (!newLwM2mSettings.getSwUpdateStrategy().equals(oldLwM2mSettings.getSwUpdateStrategy())
  790 + || (StringUtils.isNotEmpty(newLwM2mSettings.getSwUpdateRecourse()) &&
  791 + !newLwM2mSettings.getSwUpdateRecourse().equals(oldLwM2mSettings.getSwUpdateRecourse()))) {
  792 + clients.forEach(lwM2MClient -> otaService.onCurrentSoftwareStrategyUpdate(lwM2MClient, newLwM2mSettings));
  793 + }
783 } 794 }
784 } 795 }
785 796
@@ -877,7 +888,7 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl @@ -877,7 +888,7 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
877 */ 888 */
878 private void reportActivityAndRegister(SessionInfoProto sessionInfo) { 889 private void reportActivityAndRegister(SessionInfoProto sessionInfo) {
879 if (sessionInfo != null && transportService.reportActivity(sessionInfo) == null) { 890 if (sessionInfo != null && transportService.reportActivity(sessionInfo) == null) {
880 - transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, attributesService, rpcHandler, sessionInfo)); 891 + transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, attributesService, rpcHandler, sessionInfo, transportService));
881 this.reportActivitySubscription(sessionInfo); 892 this.reportActivitySubscription(sessionInfo);
882 } 893 }
883 } 894 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.uplink;
  17 +
  18 +public enum LwM2mTypeServer {
  19 + BOOTSTRAP(0, "bootstrap"),
  20 + CLIENT(1, "client");
  21 +
  22 + public int code;
  23 + public String type;
  24 +
  25 + LwM2mTypeServer(int code, String type) {
  26 + this.code = code;
  27 + this.type = type;
  28 + }
  29 +
  30 + public static LwM2mTypeServer fromLwM2mTypeServer(String type) {
  31 + for (LwM2mTypeServer sm : LwM2mTypeServer.values()) {
  32 + if (sm.type.equals(type)) {
  33 + return sm;
  34 + }
  35 + }
  36 + throw new IllegalArgumentException(String.format("Unsupported typeServer type : %d", type));
  37 + }
  38 +}
  39 +
@@ -17,6 +17,7 @@ package org.thingsboard.server.transport.mqtt; @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.mqtt;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
19 import com.google.gson.JsonParseException; 19 import com.google.gson.JsonParseException;
  20 +import io.netty.channel.ChannelFuture;
20 import io.netty.channel.ChannelHandlerContext; 21 import io.netty.channel.ChannelHandlerContext;
21 import io.netty.channel.ChannelInboundHandlerAdapter; 22 import io.netty.channel.ChannelInboundHandlerAdapter;
22 import io.netty.handler.codec.mqtt.MqttConnAckMessage; 23 import io.netty.handler.codec.mqtt.MqttConnAckMessage;
@@ -47,8 +48,9 @@ import org.thingsboard.server.common.data.DeviceProfile; @@ -47,8 +48,9 @@ import org.thingsboard.server.common.data.DeviceProfile;
47 import org.thingsboard.server.common.data.DeviceTransportType; 48 import org.thingsboard.server.common.data.DeviceTransportType;
48 import org.thingsboard.server.common.data.TransportPayloadType; 49 import org.thingsboard.server.common.data.TransportPayloadType;
49 import org.thingsboard.server.common.data.device.profile.MqttTopics; 50 import org.thingsboard.server.common.data.device.profile.MqttTopics;
50 -import org.thingsboard.server.common.data.ota.OtaPackageType;  
51 import org.thingsboard.server.common.data.id.OtaPackageId; 51 import org.thingsboard.server.common.data.id.OtaPackageId;
  52 +import org.thingsboard.server.common.data.ota.OtaPackageType;
  53 +import org.thingsboard.server.common.data.rpc.RpcStatus;
52 import org.thingsboard.server.common.msg.EncryptionUtil; 54 import org.thingsboard.server.common.msg.EncryptionUtil;
53 import org.thingsboard.server.common.msg.tools.TbRateLimitsException; 55 import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
54 import org.thingsboard.server.common.transport.SessionMsgListener; 56 import org.thingsboard.server.common.transport.SessionMsgListener;
@@ -813,7 +815,31 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -813,7 +815,31 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
813 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { 815 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {
814 log.trace("[{}] Received RPC command to device", sessionId); 816 log.trace("[{}] Received RPC command to device", sessionId);
815 try { 817 try {
816 - deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, rpcRequest).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); 818 + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, rpcRequest)
  819 + .ifPresent(payload -> {
  820 + ChannelFuture channelFuture = deviceSessionCtx.getChannel().writeAndFlush(payload);
  821 + if (rpcRequest.getPersisted()) {
  822 + channelFuture.addListener(future -> {
  823 + RpcStatus status;
  824 + Throwable t = future.cause();
  825 + if (t != null) {
  826 + log.error("Failed delivering RPC command to device!", t);
  827 + status = RpcStatus.FAILED;
  828 + } else if (rpcRequest.getOneway()) {
  829 + status = RpcStatus.SUCCESSFUL;
  830 + } else {
  831 + status = RpcStatus.DELIVERED;
  832 + }
  833 + TransportProtos.ToDevicePersistedRpcResponseMsg msg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
  834 + .setRequestId(rpcRequest.getRequestId())
  835 + .setRequestIdLSB(rpcRequest.getRequestIdLSB())
  836 + .setRequestIdMSB(rpcRequest.getRequestIdMSB())
  837 + .setStatus(status.name())
  838 + .build();
  839 + transportService.process(deviceSessionCtx.getSessionInfo(), msg, TransportServiceCallback.EMPTY);
  840 + });
  841 + }
  842 + });
817 } catch (Exception e) { 843 } catch (Exception e) {
818 log.trace("[{}] Failed to convert device RPC command to MQTT msg", sessionId, e); 844 log.trace("[{}] Failed to convert device RPC command to MQTT msg", sessionId, e);
819 } 845 }
@@ -15,9 +15,13 @@ @@ -15,9 +15,13 @@
15 */ 15 */
16 package org.thingsboard.server.transport.mqtt.session; 16 package org.thingsboard.server.transport.mqtt.session;
17 17
  18 +import io.netty.channel.ChannelFuture;
18 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
19 import org.thingsboard.server.common.data.DeviceProfile; 20 import org.thingsboard.server.common.data.DeviceProfile;
  21 +import org.thingsboard.server.common.data.rpc.RpcStatus;
20 import org.thingsboard.server.common.transport.SessionMsgListener; 22 import org.thingsboard.server.common.transport.SessionMsgListener;
  23 +import org.thingsboard.server.common.transport.TransportService;
  24 +import org.thingsboard.server.common.transport.TransportServiceCallback;
21 import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; 25 import org.thingsboard.server.common.transport.auth.TransportDeviceInfo;
22 import org.thingsboard.server.gen.transport.TransportProtos; 26 import org.thingsboard.server.gen.transport.TransportProtos;
23 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; 27 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
@@ -32,9 +36,11 @@ import java.util.concurrent.ConcurrentMap; @@ -32,9 +36,11 @@ import java.util.concurrent.ConcurrentMap;
32 public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext implements SessionMsgListener { 36 public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext implements SessionMsgListener {
33 37
34 private final GatewaySessionHandler parent; 38 private final GatewaySessionHandler parent;
  39 + private final TransportService transportService;
35 40
36 public GatewayDeviceSessionCtx(GatewaySessionHandler parent, TransportDeviceInfo deviceInfo, 41 public GatewayDeviceSessionCtx(GatewaySessionHandler parent, TransportDeviceInfo deviceInfo,
37 - DeviceProfile deviceProfile, ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap) { 42 + DeviceProfile deviceProfile, ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap,
  43 + TransportService transportService) {
38 super(UUID.randomUUID(), mqttQoSMap); 44 super(UUID.randomUUID(), mqttQoSMap);
39 this.parent = parent; 45 this.parent = parent;
40 setSessionInfo(SessionInfoProto.newBuilder() 46 setSessionInfo(SessionInfoProto.newBuilder()
@@ -56,6 +62,7 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple @@ -56,6 +62,7 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple
56 .build()); 62 .build());
57 setDeviceInfo(deviceInfo); 63 setDeviceInfo(deviceInfo);
58 setDeviceProfile(deviceProfile); 64 setDeviceProfile(deviceProfile);
  65 + this.transportService = transportService;
59 } 66 }
60 67
61 @Override 68 @Override
@@ -89,7 +96,32 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple @@ -89,7 +96,32 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple
89 @Override 96 @Override
90 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg request) { 97 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg request) {
91 try { 98 try {
92 - parent.getPayloadAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), request).ifPresent(parent::writeAndFlush); 99 + parent.getPayloadAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), request).ifPresent(
  100 + payload -> {
  101 + ChannelFuture channelFuture = parent.writeAndFlush(payload);
  102 + if (request.getPersisted()) {
  103 + channelFuture.addListener(future -> {
  104 + RpcStatus status;
  105 + Throwable t = future.cause();
  106 + if (t != null) {
  107 + log.error("Failed delivering RPC command to device!", t);
  108 + status = RpcStatus.FAILED;
  109 + } else if (request.getOneway()) {
  110 + status = RpcStatus.SUCCESSFUL;
  111 + } else {
  112 + status = RpcStatus.DELIVERED;
  113 + }
  114 + TransportProtos.ToDevicePersistedRpcResponseMsg msg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
  115 + .setRequestId(request.getRequestId())
  116 + .setRequestIdLSB(request.getRequestIdLSB())
  117 + .setRequestIdMSB(request.getRequestIdMSB())
  118 + .setStatus(status.name())
  119 + .build();
  120 + transportService.process(getSessionInfo(), msg, TransportServiceCallback.EMPTY);
  121 + });
  122 + }
  123 + }
  124 + );
93 } catch (Exception e) { 125 } catch (Exception e) {
94 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); 126 log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e);
95 } 127 }
@@ -28,6 +28,7 @@ import com.google.gson.JsonSyntaxException; @@ -28,6 +28,7 @@ import com.google.gson.JsonSyntaxException;
28 import com.google.protobuf.InvalidProtocolBufferException; 28 import com.google.protobuf.InvalidProtocolBufferException;
29 import com.google.protobuf.ProtocolStringList; 29 import com.google.protobuf.ProtocolStringList;
30 import io.netty.buffer.ByteBuf; 30 import io.netty.buffer.ByteBuf;
  31 +import io.netty.channel.ChannelFuture;
31 import io.netty.channel.ChannelHandlerContext; 32 import io.netty.channel.ChannelHandlerContext;
32 import io.netty.handler.codec.mqtt.MqttMessage; 33 import io.netty.handler.codec.mqtt.MqttMessage;
33 import io.netty.handler.codec.mqtt.MqttPublishMessage; 34 import io.netty.handler.codec.mqtt.MqttPublishMessage;
@@ -188,8 +189,8 @@ public class GatewaySessionHandler { @@ -188,8 +189,8 @@ public class GatewaySessionHandler {
188 } 189 }
189 } 190 }
190 191
191 - void writeAndFlush(MqttMessage mqttMessage) {  
192 - channel.writeAndFlush(mqttMessage); 192 + ChannelFuture writeAndFlush(MqttMessage mqttMessage) {
  193 + return channel.writeAndFlush(mqttMessage);
193 } 194 }
194 195
195 int nextMsgId() { 196 int nextMsgId() {
@@ -251,7 +252,7 @@ public class GatewaySessionHandler { @@ -251,7 +252,7 @@ public class GatewaySessionHandler {
251 new TransportServiceCallback<GetOrCreateDeviceFromGatewayResponse>() { 252 new TransportServiceCallback<GetOrCreateDeviceFromGatewayResponse>() {
252 @Override 253 @Override
253 public void onSuccess(GetOrCreateDeviceFromGatewayResponse msg) { 254 public void onSuccess(GetOrCreateDeviceFromGatewayResponse msg) {
254 - GatewayDeviceSessionCtx deviceSessionCtx = new GatewayDeviceSessionCtx(GatewaySessionHandler.this, msg.getDeviceInfo(), msg.getDeviceProfile(), mqttQoSMap); 255 + GatewayDeviceSessionCtx deviceSessionCtx = new GatewayDeviceSessionCtx(GatewaySessionHandler.this, msg.getDeviceInfo(), msg.getDeviceProfile(), mqttQoSMap, transportService);
255 if (devices.putIfAbsent(deviceName, deviceSessionCtx) == null) { 256 if (devices.putIfAbsent(deviceName, deviceSessionCtx) == null) {
256 log.trace("[{}] First got or created device [{}], type [{}] for the gateway session", sessionId, deviceName, deviceType); 257 log.trace("[{}] First got or created device [{}], type [{}] for the gateway session", sessionId, deviceName, deviceType);
257 SessionInfoProto deviceSessionInfo = deviceSessionCtx.getSessionInfo(); 258 SessionInfoProto deviceSessionInfo = deviceSessionCtx.getSessionInfo();
@@ -26,7 +26,9 @@ import org.thingsboard.server.common.data.DeviceProfile; @@ -26,7 +26,9 @@ import org.thingsboard.server.common.data.DeviceProfile;
26 import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; 26 import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
27 import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration; 27 import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration;
28 import org.thingsboard.server.common.data.id.DeviceId; 28 import org.thingsboard.server.common.data.id.DeviceId;
  29 +import org.thingsboard.server.common.data.rpc.RpcStatus;
29 import org.thingsboard.server.common.transport.SessionMsgListener; 30 import org.thingsboard.server.common.transport.SessionMsgListener;
  31 +import org.thingsboard.server.common.transport.TransportServiceCallback;
30 import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; 32 import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
31 import org.thingsboard.server.gen.transport.TransportProtos; 33 import org.thingsboard.server.gen.transport.TransportProtos;
32 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; 34 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
@@ -139,6 +141,21 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S @@ -139,6 +141,21 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S
139 @Override 141 @Override
140 public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) { 142 public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
141 snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest); 143 snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest);
  144 + if (toDeviceRequest.getPersisted()) {
  145 + RpcStatus status;
  146 + if (toDeviceRequest.getOneway()) {
  147 + status = RpcStatus.SUCCESSFUL;
  148 + } else {
  149 + status = RpcStatus.DELIVERED;
  150 + }
  151 + TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder()
  152 + .setRequestId(toDeviceRequest.getRequestId())
  153 + .setRequestIdLSB(toDeviceRequest.getRequestIdLSB())
  154 + .setRequestIdMSB(toDeviceRequest.getRequestIdMSB())
  155 + .setStatus(status.name())
  156 + .build();
  157 + snmpTransportContext.getTransportService().process(getSessionInfo(), responseMsg, TransportServiceCallback.EMPTY);
  158 + }
142 } 159 }
143 160
144 @Override 161 @Override
@@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.DeviceTransportType;
20 import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; 20 import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse;
21 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; 21 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
22 import org.thingsboard.server.common.transport.service.SessionMetaData; 22 import org.thingsboard.server.common.transport.service.SessionMetaData;
  23 +import org.thingsboard.server.gen.transport.TransportProtos;
23 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; 24 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
24 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; 25 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
25 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; 26 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
@@ -109,6 +110,8 @@ public interface TransportService { @@ -109,6 +110,8 @@ public interface TransportService {
109 110
110 void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback); 111 void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback);
111 112
  113 + void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDevicePersistedRpcResponseMsg msg, TransportServiceCallback<Void> callback);
  114 +
112 void process(SessionInfoProto sessionInfo, SubscriptionInfoProto msg, TransportServiceCallback<Void> callback); 115 void process(SessionInfoProto sessionInfo, SubscriptionInfoProto msg, TransportServiceCallback<Void> callback);
113 116
114 void process(SessionInfoProto sessionInfo, ClaimDeviceMsg msg, TransportServiceCallback<Void> callback); 117 void process(SessionInfoProto sessionInfo, ClaimDeviceMsg msg, TransportServiceCallback<Void> callback);
@@ -557,6 +557,15 @@ public class DefaultTransportService implements TransportService { @@ -557,6 +557,15 @@ public class DefaultTransportService implements TransportService {
557 } 557 }
558 } 558 }
559 559
  560 + @Override
  561 + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDevicePersistedRpcResponseMsg msg, TransportServiceCallback<Void> callback) {
  562 + if (checkLimits(sessionInfo, msg, callback)) {
  563 + reportActivityInternal(sessionInfo);
  564 + sendToDeviceActor(sessionInfo, TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPersistedRpcResponseMsg(msg).build(),
  565 + new ApiStatsProxyCallback<>(getTenantId(sessionInfo), getCustomerId(sessionInfo), 1, callback));
  566 + }
  567 + }
  568 +
560 private void processTimeout(String requestId) { 569 private void processTimeout(String requestId) {
561 RpcRequestMetadata data = toServerRpcPendingMap.remove(requestId); 570 RpcRequestMetadata data = toServerRpcPendingMap.remove(requestId);
562 if (data != null) { 571 if (data != null) {
@@ -507,6 +507,17 @@ public class ModelConstants { @@ -507,6 +507,17 @@ public class ModelConstants {
507 public static final String OTA_PACKAGE_ADDITIONAL_INFO_COLUMN = ADDITIONAL_INFO_PROPERTY; 507 public static final String OTA_PACKAGE_ADDITIONAL_INFO_COLUMN = ADDITIONAL_INFO_PROPERTY;
508 508
509 /** 509 /**
  510 + * Persisted RPC constants.
  511 + */
  512 + public static final String RPC_TABLE_NAME = "rpc";
  513 + public static final String RPC_TENANT_ID_COLUMN = TENANT_ID_COLUMN;
  514 + public static final String RPC_DEVICE_ID = "device_id";
  515 + public static final String RPC_EXPIRATION_TIME = "expiration_time";
  516 + public static final String RPC_REQUEST = "request";
  517 + public static final String RPC_RESPONSE = "response";
  518 + public static final String RPC_STATUS = "status";
  519 +
  520 + /**
510 * Edge constants. 521 * Edge constants.
511 */ 522 */
512 public static final String EDGE_COLUMN_FAMILY_NAME = "edge"; 523 public static final String EDGE_COLUMN_FAMILY_NAME = "edge";
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.sql;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import lombok.Data;
  20 +import lombok.EqualsAndHashCode;
  21 +import org.hibernate.annotations.Type;
  22 +import org.hibernate.annotations.TypeDef;
  23 +import org.thingsboard.server.common.data.id.DeviceId;
  24 +import org.thingsboard.server.common.data.id.RpcId;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.rpc.Rpc;
  27 +import org.thingsboard.server.common.data.rpc.RpcStatus;
  28 +import org.thingsboard.server.dao.model.BaseEntity;
  29 +import org.thingsboard.server.dao.model.BaseSqlEntity;
  30 +import org.thingsboard.server.dao.util.mapping.JsonStringType;
  31 +
  32 +import javax.persistence.Column;
  33 +import javax.persistence.Entity;
  34 +import javax.persistence.EnumType;
  35 +import javax.persistence.Enumerated;
  36 +import javax.persistence.Table;
  37 +import java.util.UUID;
  38 +
  39 +import static org.thingsboard.server.dao.model.ModelConstants.RPC_DEVICE_ID;
  40 +import static org.thingsboard.server.dao.model.ModelConstants.RPC_EXPIRATION_TIME;
  41 +import static org.thingsboard.server.dao.model.ModelConstants.RPC_REQUEST;
  42 +import static org.thingsboard.server.dao.model.ModelConstants.RPC_RESPONSE;
  43 +import static org.thingsboard.server.dao.model.ModelConstants.RPC_STATUS;
  44 +import static org.thingsboard.server.dao.model.ModelConstants.RPC_TABLE_NAME;
  45 +import static org.thingsboard.server.dao.model.ModelConstants.RPC_TENANT_ID_COLUMN;
  46 +
  47 +@Data
  48 +@EqualsAndHashCode(callSuper = true)
  49 +@Entity
  50 +@TypeDef(name = "json", typeClass = JsonStringType.class)
  51 +@Table(name = RPC_TABLE_NAME)
  52 +public class RpcEntity extends BaseSqlEntity<Rpc> implements BaseEntity<Rpc> {
  53 +
  54 + @Column(name = RPC_TENANT_ID_COLUMN)
  55 + private UUID tenantId;
  56 +
  57 + @Column(name = RPC_DEVICE_ID)
  58 + private UUID deviceId;
  59 +
  60 + @Column(name = RPC_EXPIRATION_TIME)
  61 + private long expirationTime;
  62 +
  63 + @Type(type = "json")
  64 + @Column(name = RPC_REQUEST)
  65 + private JsonNode request;
  66 +
  67 + @Type(type = "json")
  68 + @Column(name = RPC_RESPONSE)
  69 + private JsonNode response;
  70 +
  71 + @Enumerated(EnumType.STRING)
  72 + @Column(name = RPC_STATUS)
  73 + private RpcStatus status;
  74 +
  75 + public RpcEntity() {
  76 + super();
  77 + }
  78 +
  79 + public RpcEntity(Rpc rpc) {
  80 + this.setUuid(rpc.getUuidId());
  81 + this.createdTime = rpc.getCreatedTime();
  82 + this.tenantId = rpc.getTenantId().getId();
  83 + this.deviceId = rpc.getDeviceId().getId();
  84 + this.expirationTime = rpc.getExpirationTime();
  85 + this.request = rpc.getRequest();
  86 + this.response = rpc.getResponse();
  87 + this.status = rpc.getStatus();
  88 + }
  89 +
  90 + @Override
  91 + public Rpc toData() {
  92 + Rpc rpc = new Rpc(new RpcId(id));
  93 + rpc.setCreatedTime(createdTime);
  94 + rpc.setTenantId(new TenantId(tenantId));
  95 + rpc.setDeviceId(new DeviceId(deviceId));
  96 + rpc.setExpirationTime(expirationTime);
  97 + rpc.setRequest(request);
  98 + rpc.setResponse(response);
  99 + rpc.setStatus(status);
  100 + return rpc;
  101 + }
  102 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.rpc;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import lombok.RequiredArgsConstructor;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.stereotype.Service;
  22 +import org.thingsboard.server.common.data.id.DeviceId;
  23 +import org.thingsboard.server.common.data.id.RpcId;
  24 +import org.thingsboard.server.common.data.id.TenantId;
  25 +import org.thingsboard.server.common.data.page.PageData;
  26 +import org.thingsboard.server.common.data.page.PageLink;
  27 +import org.thingsboard.server.common.data.rpc.Rpc;
  28 +import org.thingsboard.server.common.data.rpc.RpcStatus;
  29 +import org.thingsboard.server.dao.service.PaginatedRemover;
  30 +
  31 +import static org.thingsboard.server.dao.service.Validator.validateId;
  32 +import static org.thingsboard.server.dao.service.Validator.validatePageLink;
  33 +
  34 +@Service
  35 +@Slf4j
  36 +@RequiredArgsConstructor
  37 +public class BaseRpcService implements RpcService {
  38 + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
  39 + public static final String INCORRECT_RPC_ID = "Incorrect rpcId ";
  40 +
  41 + private final RpcDao rpcDao;
  42 +
  43 + @Override
  44 + public Rpc save(Rpc rpc) {
  45 + log.trace("Executing save, [{}]", rpc);
  46 + return rpcDao.save(rpc.getTenantId(), rpc);
  47 + }
  48 +
  49 + @Override
  50 + public void deleteRpc(TenantId tenantId, RpcId rpcId) {
  51 + log.trace("Executing deleteRpc, tenantId [{}], rpcId [{}]", tenantId, rpcId);
  52 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  53 + validateId(rpcId, INCORRECT_RPC_ID + rpcId);
  54 + rpcDao.removeById(tenantId, rpcId.getId());
  55 + }
  56 +
  57 + @Override
  58 + public void deleteAllRpcByTenantId(TenantId tenantId) {
  59 + log.trace("Executing deleteAllRpcByTenantId, tenantId [{}]", tenantId);
  60 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  61 + tenantRpcRemover.removeEntities(tenantId, tenantId);
  62 + }
  63 +
  64 + @Override
  65 + public Rpc findById(TenantId tenantId, RpcId rpcId) {
  66 + log.trace("Executing findById, tenantId [{}], rpcId [{}]", tenantId, rpcId);
  67 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  68 + validateId(rpcId, INCORRECT_RPC_ID + rpcId);
  69 + return rpcDao.findById(tenantId, rpcId.getId());
  70 + }
  71 +
  72 + @Override
  73 + public ListenableFuture<Rpc> findRpcByIdAsync(TenantId tenantId, RpcId rpcId) {
  74 + log.trace("Executing findRpcByIdAsync, tenantId [{}], rpcId: [{}]", tenantId, rpcId);
  75 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  76 + validateId(rpcId, INCORRECT_RPC_ID + rpcId);
  77 + return rpcDao.findByIdAsync(tenantId, rpcId.getId());
  78 + }
  79 +
  80 + @Override
  81 + public PageData<Rpc> findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) {
  82 + log.trace("Executing findAllByDeviceIdAndStatus, tenantId [{}], deviceId [{}], rpcStatus [{}], pageLink [{}]", tenantId, deviceId, rpcStatus, pageLink);
  83 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  84 + validatePageLink(pageLink);
  85 + return rpcDao.findAllByDeviceId(tenantId, deviceId, rpcStatus, pageLink);
  86 + }
  87 +
  88 + private PaginatedRemover<TenantId, Rpc> tenantRpcRemover =
  89 + new PaginatedRemover<>() {
  90 + @Override
  91 + protected PageData<Rpc> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) {
  92 + return rpcDao.findAllRpcByTenantId(id, pageLink);
  93 + }
  94 +
  95 + @Override
  96 + protected void removeEntity(TenantId tenantId, Rpc entity) {
  97 + deleteRpc(tenantId, entity.getId());
  98 + }
  99 + };
  100 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.rpc;
  17 +
  18 +import org.thingsboard.server.common.data.id.DeviceId;
  19 +import org.thingsboard.server.common.data.id.TenantId;
  20 +import org.thingsboard.server.common.data.page.PageData;
  21 +import org.thingsboard.server.common.data.page.PageLink;
  22 +import org.thingsboard.server.common.data.rpc.Rpc;
  23 +import org.thingsboard.server.common.data.rpc.RpcStatus;
  24 +import org.thingsboard.server.dao.Dao;
  25 +
  26 +public interface RpcDao extends Dao<Rpc> {
  27 + PageData<Rpc> findAllByDeviceId(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink);
  28 +
  29 + PageData<Rpc> findAllRpcByTenantId(TenantId tenantId, PageLink pageLink);
  30 +
  31 + Long deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime);
  32 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sql.rpc;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.data.repository.CrudRepository;
  21 +import org.springframework.stereotype.Component;
  22 +import org.thingsboard.server.common.data.id.DeviceId;
  23 +import org.thingsboard.server.common.data.id.TenantId;
  24 +import org.thingsboard.server.common.data.page.PageData;
  25 +import org.thingsboard.server.common.data.page.PageLink;
  26 +import org.thingsboard.server.common.data.rpc.Rpc;
  27 +import org.thingsboard.server.common.data.rpc.RpcStatus;
  28 +import org.thingsboard.server.dao.DaoUtil;
  29 +import org.thingsboard.server.dao.model.sql.RpcEntity;
  30 +import org.thingsboard.server.dao.rpc.RpcDao;
  31 +import org.thingsboard.server.dao.sql.JpaAbstractDao;
  32 +
  33 +import java.util.UUID;
  34 +
  35 +@Slf4j
  36 +@Component
  37 +@AllArgsConstructor
  38 +public class JpaRpcDao extends JpaAbstractDao<RpcEntity, Rpc> implements RpcDao {
  39 +
  40 + private final RpcRepository rpcRepository;
  41 +
  42 + @Override
  43 + protected Class<RpcEntity> getEntityClass() {
  44 + return RpcEntity.class;
  45 + }
  46 +
  47 + @Override
  48 + protected CrudRepository<RpcEntity, UUID> getCrudRepository() {
  49 + return rpcRepository;
  50 + }
  51 +
  52 + @Override
  53 + public PageData<Rpc> findAllByDeviceId(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) {
  54 + return DaoUtil.toPageData(rpcRepository.findAllByTenantIdAndDeviceIdAndStatus(tenantId.getId(), deviceId.getId(), rpcStatus, DaoUtil.toPageable(pageLink)));
  55 + }
  56 +
  57 + @Override
  58 + public PageData<Rpc> findAllRpcByTenantId(TenantId tenantId, PageLink pageLink) {
  59 + return DaoUtil.toPageData(rpcRepository.findAllByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink)));
  60 + }
  61 +
  62 + @Override
  63 + public Long deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime) {
  64 + return rpcRepository.deleteOutdatedRpcByTenantId(tenantId.getId(), expirationTime);
  65 + }
  66 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sql.rpc;
  17 +
  18 +import org.springframework.data.domain.Page;
  19 +import org.springframework.data.domain.Pageable;
  20 +import org.springframework.data.jpa.repository.Query;
  21 +import org.springframework.data.repository.CrudRepository;
  22 +import org.springframework.data.repository.query.Param;
  23 +import org.thingsboard.server.common.data.rpc.RpcStatus;
  24 +import org.thingsboard.server.dao.model.sql.RpcEntity;
  25 +
  26 +import java.util.UUID;
  27 +
  28 +public interface RpcRepository extends CrudRepository<RpcEntity, UUID> {
  29 + Page<RpcEntity> findAllByTenantIdAndDeviceIdAndStatus(UUID tenantId, UUID deviceId, RpcStatus status, Pageable pageable);
  30 +
  31 + Page<RpcEntity> findAllByTenantId(UUID tenantId, Pageable pageable);
  32 +
  33 + @Query(value = "WITH deleted AS (DELETE FROM rpc WHERE (tenant_id = :tenantId AND created_time < :expirationTime) IS TRUE RETURNING *) SELECT count(*) FROM deleted",
  34 + nativeQuery = true)
  35 + Long deleteOutdatedRpcByTenantId(@Param("tenantId") UUID tenantId, @Param("expirationTime") Long expirationTime);
  36 +}
@@ -37,6 +37,7 @@ import org.thingsboard.server.dao.entityview.EntityViewService; @@ -37,6 +37,7 @@ import org.thingsboard.server.dao.entityview.EntityViewService;
37 import org.thingsboard.server.dao.exception.DataValidationException; 37 import org.thingsboard.server.dao.exception.DataValidationException;
38 import org.thingsboard.server.dao.ota.OtaPackageService; 38 import org.thingsboard.server.dao.ota.OtaPackageService;
39 import org.thingsboard.server.dao.resource.ResourceService; 39 import org.thingsboard.server.dao.resource.ResourceService;
  40 +import org.thingsboard.server.dao.rpc.RpcService;
40 import org.thingsboard.server.dao.rule.RuleChainService; 41 import org.thingsboard.server.dao.rule.RuleChainService;
41 import org.thingsboard.server.dao.service.DataValidator; 42 import org.thingsboard.server.dao.service.DataValidator;
42 import org.thingsboard.server.dao.service.PaginatedRemover; 43 import org.thingsboard.server.dao.service.PaginatedRemover;
@@ -96,6 +97,9 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe @@ -96,6 +97,9 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
96 @Autowired 97 @Autowired
97 private OtaPackageService otaPackageService; 98 private OtaPackageService otaPackageService;
98 99
  100 + @Autowired
  101 + private RpcService rpcService;
  102 +
99 @Override 103 @Override
100 public Tenant findTenantById(TenantId tenantId) { 104 public Tenant findTenantById(TenantId tenantId) {
101 log.trace("Executing findTenantById [{}]", tenantId); 105 log.trace("Executing findTenantById [{}]", tenantId);
@@ -151,6 +155,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe @@ -151,6 +155,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
151 apiUsageStateService.deleteApiUsageStateByTenantId(tenantId); 155 apiUsageStateService.deleteApiUsageStateByTenantId(tenantId);
152 resourceService.deleteResourcesByTenantId(tenantId); 156 resourceService.deleteResourcesByTenantId(tenantId);
153 otaPackageService.deleteOtaPackagesByTenantId(tenantId); 157 otaPackageService.deleteOtaPackagesByTenantId(tenantId);
  158 + rpcService.deleteAllRpcByTenantId(tenantId);
154 tenantDao.removeById(tenantId, tenantId.getId()); 159 tenantDao.removeById(tenantId, tenantId.getId());
155 deleteEntityRelations(tenantId, tenantId); 160 deleteEntityRelations(tenantId, tenantId);
156 } 161 }
@@ -567,3 +567,14 @@ CREATE TABLE IF NOT EXISTS edge_event ( @@ -567,3 +567,14 @@ CREATE TABLE IF NOT EXISTS edge_event (
567 tenant_id uuid, 567 tenant_id uuid,
568 ts bigint NOT NULL 568 ts bigint NOT NULL
569 ); 569 );
  570 +
  571 +CREATE TABLE IF NOT EXISTS rpc (
  572 + id uuid NOT NULL CONSTRAINT rpc_pkey PRIMARY KEY,
  573 + created_time bigint NOT NULL,
  574 + tenant_id uuid NOT NULL,
  575 + device_id uuid NOT NULL,
  576 + expiration_time bigint NOT NULL,
  577 + request varchar(10000000) NOT NULL,
  578 + response varchar(10000000),
  579 + status varchar(255) NOT NULL
  580 +);
@@ -46,3 +46,4 @@ CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribu @@ -46,3 +46,4 @@ CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribu
46 46
47 CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time); 47 CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time);
48 48
  49 +CREATE INDEX IF NOT EXISTS idx_rpc_tenant_id_device_id ON rpc(tenant_id, device_id);
@@ -605,6 +605,17 @@ CREATE TABLE IF NOT EXISTS edge_event ( @@ -605,6 +605,17 @@ CREATE TABLE IF NOT EXISTS edge_event (
605 ts bigint NOT NULL 605 ts bigint NOT NULL
606 ); 606 );
607 607
  608 +CREATE TABLE IF NOT EXISTS rpc (
  609 + id uuid NOT NULL CONSTRAINT rpc_pkey PRIMARY KEY,
  610 + created_time bigint NOT NULL,
  611 + tenant_id uuid NOT NULL,
  612 + device_id uuid NOT NULL,
  613 + expiration_time bigint NOT NULL,
  614 + request varchar(10000000) NOT NULL,
  615 + response varchar(10000000),
  616 + status varchar(255) NOT NULL
  617 +);
  618 +
608 CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) 619 CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint)
609 LANGUAGE plpgsql AS 620 LANGUAGE plpgsql AS
610 $$ 621 $$
@@ -36,4 +36,5 @@ DROP TABLE IF EXISTS resource; @@ -36,4 +36,5 @@ DROP TABLE IF EXISTS resource;
36 DROP TABLE IF EXISTS ota_package; 36 DROP TABLE IF EXISTS ota_package;
37 DROP TABLE IF EXISTS edge; 37 DROP TABLE IF EXISTS edge;
38 DROP TABLE IF EXISTS edge_event; 38 DROP TABLE IF EXISTS edge_event;
  39 +DROP TABLE IF EXISTS rpc;
39 DROP FUNCTION IF EXISTS to_uuid; 40 DROP FUNCTION IF EXISTS to_uuid;
@@ -37,3 +37,4 @@ DROP TABLE IF EXISTS resource; @@ -37,3 +37,4 @@ DROP TABLE IF EXISTS resource;
37 DROP TABLE IF EXISTS firmware; 37 DROP TABLE IF EXISTS firmware;
38 DROP TABLE IF EXISTS edge; 38 DROP TABLE IF EXISTS edge;
39 DROP TABLE IF EXISTS edge_event; 39 DROP TABLE IF EXISTS edge_event;
  40 +DROP TABLE IF EXISTS rpc;
@@ -35,6 +35,7 @@ public final class RuleEngineDeviceRpcRequest { @@ -35,6 +35,7 @@ public final class RuleEngineDeviceRpcRequest {
35 private final UUID requestUUID; 35 private final UUID requestUUID;
36 private final String originServiceId; 36 private final String originServiceId;
37 private final boolean oneway; 37 private final boolean oneway;
  38 + private final boolean persisted;
38 private final String method; 39 private final String method;
39 private final String body; 40 private final String body;
40 private final long expirationTime; 41 private final long expirationTime;
@@ -33,8 +33,8 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; @@ -33,8 +33,8 @@ import org.thingsboard.server.common.msg.session.SessionMsgType;
33 type = ComponentType.FILTER, 33 type = ComponentType.FILTER,
34 name = "message type switch", 34 name = "message type switch",
35 configClazz = EmptyNodeConfiguration.class, 35 configClazz = EmptyNodeConfiguration.class,
36 - relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "Activity Event", "Inactivity Event",  
37 - "Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned", 36 + relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "RPC Queued", "RPC Delivered", "RPC Successful", "RPC Timeout", "RPC Failed",
  37 + "Activity Event", "Inactivity Event", "Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned",
38 "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other", "Entity Assigned From Tenant", "Entity Assigned To Tenant", 38 "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other", "Entity Assigned From Tenant", "Entity Assigned To Tenant",
39 "Timeseries Updated", "Timeseries Deleted"}, 39 "Timeseries Updated", "Timeseries Deleted"},
40 nodeDescription = "Route incoming messages by Message Type", 40 nodeDescription = "Route incoming messages by Message Type",
@@ -95,6 +95,16 @@ public class TbMsgTypeSwitchNode implements TbNode { @@ -95,6 +95,16 @@ public class TbMsgTypeSwitchNode implements TbNode {
95 relationType = "Timeseries Updated"; 95 relationType = "Timeseries Updated";
96 } else if (msg.getType().equals(DataConstants.TIMESERIES_DELETED)) { 96 } else if (msg.getType().equals(DataConstants.TIMESERIES_DELETED)) {
97 relationType = "Timeseries Deleted"; 97 relationType = "Timeseries Deleted";
  98 + } else if (msg.getType().equals(DataConstants.RPC_QUEUED)) {
  99 + relationType = "RPC Queued";
  100 + } else if (msg.getType().equals(DataConstants.RPC_DELIVERED)) {
  101 + relationType = "RPC Delivered";
  102 + } else if (msg.getType().equals(DataConstants.RPC_SUCCESSFUL)) {
  103 + relationType = "RPC Successful";
  104 + } else if (msg.getType().equals(DataConstants.RPC_TIMEOUT)) {
  105 + relationType = "RPC Timeout";
  106 + } else if (msg.getType().equals(DataConstants.RPC_FAILED)) {
  107 + relationType = "RPC Failed";
98 } else { 108 } else {
99 relationType = "Other"; 109 relationType = "Other";
100 } 110 }
@@ -81,6 +81,9 @@ public class TbSendRPCRequestNode implements TbNode { @@ -81,6 +81,9 @@ public class TbSendRPCRequestNode implements TbNode {
81 tmp = msg.getMetaData().getValue("oneway"); 81 tmp = msg.getMetaData().getValue("oneway");
82 boolean oneway = !StringUtils.isEmpty(tmp) && Boolean.parseBoolean(tmp); 82 boolean oneway = !StringUtils.isEmpty(tmp) && Boolean.parseBoolean(tmp);
83 83
  84 + tmp = msg.getMetaData().getValue("persisted");
  85 + boolean persisted = !StringUtils.isEmpty(tmp) && Boolean.parseBoolean(tmp);
  86 +
84 tmp = msg.getMetaData().getValue("requestUUID"); 87 tmp = msg.getMetaData().getValue("requestUUID");
85 UUID requestUUID = !StringUtils.isEmpty(tmp) ? UUID.fromString(tmp) : Uuids.timeBased(); 88 UUID requestUUID = !StringUtils.isEmpty(tmp) ? UUID.fromString(tmp) : Uuids.timeBased();
86 tmp = msg.getMetaData().getValue("originServiceId"); 89 tmp = msg.getMetaData().getValue("originServiceId");
@@ -108,6 +111,7 @@ public class TbSendRPCRequestNode implements TbNode { @@ -108,6 +111,7 @@ public class TbSendRPCRequestNode implements TbNode {
108 .originServiceId(originServiceId) 111 .originServiceId(originServiceId)
109 .expirationTime(expirationTime) 112 .expirationTime(expirationTime)
110 .restApiCall(restApiCall) 113 .restApiCall(restApiCall)
  114 + .persisted(persisted)
111 .build(); 115 .build();
112 116
113 ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> { 117 ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> {
@@ -160,9 +160,9 @@ @@ -160,9 +160,9 @@
160 <!-- <div fxLayout="column">--> 160 <!-- <div fxLayout="column">-->
161 <!-- <mat-form-field class="mat-block">--> 161 <!-- <mat-form-field class="mat-block">-->
162 <!-- <mat-label>{{ 'device-profile.lwm2m.client-strategy-label' | translate }}</mat-label>--> 162 <!-- <mat-label>{{ 'device-profile.lwm2m.client-strategy-label' | translate }}</mat-label>-->
163 -<!-- <mat-select formControlName="clientStrategy"--> 163 +<!-- <mat-select formControlName="clientOnlyObserveAfterConnect"-->
164 <!-- matTooltip="{{ 'device-profile.lwm2m.client-strategy-tip' | translate:--> 164 <!-- matTooltip="{{ 'device-profile.lwm2m.client-strategy-tip' | translate:-->
165 -<!-- { count: +lwm2mDeviceProfileFormGroup.get('clientStrategy').value } }}"--> 165 +<!-- { count: +lwm2mDeviceProfileFormGroup.get('clientOnlyObserveAfterConnect').value } }}"-->
166 <!-- matTooltipPosition="above">--> 166 <!-- matTooltipPosition="above">-->
167 <!-- <mat-option value=1>{{ 'device-profile.lwm2m.client-strategy-connect' | translate:--> 167 <!-- <mat-option value=1>{{ 'device-profile.lwm2m.client-strategy-connect' | translate:-->
168 <!-- {count: 1} }}</mat-option>--> 168 <!-- {count: 1} }}</mat-option>-->
@@ -97,7 +97,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -97,7 +97,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
97 binding: [], 97 binding: [],
98 bootstrapServer: [null, Validators.required], 98 bootstrapServer: [null, Validators.required],
99 lwm2mServer: [null, Validators.required], 99 lwm2mServer: [null, Validators.required],
100 - clientStrategy: [1, []], 100 + clientOnlyObserveAfterConnect: [1, []],
101 fwUpdateStrategy: [1, []], 101 fwUpdateStrategy: [1, []],
102 swUpdateStrategy: [1, []], 102 swUpdateStrategy: [1, []],
103 fwUpdateRecourse: [{value: '', disabled: true}, []], 103 fwUpdateRecourse: [{value: '', disabled: true}, []],
@@ -216,7 +216,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -216,7 +216,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
216 binding: this.configurationValue.bootstrap.servers.binding, 216 binding: this.configurationValue.bootstrap.servers.binding,
217 bootstrapServer: this.configurationValue.bootstrap.bootstrapServer, 217 bootstrapServer: this.configurationValue.bootstrap.bootstrapServer,
218 lwm2mServer: this.configurationValue.bootstrap.lwm2mServer, 218 lwm2mServer: this.configurationValue.bootstrap.lwm2mServer,
219 - clientStrategy: this.configurationValue.clientLwM2mSettings.clientStrategy, 219 + clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect,
220 fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1, 220 fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1,
221 swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1, 221 swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1,
222 fwUpdateRecourse: fwResource, 222 fwUpdateRecourse: fwResource,
@@ -257,7 +257,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -257,7 +257,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
257 bootstrapServers.defaultMinPeriod = config.defaultMinPeriod; 257 bootstrapServers.defaultMinPeriod = config.defaultMinPeriod;
258 bootstrapServers.notifIfDisabled = config.notifIfDisabled; 258 bootstrapServers.notifIfDisabled = config.notifIfDisabled;
259 bootstrapServers.binding = config.binding; 259 bootstrapServers.binding = config.binding;
260 - this.configurationValue.clientLwM2mSettings.clientStrategy = config.clientStrategy; 260 + this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect = config.clientOnlyObserveAfterConnect;
261 this.configurationValue.clientLwM2mSettings.fwUpdateStrategy = config.fwUpdateStrategy; 261 this.configurationValue.clientLwM2mSettings.fwUpdateStrategy = config.fwUpdateStrategy;
262 this.configurationValue.clientLwM2mSettings.swUpdateStrategy = config.swUpdateStrategy; 262 this.configurationValue.clientLwM2mSettings.swUpdateStrategy = config.swUpdateStrategy;
263 this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = config.fwUpdateRecourse; 263 this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = config.fwUpdateRecourse;
@@ -168,7 +168,7 @@ export interface Lwm2mProfileConfigModels { @@ -168,7 +168,7 @@ export interface Lwm2mProfileConfigModels {
168 } 168 }
169 169
170 export interface ClientLwM2mSettings { 170 export interface ClientLwM2mSettings {
171 - clientStrategy: string; 171 + clientOnlyObserveAfterConnect: number;
172 fwUpdateStrategy: number; 172 fwUpdateStrategy: number;
173 swUpdateStrategy: number; 173 swUpdateStrategy: number;
174 fwUpdateRecourse: string; 174 fwUpdateRecourse: string;
@@ -240,7 +240,7 @@ export function getDefaultProfileConfig(hostname?: any): Lwm2mProfileConfigModel @@ -240,7 +240,7 @@ export function getDefaultProfileConfig(hostname?: any): Lwm2mProfileConfigModel
240 240
241 function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings { 241 function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings {
242 return { 242 return {
243 - clientStrategy: '1', 243 + clientOnlyObserveAfterConnect: 1,
244 fwUpdateStrategy: 1, 244 fwUpdateStrategy: 1,
245 swUpdateStrategy: 1, 245 swUpdateStrategy: 1,
246 fwUpdateRecourse: DEFAULT_FW_UPDATE_RESOURCE, 246 fwUpdateRecourse: DEFAULT_FW_UPDATE_RESOURCE,
@@ -197,6 +197,18 @@ @@ -197,6 +197,18 @@
197 </mat-error> 197 </mat-error>
198 </mat-form-field> 198 </mat-form-field>
199 <mat-form-field class="mat-block"> 199 <mat-form-field class="mat-block">
  200 + <mat-label translate>tenant-profile.rpc-ttl-days</mat-label>
  201 + <input matInput required min="0" step="1"
  202 + formControlName="rpcTtlDays"
  203 + type="number">
  204 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('rpcTtlDays').hasError('required')">
  205 + {{ 'tenant-profile.rpc-ttl-days-required' | translate}}
  206 + </mat-error>
  207 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('rpcTtlDays').hasError('min')">
  208 + {{ 'tenant-profile.rpc-ttl-days-days-range' | translate}}
  209 + </mat-error>
  210 + </mat-form-field>
  211 + <mat-form-field class="mat-block">
200 <mat-label translate>tenant-profile.max-rule-node-executions-per-message</mat-label> 212 <mat-label translate>tenant-profile.max-rule-node-executions-per-message</mat-label>
201 <input matInput required min="0" step="1" 213 <input matInput required min="0" step="1"
202 formControlName="maxRuleNodeExecutionsPerMessage" 214 formControlName="maxRuleNodeExecutionsPerMessage"
@@ -77,7 +77,8 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA @@ -77,7 +77,8 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA
77 maxSms: [null, [Validators.required, Validators.min(0)]], 77 maxSms: [null, [Validators.required, Validators.min(0)]],
78 maxCreatedAlarms: [null, [Validators.required, Validators.min(0)]], 78 maxCreatedAlarms: [null, [Validators.required, Validators.min(0)]],
79 defaultStorageTtlDays: [null, [Validators.required, Validators.min(0)]], 79 defaultStorageTtlDays: [null, [Validators.required, Validators.min(0)]],
80 - alarmsTtlDays: [null, [Validators.required, Validators.min(0)]] 80 + alarmsTtlDays: [null, [Validators.required, Validators.min(0)]],
  81 + rpcTtlDays: [null, [Validators.required, Validators.min(0)]]
81 }); 82 });
82 this.defaultTenantProfileConfigurationFormGroup.valueChanges.subscribe(() => { 83 this.defaultTenantProfileConfigurationFormGroup.valueChanges.subscribe(() => {
83 this.updateModel(); 84 this.updateModel();
@@ -352,7 +352,12 @@ export enum MessageType { @@ -352,7 +352,12 @@ export enum MessageType {
352 ATTRIBUTES_UPDATED = 'ATTRIBUTES_UPDATED', 352 ATTRIBUTES_UPDATED = 'ATTRIBUTES_UPDATED',
353 ATTRIBUTES_DELETED = 'ATTRIBUTES_DELETED', 353 ATTRIBUTES_DELETED = 'ATTRIBUTES_DELETED',
354 TIMESERIES_UPDATED = 'TIMESERIES_UPDATED', 354 TIMESERIES_UPDATED = 'TIMESERIES_UPDATED',
355 - TIMESERIES_DELETED = 'TIMESERIES_DELETED' 355 + TIMESERIES_DELETED = 'TIMESERIES_DELETED',
  356 + RPC_QUEUED = 'RPC_QUEUED',
  357 + RPC_DELIVERED = 'RPC_DELIVERED',
  358 + RPC_SUCCESSFUL = 'RPC_SUCCESSFUL',
  359 + RPC_TIMEOUT = 'RPC_TIMEOUT',
  360 + RPC_FAILED = 'RPC_FAILED'
356 } 361 }
357 362
358 export const messageTypeNames = new Map<MessageType, string>( 363 export const messageTypeNames = new Map<MessageType, string>(
@@ -373,7 +378,12 @@ export const messageTypeNames = new Map<MessageType, string>( @@ -373,7 +378,12 @@ export const messageTypeNames = new Map<MessageType, string>(
373 [MessageType.ATTRIBUTES_UPDATED, 'Attributes Updated'], 378 [MessageType.ATTRIBUTES_UPDATED, 'Attributes Updated'],
374 [MessageType.ATTRIBUTES_DELETED, 'Attributes Deleted'], 379 [MessageType.ATTRIBUTES_DELETED, 'Attributes Deleted'],
375 [MessageType.TIMESERIES_UPDATED, 'Timeseries Updated'], 380 [MessageType.TIMESERIES_UPDATED, 'Timeseries Updated'],
376 - [MessageType.TIMESERIES_DELETED, 'Timeseries Deleted'] 381 + [MessageType.TIMESERIES_DELETED, 'Timeseries Deleted'],
  382 + [MessageType.RPC_QUEUED, 'RPC Queued'],
  383 + [MessageType.RPC_DELIVERED, 'RPC Delivered'],
  384 + [MessageType.RPC_SUCCESSFUL, 'RPC Successful'],
  385 + [MessageType.RPC_TIMEOUT, 'RPC Timeout'],
  386 + [MessageType.RPC_FAILED, 'RPC Failed']
377 ] 387 ]
378 ); 388 );
379 389
@@ -53,6 +53,7 @@ export interface DefaultTenantProfileConfiguration { @@ -53,6 +53,7 @@ export interface DefaultTenantProfileConfiguration {
53 53
54 defaultStorageTtlDays: number; 54 defaultStorageTtlDays: number;
55 alarmsTtlDays: number; 55 alarmsTtlDays: number;
  56 + rpcTtlDays: number;
56 } 57 }
57 58
58 export type TenantProfileConfigurations = DefaultTenantProfileConfiguration; 59 export type TenantProfileConfigurations = DefaultTenantProfileConfiguration;
@@ -85,7 +86,8 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan @@ -85,7 +86,8 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan
85 maxSms: 0, 86 maxSms: 0,
86 maxCreatedAlarms: 0, 87 maxCreatedAlarms: 0,
87 defaultStorageTtlDays: 0, 88 defaultStorageTtlDays: 0,
88 - alarmsTtlDays: 0 89 + alarmsTtlDays: 0,
  90 + rpcTtlDays: 0
89 }; 91 };
90 configuration = {...defaultConfiguration, type: TenantProfileType.DEFAULT}; 92 configuration = {...defaultConfiguration, type: TenantProfileType.DEFAULT};
91 break; 93 break;
@@ -2638,6 +2638,9 @@ @@ -2638,6 +2638,9 @@
2638 "alarms-ttl-days": "Alarms TTL days (0 - unlimited)", 2638 "alarms-ttl-days": "Alarms TTL days (0 - unlimited)",
2639 "alarms-ttl-days-required": "Alarms TTL days required", 2639 "alarms-ttl-days-required": "Alarms TTL days required",
2640 "alarms-ttl-days-days-range": "Alarms TTL days can't be negative", 2640 "alarms-ttl-days-days-range": "Alarms TTL days can't be negative",
  2641 + "rpc-ttl-days": "RPC TTL days (0 - unlimited)",
  2642 + "rpc-ttl-days-required": "RPC TTL days required",
  2643 + "rpc-ttl-days-days-range": "RPC TTL days can't be negative",
2641 "max-rule-node-executions-per-message": "Maximum number of rule node executions per message (0 - unlimited)", 2644 "max-rule-node-executions-per-message": "Maximum number of rule node executions per message (0 - unlimited)",
2642 "max-rule-node-executions-per-message-required": "Maximum number of rule node executions per message is required.", 2645 "max-rule-node-executions-per-message-required": "Maximum number of rule node executions per message is required.",
2643 "max-rule-node-executions-per-message-range": "Maximum number of rule node executions per message can't be negative", 2646 "max-rule-node-executions-per-message-range": "Maximum number of rule node executions per message can't be negative",