Showing
14 changed files
with
114 additions
and
41 deletions
@@ -400,6 +400,14 @@ public class ActorSystemContext { | @@ -400,6 +400,14 @@ public class ActorSystemContext { | ||
400 | @Getter | 400 | @Getter |
401 | private String debugPerTenantLimitsConfiguration; | 401 | private String debugPerTenantLimitsConfiguration; |
402 | 402 | ||
403 | + @Value("${actors.rpc.sequence.enabled:true}") | ||
404 | + @Getter | ||
405 | + private boolean rpcSequenceEnabled; | ||
406 | + | ||
407 | + @Value("${actors.rpc.persistent.retries:5}") | ||
408 | + @Getter | ||
409 | + private int maxPersistentRpcRetries; | ||
410 | + | ||
403 | @Getter | 411 | @Getter |
404 | @Setter | 412 | @Setter |
405 | private TbActorSystem actorSystem; | 413 | private TbActorSystem actorSystem; |
@@ -121,6 +121,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | @@ -121,6 +121,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | ||
121 | private final Map<UUID, SessionInfo> attributeSubscriptions; | 121 | private final Map<UUID, SessionInfo> attributeSubscriptions; |
122 | private final Map<UUID, SessionInfo> rpcSubscriptions; | 122 | private final Map<UUID, SessionInfo> rpcSubscriptions; |
123 | private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap; | 123 | private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap; |
124 | + private final boolean rpcSequenceEnabled; | ||
124 | 125 | ||
125 | private int rpcSeq = 0; | 126 | private int rpcSeq = 0; |
126 | private String deviceName; | 127 | private String deviceName; |
@@ -132,6 +133,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | @@ -132,6 +133,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | ||
132 | super(systemContext); | 133 | super(systemContext); |
133 | this.tenantId = tenantId; | 134 | this.tenantId = tenantId; |
134 | this.deviceId = deviceId; | 135 | this.deviceId = deviceId; |
136 | + this.rpcSequenceEnabled = systemContext.isRpcSequenceEnabled(); | ||
135 | this.attributeSubscriptions = new HashMap<>(); | 137 | this.attributeSubscriptions = new HashMap<>(); |
136 | this.rpcSubscriptions = new HashMap<>(); | 138 | this.rpcSubscriptions = new HashMap<>(); |
137 | this.toDeviceRpcPendingMap = new LinkedHashMap<>(); | 139 | this.toDeviceRpcPendingMap = new LinkedHashMap<>(); |
@@ -185,19 +187,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | @@ -185,19 +187,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | ||
185 | if (timeout <= 0) { | 187 | if (timeout <= 0) { |
186 | log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime()); | 188 | log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime()); |
187 | if (persisted) { | 189 | if (persisted) { |
188 | - createRpc(request, RpcStatus.TIMEOUT); | 190 | + createRpc(request, RpcStatus.EXPIRED); |
189 | } | 191 | } |
190 | return; | 192 | return; |
191 | } else if (persisted) { | 193 | } else if (persisted) { |
192 | createRpc(request, RpcStatus.QUEUED); | 194 | createRpc(request, RpcStatus.QUEUED); |
193 | } | 195 | } |
194 | 196 | ||
195 | - boolean sent; | 197 | + boolean sent = false; |
196 | if (systemContext.isEdgesEnabled() && edgeId != null) { | 198 | if (systemContext.isEdgesEnabled() && edgeId != null) { |
197 | log.debug("[{}][{}] device is related to edge [{}]. Saving RPC request to edge queue", tenantId, deviceId, edgeId.getId()); | 199 | log.debug("[{}][{}] device is related to edge [{}]. Saving RPC request to edge queue", tenantId, deviceId, edgeId.getId()); |
198 | saveRpcRequestToEdgeQueue(request, rpcRequest.getRequestId()); | 200 | saveRpcRequestToEdgeQueue(request, rpcRequest.getRequestId()); |
199 | sent = true; | 201 | sent = true; |
200 | - } else { | 202 | + } else if (!rpcSequenceEnabled || toDeviceRpcPendingMap.isEmpty()) { |
201 | sent = rpcSubscriptions.size() > 0; | 203 | sent = rpcSubscriptions.size() > 0; |
202 | Set<UUID> syncSessionSet = new HashSet<>(); | 204 | Set<UUID> syncSessionSet = new HashSet<>(); |
203 | rpcSubscriptions.forEach((key, value) -> { | 205 | rpcSubscriptions.forEach((key, value) -> { |
@@ -292,7 +294,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | @@ -292,7 +294,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | ||
292 | if (requestMd != null) { | 294 | if (requestMd != null) { |
293 | log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); | 295 | log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); |
294 | if (requestMd.getMsg().getMsg().isPersisted()) { | 296 | if (requestMd.getMsg().getMsg().isPersisted()) { |
295 | - systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.TIMEOUT, null); | 297 | + systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.EXPIRED, null); |
296 | } | 298 | } |
297 | systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), | 299 | systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), |
298 | null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); | 300 | null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); |
@@ -300,7 +302,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | @@ -300,7 +302,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | ||
300 | } | 302 | } |
301 | } | 303 | } |
302 | 304 | ||
303 | - private void sendPendingRequest(TbActorCtx context, UUID sessionId, String nodeId) { | 305 | + private void sendPendingRequests(TbActorCtx context, UUID sessionId, String nodeId) { |
304 | SessionType sessionType = getSessionType(sessionId); | 306 | SessionType sessionType = getSessionType(sessionId); |
305 | if (!toDeviceRpcPendingMap.isEmpty()) { | 307 | if (!toDeviceRpcPendingMap.isEmpty()) { |
306 | log.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId); | 308 | log.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId); |
@@ -312,11 +314,34 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | @@ -312,11 +314,34 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | ||
312 | log.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId); | 314 | log.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId); |
313 | } | 315 | } |
314 | Set<Integer> sentOneWayIds = new HashSet<>(); | 316 | Set<Integer> sentOneWayIds = new HashSet<>(); |
315 | - toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, nodeId, sentOneWayIds)); | 317 | + |
318 | + if (sessionType == SessionType.ASYNC) { | ||
319 | + if (rpcSequenceEnabled) { | ||
320 | + List<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> entries = new ArrayList<>(); | ||
321 | + for (Map.Entry<Integer, ToDeviceRpcRequestMetadata> entry : toDeviceRpcPendingMap.entrySet()) { | ||
322 | + if (entry.getValue().isDelivered()) { | ||
323 | + continue; | ||
324 | + } | ||
325 | + entries.add(entry); | ||
326 | + if (entry.getValue().getMsg().getMsg().isPersisted() || entry.getValue().getMsg().getMsg().isOneway()) { | ||
327 | + break; | ||
328 | + } | ||
329 | + } | ||
330 | + entries.forEach(processPendingRpc(context, sessionId, nodeId, sentOneWayIds)); | ||
331 | + } else { | ||
332 | + toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, nodeId, sentOneWayIds)); | ||
333 | + } | ||
334 | + } else { | ||
335 | + toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, nodeId, sentOneWayIds)); | ||
336 | + } | ||
337 | + | ||
338 | + sentOneWayIds.stream().filter(id -> !toDeviceRpcPendingMap.get(id).getMsg().getMsg().isPersisted()).forEach(toDeviceRpcPendingMap::remove); | ||
316 | } | 339 | } |
317 | 340 | ||
318 | private void sendNextPendingRequest(TbActorCtx context) { | 341 | private void sendNextPendingRequest(TbActorCtx context) { |
319 | - rpcSubscriptions.forEach((id, s) -> sendPendingRequest(context, id, s.getNodeId())); | 342 | + if (rpcSequenceEnabled) { |
343 | + rpcSubscriptions.forEach((id, s) -> sendPendingRequests(context, id, s.getNodeId())); | ||
344 | + } | ||
320 | } | 345 | } |
321 | 346 | ||
322 | private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(TbActorCtx context, UUID sessionId, String nodeId, Set<Integer> sentOneWayIds) { | 347 | private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(TbActorCtx context, UUID sessionId, String nodeId, Set<Integer> sentOneWayIds) { |
@@ -338,11 +363,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | @@ -338,11 +363,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | ||
338 | .setPersisted(request.isPersisted()) | 363 | .setPersisted(request.isPersisted()) |
339 | .build(); | 364 | .build(); |
340 | sendToTransport(rpcRequest, sessionId, nodeId); | 365 | sendToTransport(rpcRequest, sessionId, nodeId); |
341 | - | ||
342 | - if (SessionType.ASYNC.equals(getSessionType(sessionId)) && request.isOneway() && !request.isPersisted()) { | ||
343 | - toDeviceRpcPendingMap.remove(entry.getKey()); | ||
344 | - sendPendingRequest(context, sessionId, nodeId); | ||
345 | - } | ||
346 | }; | 366 | }; |
347 | } | 367 | } |
348 | 368 | ||
@@ -361,7 +381,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | @@ -361,7 +381,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | ||
361 | processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToRPC()); | 381 | processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToRPC()); |
362 | } | 382 | } |
363 | if (msg.hasSendPendingRPC()) { | 383 | if (msg.hasSendPendingRPC()) { |
364 | - sendPendingRequest(context, getSessionId(sessionInfo), sessionInfo.getNodeId()); | 384 | + sendPendingRequests(context, getSessionId(sessionInfo), sessionInfo.getNodeId()); |
365 | } | 385 | } |
366 | if (msg.hasGetAttributes()) { | 386 | if (msg.hasGetAttributes()) { |
367 | handleGetAttributesRequest(context, sessionInfo, msg.getGetAttributes()); | 387 | handleGetAttributesRequest(context, sessionInfo, msg.getGetAttributes()); |
@@ -559,16 +579,28 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | @@ -559,16 +579,28 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | ||
559 | private void processPersistedRpcResponses(TbActorCtx context, SessionInfoProto sessionInfo, ToDevicePersistedRpcResponseMsg responseMsg) { | 579 | private void processPersistedRpcResponses(TbActorCtx context, SessionInfoProto sessionInfo, ToDevicePersistedRpcResponseMsg responseMsg) { |
560 | UUID rpcId = new UUID(responseMsg.getRequestIdMSB(), responseMsg.getRequestIdLSB()); | 580 | UUID rpcId = new UUID(responseMsg.getRequestIdMSB(), responseMsg.getRequestIdLSB()); |
561 | RpcStatus status = RpcStatus.valueOf(responseMsg.getStatus()); | 581 | RpcStatus status = RpcStatus.valueOf(responseMsg.getStatus()); |
562 | - | ||
563 | - ToDeviceRpcRequestMetadata md; | ||
564 | - if (RpcStatus.DELIVERED.equals(status)) { | ||
565 | - md = toDeviceRpcPendingMap.get(responseMsg.getRequestId()); | ||
566 | - } else { | ||
567 | - md = toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); | ||
568 | - } | 582 | + ToDeviceRpcRequestMetadata md = toDeviceRpcPendingMap.get(responseMsg.getRequestId()); |
569 | 583 | ||
570 | if (md != null) { | 584 | if (md != null) { |
585 | + if (status.equals(RpcStatus.DELIVERED)) { | ||
586 | + if (md.getMsg().getMsg().isOneway()) { | ||
587 | + toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); | ||
588 | + } else { | ||
589 | + md.setDelivered(true); | ||
590 | + } | ||
591 | + } else if (status.equals(RpcStatus.TIMEOUT)) { | ||
592 | + if (systemContext.getMaxPersistentRpcRetries() <= md.getRetries()) { | ||
593 | + toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); | ||
594 | + status = RpcStatus.FAILED; | ||
595 | + } else { | ||
596 | + md.setRetries(md.getRetries() + 1); | ||
597 | + } | ||
598 | + } | ||
599 | + | ||
571 | systemContext.getTbRpcService().save(tenantId, new RpcId(rpcId), status, null); | 600 | systemContext.getTbRpcService().save(tenantId, new RpcId(rpcId), status, null); |
601 | + if (status != RpcStatus.SENT) { | ||
602 | + sendNextPendingRequest(context); | ||
603 | + } | ||
572 | } else { | 604 | } else { |
573 | log.info("[{}][{}] Rpc has already removed from pending map.", deviceId, rpcId); | 605 | log.info("[{}][{}] Rpc has already removed from pending map.", deviceId, rpcId); |
574 | } | 606 | } |
@@ -608,7 +640,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | @@ -608,7 +640,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | ||
608 | sessionMD.setSubscribedToRPC(true); | 640 | sessionMD.setSubscribedToRPC(true); |
609 | log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); | 641 | log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); |
610 | rpcSubscriptions.put(sessionId, sessionMD.getSessionInfo()); | 642 | rpcSubscriptions.put(sessionId, sessionMD.getSessionInfo()); |
611 | - sendPendingRequest(context, sessionId, sessionInfo.getNodeId()); | 643 | + sendPendingRequests(context, sessionId, sessionInfo.getNodeId()); |
612 | dumpSessions(); | 644 | dumpSessions(); |
613 | } | 645 | } |
614 | } | 646 | } |
@@ -884,7 +916,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | @@ -884,7 +916,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { | ||
884 | ToDeviceRpcRequest msg = JacksonUtil.convertValue(rpc.getRequest(), ToDeviceRpcRequest.class); | 916 | ToDeviceRpcRequest msg = JacksonUtil.convertValue(rpc.getRequest(), ToDeviceRpcRequest.class); |
885 | long timeout = rpc.getExpirationTime() - System.currentTimeMillis(); | 917 | long timeout = rpc.getExpirationTime() - System.currentTimeMillis(); |
886 | if (timeout <= 0) { | 918 | if (timeout <= 0) { |
887 | - rpc.setStatus(RpcStatus.TIMEOUT); | 919 | + rpc.setStatus(RpcStatus.EXPIRED); |
888 | systemContext.getTbRpcService().save(tenantId, rpc); | 920 | systemContext.getTbRpcService().save(tenantId, rpc); |
889 | } else { | 921 | } else { |
890 | registerPendingRpcRequest(ctx, new ToDeviceRpcRequestActorMsg(systemContext.getServiceId(), msg), false, creteToDeviceRpcRequestMsg(msg), timeout); | 922 | registerPendingRpcRequest(ctx, new ToDeviceRpcRequestActorMsg(systemContext.getServiceId(), msg), false, creteToDeviceRpcRequestMsg(msg), timeout); |
@@ -25,4 +25,6 @@ import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; | @@ -25,4 +25,6 @@ import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; | ||
25 | public class ToDeviceRpcRequestMetadata { | 25 | public class ToDeviceRpcRequestMetadata { |
26 | private final ToDeviceRpcRequestActorMsg msg; | 26 | private final ToDeviceRpcRequestActorMsg msg; |
27 | private final boolean sent; | 27 | private final boolean sent; |
28 | + private int retries; | ||
29 | + private boolean delivered; | ||
28 | } | 30 | } |
@@ -326,6 +326,11 @@ actors: | @@ -326,6 +326,11 @@ actors: | ||
326 | queue_size: "${ACTORS_RULE_TRANSACTION_QUEUE_SIZE:15000}" | 326 | queue_size: "${ACTORS_RULE_TRANSACTION_QUEUE_SIZE:15000}" |
327 | # Time in milliseconds for transaction to complete | 327 | # Time in milliseconds for transaction to complete |
328 | duration: "${ACTORS_RULE_TRANSACTION_DURATION:60000}" | 328 | duration: "${ACTORS_RULE_TRANSACTION_DURATION:60000}" |
329 | + rpc: | ||
330 | + persistent: | ||
331 | + retries: "${ACTORS_RPC_PERSISTENT_RETRIES:5}" | ||
332 | + sequence: | ||
333 | + enabled: "${ACTORS_RPC_SEQUENCE_ENABLED:true}" | ||
329 | statistics: | 334 | statistics: |
330 | # Enable/disable actor statistics | 335 | # Enable/disable actor statistics |
331 | enabled: "${ACTORS_STATISTICS_ENABLED:true}" | 336 | enabled: "${ACTORS_STATISTICS_ENABLED:true}" |
@@ -16,5 +16,5 @@ | @@ -16,5 +16,5 @@ | ||
16 | package org.thingsboard.server.common.data.rpc; | 16 | package org.thingsboard.server.common.data.rpc; |
17 | 17 | ||
18 | public enum RpcStatus { | 18 | public enum RpcStatus { |
19 | - QUEUED, DELIVERED, SUCCESSFUL, TIMEOUT, FAILED | 19 | + QUEUED, SENT, DELIVERED, SUCCESSFUL, TIMEOUT, EXPIRED, FAILED |
20 | } | 20 | } |
@@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo | @@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo | ||
42 | import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; | 42 | import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; |
43 | import org.thingsboard.server.common.data.id.DeviceId; | 43 | import org.thingsboard.server.common.data.id.DeviceId; |
44 | import org.thingsboard.server.common.data.id.DeviceProfileId; | 44 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
45 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | ||
45 | import org.thingsboard.server.common.msg.session.FeatureType; | 46 | import org.thingsboard.server.common.msg.session.FeatureType; |
46 | import org.thingsboard.server.common.msg.session.SessionMsgType; | 47 | import org.thingsboard.server.common.msg.session.SessionMsgType; |
47 | import org.thingsboard.server.common.transport.SessionMsgListener; | 48 | import org.thingsboard.server.common.transport.SessionMsgListener; |
@@ -532,7 +533,7 @@ public class DefaultCoapClientContext implements CoapClientContext { | @@ -532,7 +533,7 @@ public class DefaultCoapClientContext implements CoapClientContext { | ||
532 | response.addMessageObserver(new TbCoapMessageObserver(requestId, id -> { | 533 | response.addMessageObserver(new TbCoapMessageObserver(requestId, id -> { |
533 | TransportProtos.ToDeviceRpcRequestMsg rpcRequestMsg = transportContext.getRpcAwaitingAck().remove(id); | 534 | TransportProtos.ToDeviceRpcRequestMsg rpcRequestMsg = transportContext.getRpcAwaitingAck().remove(id); |
534 | if (rpcRequestMsg != null) { | 535 | if (rpcRequestMsg != null) { |
535 | - transportService.process(state.getSession(), rpcRequestMsg, TransportServiceCallback.EMPTY); | 536 | + transportService.process(state.getSession(), rpcRequestMsg, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY); |
536 | } | 537 | } |
537 | }, null)); | 538 | }, null)); |
538 | } | 539 | } |
@@ -553,8 +554,12 @@ public class DefaultCoapClientContext implements CoapClientContext { | @@ -553,8 +554,12 @@ public class DefaultCoapClientContext implements CoapClientContext { | ||
553 | transportService.process(state.getSession(), | 554 | transportService.process(state.getSession(), |
554 | TransportProtos.ToDeviceRpcResponseMsg.newBuilder() | 555 | TransportProtos.ToDeviceRpcResponseMsg.newBuilder() |
555 | .setRequestId(msg.getRequestId()).setError(error).build(), TransportServiceCallback.EMPTY); | 556 | .setRequestId(msg.getRequestId()).setError(error).build(), TransportServiceCallback.EMPTY); |
556 | - } else if (msg.getPersisted() && !conRequest && sent) { | ||
557 | - transportService.process(state.getSession(), msg, TransportServiceCallback.EMPTY); | 557 | + } else if (msg.getPersisted() && sent) { |
558 | + if (conRequest) { | ||
559 | + transportService.process(state.getSession(), msg, RpcStatus.SENT, TransportServiceCallback.EMPTY); | ||
560 | + } else { | ||
561 | + transportService.process(state.getSession(), msg, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY); | ||
562 | + } | ||
558 | } | 563 | } |
559 | } | 564 | } |
560 | } | 565 | } |
@@ -409,7 +409,7 @@ public class DeviceApiController implements TbTransportService { | @@ -409,7 +409,7 @@ public class DeviceApiController implements TbTransportService { | ||
409 | public void onToDeviceRpcRequest(UUID sessionId, ToDeviceRpcRequestMsg msg) { | 409 | public void onToDeviceRpcRequest(UUID sessionId, ToDeviceRpcRequestMsg msg) { |
410 | log.trace("[{}] Received RPC command to device", sessionId); | 410 | log.trace("[{}] Received RPC command to device", sessionId); |
411 | responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK)); | 411 | responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg, true).toString(), HttpStatus.OK)); |
412 | - transportService.process(sessionInfo, msg, TransportServiceCallback.EMPTY); | 412 | + transportService.process(sessionInfo, msg, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY); |
413 | } | 413 | } |
414 | 414 | ||
415 | @Override | 415 | @Override |
@@ -21,7 +21,9 @@ import org.eclipse.leshan.core.ResponseCode; | @@ -21,7 +21,9 @@ import org.eclipse.leshan.core.ResponseCode; | ||
21 | import org.springframework.stereotype.Service; | 21 | import org.springframework.stereotype.Service; |
22 | import org.thingsboard.common.util.JacksonUtil; | 22 | import org.thingsboard.common.util.JacksonUtil; |
23 | import org.thingsboard.server.common.data.StringUtils; | 23 | import org.thingsboard.server.common.data.StringUtils; |
24 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | ||
24 | import org.thingsboard.server.common.transport.TransportService; | 25 | import org.thingsboard.server.common.transport.TransportService; |
26 | +import org.thingsboard.server.common.transport.TransportServiceCallback; | ||
25 | import org.thingsboard.server.gen.transport.TransportProtos; | 27 | import org.thingsboard.server.gen.transport.TransportProtos; |
26 | import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; | 28 | import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; |
27 | import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; | 29 | import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; |
@@ -158,6 +160,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { | @@ -158,6 +160,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { | ||
158 | throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); | 160 | throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); |
159 | } | 161 | } |
160 | } | 162 | } |
163 | + transportService.process(client.getSession(), rpcRequest, RpcStatus.SENT, TransportServiceCallback.EMPTY); | ||
161 | } catch (IllegalArgumentException e) { | 164 | } catch (IllegalArgumentException e) { |
162 | this.sendErrorRpcResponse(sessionInfo, rpcRequest.getRequestId(), ResponseCode.BAD_REQUEST, e.getMessage()); | 165 | this.sendErrorRpcResponse(sessionInfo, rpcRequest.getRequestId(), ResponseCode.BAD_REQUEST, e.getMessage()); |
163 | } | 166 | } |
@@ -19,6 +19,7 @@ import org.eclipse.leshan.core.ResponseCode; | @@ -19,6 +19,7 @@ import org.eclipse.leshan.core.ResponseCode; | ||
19 | import org.eclipse.leshan.core.request.exception.ClientSleepingException; | 19 | import org.eclipse.leshan.core.request.exception.ClientSleepingException; |
20 | import org.thingsboard.common.util.JacksonUtil; | 20 | import org.thingsboard.common.util.JacksonUtil; |
21 | import org.thingsboard.server.common.data.StringUtils; | 21 | import org.thingsboard.server.common.data.StringUtils; |
22 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | ||
22 | import org.thingsboard.server.common.transport.TransportService; | 23 | import org.thingsboard.server.common.transport.TransportService; |
23 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 24 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
24 | import org.thingsboard.server.gen.transport.TransportProtos; | 25 | import org.thingsboard.server.gen.transport.TransportProtos; |
@@ -44,7 +45,7 @@ public abstract class RpcDownlinkRequestCallbackProxy<R, T> implements DownlinkR | @@ -44,7 +45,7 @@ public abstract class RpcDownlinkRequestCallbackProxy<R, T> implements DownlinkR | ||
44 | 45 | ||
45 | @Override | 46 | @Override |
46 | public void onSuccess(R request, T response) { | 47 | public void onSuccess(R request, T response) { |
47 | - transportService.process(client.getSession(), this.request, TransportServiceCallback.EMPTY); | 48 | + transportService.process(client.getSession(), this.request, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY); |
48 | sendRpcReplyOnSuccess(response); | 49 | sendRpcReplyOnSuccess(response); |
49 | if (callback != null) { | 50 | if (callback != null) { |
50 | callback.onSuccess(request, response); | 51 | callback.onSuccess(request, response); |
@@ -61,7 +62,9 @@ public abstract class RpcDownlinkRequestCallbackProxy<R, T> implements DownlinkR | @@ -61,7 +62,9 @@ public abstract class RpcDownlinkRequestCallbackProxy<R, T> implements DownlinkR | ||
61 | 62 | ||
62 | @Override | 63 | @Override |
63 | public void onError(String params, Exception e) { | 64 | public void onError(String params, Exception e) { |
64 | - if (!(e instanceof TimeoutException || e instanceof ClientSleepingException)) { | 65 | + if (e instanceof TimeoutException) { |
66 | + transportService.process(client.getSession(), this.request, RpcStatus.TIMEOUT, TransportServiceCallback.EMPTY); | ||
67 | + } else if (!(e instanceof ClientSleepingException)) { | ||
65 | sendRpcReplyOnError(e); | 68 | sendRpcReplyOnError(e); |
66 | } | 69 | } |
67 | if (callback != null) { | 70 | if (callback != null) { |
@@ -50,6 +50,7 @@ import org.thingsboard.server.common.data.TransportPayloadType; | @@ -50,6 +50,7 @@ import org.thingsboard.server.common.data.TransportPayloadType; | ||
50 | import org.thingsboard.server.common.data.device.profile.MqttTopics; | 50 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
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; | 52 | import org.thingsboard.server.common.data.ota.OtaPackageType; |
53 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | ||
53 | import org.thingsboard.server.common.msg.EncryptionUtil; | 54 | import org.thingsboard.server.common.msg.EncryptionUtil; |
54 | import org.thingsboard.server.common.msg.tools.TbRateLimitsException; | 55 | import org.thingsboard.server.common.msg.tools.TbRateLimitsException; |
55 | import org.thingsboard.server.common.transport.SessionMsgListener; | 56 | import org.thingsboard.server.common.transport.SessionMsgListener; |
@@ -272,7 +273,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -272,7 +273,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
272 | int msgId = ((MqttPubAckMessage) msg).variableHeader().messageId(); | 273 | int msgId = ((MqttPubAckMessage) msg).variableHeader().messageId(); |
273 | TransportProtos.ToDeviceRpcRequestMsg rpcRequest = rpcAwaitingAck.remove(msgId); | 274 | TransportProtos.ToDeviceRpcRequestMsg rpcRequest = rpcAwaitingAck.remove(msgId); |
274 | if (rpcRequest != null) { | 275 | if (rpcRequest != null) { |
275 | - transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequest, TransportServiceCallback.EMPTY); | 276 | + transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequest, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY); |
276 | } | 277 | } |
277 | break; | 278 | break; |
278 | default: | 279 | default: |
@@ -856,10 +857,14 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -856,10 +857,14 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
856 | }, Math.max(0, rpcRequest.getExpirationTime() - System.currentTimeMillis()), TimeUnit.MILLISECONDS); | 857 | }, Math.max(0, rpcRequest.getExpirationTime() - System.currentTimeMillis()), TimeUnit.MILLISECONDS); |
857 | } | 858 | } |
858 | var cf = publish(payload, deviceSessionCtx); | 859 | var cf = publish(payload, deviceSessionCtx); |
859 | - if (rpcRequest.getPersisted() && !isAckExpected(payload)) { | 860 | + if (rpcRequest.getPersisted()) { |
860 | cf.addListener(result -> { | 861 | cf.addListener(result -> { |
861 | if (result.cause() == null) { | 862 | if (result.cause() == null) { |
862 | - transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequest, TransportServiceCallback.EMPTY); | 863 | + if (isAckExpected(payload)) { |
864 | + transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequest, RpcStatus.SENT, TransportServiceCallback.EMPTY); | ||
865 | + } else { | ||
866 | + transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequest, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY); | ||
867 | + } | ||
863 | } | 868 | } |
864 | }); | 869 | }); |
865 | } | 870 | } |
@@ -16,8 +16,10 @@ | @@ -16,8 +16,10 @@ | ||
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 io.netty.channel.ChannelFuture; |
19 | +import io.netty.handler.codec.mqtt.MqttMessage; | ||
19 | import lombok.extern.slf4j.Slf4j; | 20 | import lombok.extern.slf4j.Slf4j; |
20 | import org.thingsboard.server.common.data.DeviceProfile; | 21 | import org.thingsboard.server.common.data.DeviceProfile; |
22 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | ||
21 | import org.thingsboard.server.common.transport.SessionMsgListener; | 23 | import org.thingsboard.server.common.transport.SessionMsgListener; |
22 | import org.thingsboard.server.common.transport.TransportService; | 24 | import org.thingsboard.server.common.transport.TransportService; |
23 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 25 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
@@ -102,9 +104,13 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple | @@ -102,9 +104,13 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple | ||
102 | payload -> { | 104 | payload -> { |
103 | ChannelFuture channelFuture = parent.writeAndFlush(payload); | 105 | ChannelFuture channelFuture = parent.writeAndFlush(payload); |
104 | if (request.getPersisted()) { | 106 | if (request.getPersisted()) { |
105 | - channelFuture.addListener(future -> { | ||
106 | - if (future.cause() == null) { | ||
107 | - transportService.process(getSessionInfo(), request, TransportServiceCallback.EMPTY); | 107 | + channelFuture.addListener(result -> { |
108 | + if (result.cause() == null) { | ||
109 | + if (isAckExpected(payload)) { | ||
110 | + transportService.process(getSessionInfo(), request, RpcStatus.SENT, TransportServiceCallback.EMPTY); | ||
111 | + } else { | ||
112 | + transportService.process(getSessionInfo(), request, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY); | ||
113 | + } | ||
108 | } | 114 | } |
109 | }); | 115 | }); |
110 | } | 116 | } |
@@ -129,4 +135,8 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple | @@ -129,4 +135,8 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple | ||
129 | // This feature is not supported in the TB IoT Gateway yet. | 135 | // This feature is not supported in the TB IoT Gateway yet. |
130 | } | 136 | } |
131 | 137 | ||
138 | + private boolean isAckExpected(MqttMessage message) { | ||
139 | + return message.fixedHeader().qosLevel().value() > 0; | ||
140 | + } | ||
141 | + | ||
132 | } | 142 | } |
@@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.DeviceProfile; | @@ -26,6 +26,7 @@ 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; |
30 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 31 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
31 | import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; | 32 | import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; |
@@ -142,7 +143,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S | @@ -142,7 +143,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S | ||
142 | public void onToDeviceRpcRequest(UUID sessionId, ToDeviceRpcRequestMsg toDeviceRequest) { | 143 | public void onToDeviceRpcRequest(UUID sessionId, ToDeviceRpcRequestMsg toDeviceRequest) { |
143 | log.trace("[{}] Received RPC command to device", sessionId); | 144 | log.trace("[{}] Received RPC command to device", sessionId); |
144 | snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest); | 145 | snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest); |
145 | - snmpTransportContext.getTransportService().process(getSessionInfo(), toDeviceRequest, TransportServiceCallback.EMPTY); | 146 | + snmpTransportContext.getTransportService().process(getSessionInfo(), toDeviceRequest, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY); |
146 | } | 147 | } |
147 | 148 | ||
148 | @Override | 149 | @Override |
@@ -17,6 +17,7 @@ package org.thingsboard.server.common.transport; | @@ -17,6 +17,7 @@ package org.thingsboard.server.common.transport; | ||
17 | 17 | ||
18 | import org.thingsboard.server.common.data.DeviceProfile; | 18 | import org.thingsboard.server.common.data.DeviceProfile; |
19 | import org.thingsboard.server.common.data.DeviceTransportType; | 19 | import org.thingsboard.server.common.data.DeviceTransportType; |
20 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | ||
20 | import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; | 21 | import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; |
21 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; | 22 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
22 | import org.thingsboard.server.common.transport.service.SessionMetaData; | 23 | import org.thingsboard.server.common.transport.service.SessionMetaData; |
@@ -112,7 +113,7 @@ public interface TransportService { | @@ -112,7 +113,7 @@ public interface TransportService { | ||
112 | 113 | ||
113 | void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback); | 114 | void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback); |
114 | 115 | ||
115 | - void process(SessionInfoProto sessionInfo, ToDeviceRpcRequestMsg msg, TransportServiceCallback<Void> callback); | 116 | + void process(SessionInfoProto sessionInfo, ToDeviceRpcRequestMsg msg, RpcStatus rpcStatus, TransportServiceCallback<Void> callback); |
116 | 117 | ||
117 | void process(SessionInfoProto sessionInfo, SubscriptionInfoProto msg, TransportServiceCallback<Void> callback); | 118 | void process(SessionInfoProto sessionInfo, SubscriptionInfoProto msg, TransportServiceCallback<Void> callback); |
118 | 119 |
@@ -580,15 +580,13 @@ public class DefaultTransportService implements TransportService { | @@ -580,15 +580,13 @@ public class DefaultTransportService implements TransportService { | ||
580 | } | 580 | } |
581 | 581 | ||
582 | @Override | 582 | @Override |
583 | - public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcRequestMsg msg, TransportServiceCallback<Void> callback) { | 583 | + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToDeviceRpcRequestMsg msg, RpcStatus rpcStatus, TransportServiceCallback<Void> callback) { |
584 | if (msg.getPersisted()) { | 584 | if (msg.getPersisted()) { |
585 | - RpcStatus status = msg.getOneway() ? RpcStatus.SUCCESSFUL : RpcStatus.DELIVERED; | ||
586 | - | ||
587 | TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder() | 585 | TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder() |
588 | .setRequestId(msg.getRequestId()) | 586 | .setRequestId(msg.getRequestId()) |
589 | .setRequestIdLSB(msg.getRequestIdLSB()) | 587 | .setRequestIdLSB(msg.getRequestIdLSB()) |
590 | .setRequestIdMSB(msg.getRequestIdMSB()) | 588 | .setRequestIdMSB(msg.getRequestIdMSB()) |
591 | - .setStatus(status.name()) | 589 | + .setStatus(rpcStatus.name()) |
592 | .build(); | 590 | .build(); |
593 | 591 | ||
594 | if (checkLimits(sessionInfo, responseMsg, callback)) { | 592 | if (checkLimits(sessionInfo, responseMsg, callback)) { |