Showing
14 changed files
with
114 additions
and
41 deletions
... | ... | @@ -400,6 +400,14 @@ public class ActorSystemContext { |
400 | 400 | @Getter |
401 | 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 | 411 | @Getter |
404 | 412 | @Setter |
405 | 413 | private TbActorSystem actorSystem; | ... | ... |
... | ... | @@ -121,6 +121,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
121 | 121 | private final Map<UUID, SessionInfo> attributeSubscriptions; |
122 | 122 | private final Map<UUID, SessionInfo> rpcSubscriptions; |
123 | 123 | private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap; |
124 | + private final boolean rpcSequenceEnabled; | |
124 | 125 | |
125 | 126 | private int rpcSeq = 0; |
126 | 127 | private String deviceName; |
... | ... | @@ -132,6 +133,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
132 | 133 | super(systemContext); |
133 | 134 | this.tenantId = tenantId; |
134 | 135 | this.deviceId = deviceId; |
136 | + this.rpcSequenceEnabled = systemContext.isRpcSequenceEnabled(); | |
135 | 137 | this.attributeSubscriptions = new HashMap<>(); |
136 | 138 | this.rpcSubscriptions = new HashMap<>(); |
137 | 139 | this.toDeviceRpcPendingMap = new LinkedHashMap<>(); |
... | ... | @@ -185,19 +187,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
185 | 187 | if (timeout <= 0) { |
186 | 188 | log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime()); |
187 | 189 | if (persisted) { |
188 | - createRpc(request, RpcStatus.TIMEOUT); | |
190 | + createRpc(request, RpcStatus.EXPIRED); | |
189 | 191 | } |
190 | 192 | return; |
191 | 193 | } else if (persisted) { |
192 | 194 | createRpc(request, RpcStatus.QUEUED); |
193 | 195 | } |
194 | 196 | |
195 | - boolean sent; | |
197 | + boolean sent = false; | |
196 | 198 | if (systemContext.isEdgesEnabled() && edgeId != null) { |
197 | 199 | log.debug("[{}][{}] device is related to edge [{}]. Saving RPC request to edge queue", tenantId, deviceId, edgeId.getId()); |
198 | 200 | saveRpcRequestToEdgeQueue(request, rpcRequest.getRequestId()); |
199 | 201 | sent = true; |
200 | - } else { | |
202 | + } else if (!rpcSequenceEnabled || toDeviceRpcPendingMap.isEmpty()) { | |
201 | 203 | sent = rpcSubscriptions.size() > 0; |
202 | 204 | Set<UUID> syncSessionSet = new HashSet<>(); |
203 | 205 | rpcSubscriptions.forEach((key, value) -> { |
... | ... | @@ -292,7 +294,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
292 | 294 | if (requestMd != null) { |
293 | 295 | log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); |
294 | 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 | 299 | systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), |
298 | 300 | null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); |
... | ... | @@ -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 | 306 | SessionType sessionType = getSessionType(sessionId); |
305 | 307 | if (!toDeviceRpcPendingMap.isEmpty()) { |
306 | 308 | log.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId); |
... | ... | @@ -312,11 +314,34 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
312 | 314 | log.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId); |
313 | 315 | } |
314 | 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 | 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 | 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 | 363 | .setPersisted(request.isPersisted()) |
339 | 364 | .build(); |
340 | 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 | 381 | processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToRPC()); |
362 | 382 | } |
363 | 383 | if (msg.hasSendPendingRPC()) { |
364 | - sendPendingRequest(context, getSessionId(sessionInfo), sessionInfo.getNodeId()); | |
384 | + sendPendingRequests(context, getSessionId(sessionInfo), sessionInfo.getNodeId()); | |
365 | 385 | } |
366 | 386 | if (msg.hasGetAttributes()) { |
367 | 387 | handleGetAttributesRequest(context, sessionInfo, msg.getGetAttributes()); |
... | ... | @@ -559,16 +579,28 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
559 | 579 | private void processPersistedRpcResponses(TbActorCtx context, SessionInfoProto sessionInfo, ToDevicePersistedRpcResponseMsg responseMsg) { |
560 | 580 | UUID rpcId = new UUID(responseMsg.getRequestIdMSB(), responseMsg.getRequestIdLSB()); |
561 | 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 | 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 | 600 | systemContext.getTbRpcService().save(tenantId, new RpcId(rpcId), status, null); |
601 | + if (status != RpcStatus.SENT) { | |
602 | + sendNextPendingRequest(context); | |
603 | + } | |
572 | 604 | } else { |
573 | 605 | log.info("[{}][{}] Rpc has already removed from pending map.", deviceId, rpcId); |
574 | 606 | } |
... | ... | @@ -608,7 +640,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
608 | 640 | sessionMD.setSubscribedToRPC(true); |
609 | 641 | log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); |
610 | 642 | rpcSubscriptions.put(sessionId, sessionMD.getSessionInfo()); |
611 | - sendPendingRequest(context, sessionId, sessionInfo.getNodeId()); | |
643 | + sendPendingRequests(context, sessionId, sessionInfo.getNodeId()); | |
612 | 644 | dumpSessions(); |
613 | 645 | } |
614 | 646 | } |
... | ... | @@ -884,7 +916,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
884 | 916 | ToDeviceRpcRequest msg = JacksonUtil.convertValue(rpc.getRequest(), ToDeviceRpcRequest.class); |
885 | 917 | long timeout = rpc.getExpirationTime() - System.currentTimeMillis(); |
886 | 918 | if (timeout <= 0) { |
887 | - rpc.setStatus(RpcStatus.TIMEOUT); | |
919 | + rpc.setStatus(RpcStatus.EXPIRED); | |
888 | 920 | systemContext.getTbRpcService().save(tenantId, rpc); |
889 | 921 | } else { |
890 | 922 | registerPendingRpcRequest(ctx, new ToDeviceRpcRequestActorMsg(systemContext.getServiceId(), msg), false, creteToDeviceRpcRequestMsg(msg), timeout); | ... | ... |
... | ... | @@ -25,4 +25,6 @@ import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; |
25 | 25 | public class ToDeviceRpcRequestMetadata { |
26 | 26 | private final ToDeviceRpcRequestActorMsg msg; |
27 | 27 | private final boolean sent; |
28 | + private int retries; | |
29 | + private boolean delivered; | |
28 | 30 | } | ... | ... |
... | ... | @@ -326,6 +326,11 @@ actors: |
326 | 326 | queue_size: "${ACTORS_RULE_TRANSACTION_QUEUE_SIZE:15000}" |
327 | 327 | # Time in milliseconds for transaction to complete |
328 | 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 | 334 | statistics: |
330 | 335 | # Enable/disable actor statistics |
331 | 336 | enabled: "${ACTORS_STATISTICS_ENABLED:true}" | ... | ... |
... | ... | @@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo |
42 | 42 | import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; |
43 | 43 | import org.thingsboard.server.common.data.id.DeviceId; |
44 | 44 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
45 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | |
45 | 46 | import org.thingsboard.server.common.msg.session.FeatureType; |
46 | 47 | import org.thingsboard.server.common.msg.session.SessionMsgType; |
47 | 48 | import org.thingsboard.server.common.transport.SessionMsgListener; |
... | ... | @@ -532,7 +533,7 @@ public class DefaultCoapClientContext implements CoapClientContext { |
532 | 533 | response.addMessageObserver(new TbCoapMessageObserver(requestId, id -> { |
533 | 534 | TransportProtos.ToDeviceRpcRequestMsg rpcRequestMsg = transportContext.getRpcAwaitingAck().remove(id); |
534 | 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 | 538 | }, null)); |
538 | 539 | } |
... | ... | @@ -553,8 +554,12 @@ public class DefaultCoapClientContext implements CoapClientContext { |
553 | 554 | transportService.process(state.getSession(), |
554 | 555 | TransportProtos.ToDeviceRpcResponseMsg.newBuilder() |
555 | 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 | 409 | public void onToDeviceRpcRequest(UUID sessionId, ToDeviceRpcRequestMsg msg) { |
410 | 410 | log.trace("[{}] Received RPC command to device", sessionId); |
411 | 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 | 415 | @Override | ... | ... |
... | ... | @@ -21,7 +21,9 @@ import org.eclipse.leshan.core.ResponseCode; |
21 | 21 | import org.springframework.stereotype.Service; |
22 | 22 | import org.thingsboard.common.util.JacksonUtil; |
23 | 23 | import org.thingsboard.server.common.data.StringUtils; |
24 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | |
24 | 25 | import org.thingsboard.server.common.transport.TransportService; |
26 | +import org.thingsboard.server.common.transport.TransportServiceCallback; | |
25 | 27 | import org.thingsboard.server.gen.transport.TransportProtos; |
26 | 28 | import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; |
27 | 29 | import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; |
... | ... | @@ -158,6 +160,7 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { |
158 | 160 | throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); |
159 | 161 | } |
160 | 162 | } |
163 | + transportService.process(client.getSession(), rpcRequest, RpcStatus.SENT, TransportServiceCallback.EMPTY); | |
161 | 164 | } catch (IllegalArgumentException e) { |
162 | 165 | this.sendErrorRpcResponse(sessionInfo, rpcRequest.getRequestId(), ResponseCode.BAD_REQUEST, e.getMessage()); |
163 | 166 | } | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import org.eclipse.leshan.core.ResponseCode; |
19 | 19 | import org.eclipse.leshan.core.request.exception.ClientSleepingException; |
20 | 20 | import org.thingsboard.common.util.JacksonUtil; |
21 | 21 | import org.thingsboard.server.common.data.StringUtils; |
22 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | |
22 | 23 | import org.thingsboard.server.common.transport.TransportService; |
23 | 24 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
24 | 25 | import org.thingsboard.server.gen.transport.TransportProtos; |
... | ... | @@ -44,7 +45,7 @@ public abstract class RpcDownlinkRequestCallbackProxy<R, T> implements DownlinkR |
44 | 45 | |
45 | 46 | @Override |
46 | 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 | 49 | sendRpcReplyOnSuccess(response); |
49 | 50 | if (callback != null) { |
50 | 51 | callback.onSuccess(request, response); |
... | ... | @@ -61,7 +62,9 @@ public abstract class RpcDownlinkRequestCallbackProxy<R, T> implements DownlinkR |
61 | 62 | |
62 | 63 | @Override |
63 | 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 | 68 | sendRpcReplyOnError(e); |
66 | 69 | } |
67 | 70 | if (callback != null) { | ... | ... |
... | ... | @@ -50,6 +50,7 @@ import org.thingsboard.server.common.data.TransportPayloadType; |
50 | 50 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
51 | 51 | import org.thingsboard.server.common.data.id.OtaPackageId; |
52 | 52 | import org.thingsboard.server.common.data.ota.OtaPackageType; |
53 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | |
53 | 54 | import org.thingsboard.server.common.msg.EncryptionUtil; |
54 | 55 | import org.thingsboard.server.common.msg.tools.TbRateLimitsException; |
55 | 56 | import org.thingsboard.server.common.transport.SessionMsgListener; |
... | ... | @@ -272,7 +273,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement |
272 | 273 | int msgId = ((MqttPubAckMessage) msg).variableHeader().messageId(); |
273 | 274 | TransportProtos.ToDeviceRpcRequestMsg rpcRequest = rpcAwaitingAck.remove(msgId); |
274 | 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 | 278 | break; |
278 | 279 | default: |
... | ... | @@ -856,10 +857,14 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement |
856 | 857 | }, Math.max(0, rpcRequest.getExpirationTime() - System.currentTimeMillis()), TimeUnit.MILLISECONDS); |
857 | 858 | } |
858 | 859 | var cf = publish(payload, deviceSessionCtx); |
859 | - if (rpcRequest.getPersisted() && !isAckExpected(payload)) { | |
860 | + if (rpcRequest.getPersisted()) { | |
860 | 861 | cf.addListener(result -> { |
861 | 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 | 16 | package org.thingsboard.server.transport.mqtt.session; |
17 | 17 | |
18 | 18 | import io.netty.channel.ChannelFuture; |
19 | +import io.netty.handler.codec.mqtt.MqttMessage; | |
19 | 20 | import lombok.extern.slf4j.Slf4j; |
20 | 21 | import org.thingsboard.server.common.data.DeviceProfile; |
22 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | |
21 | 23 | import org.thingsboard.server.common.transport.SessionMsgListener; |
22 | 24 | import org.thingsboard.server.common.transport.TransportService; |
23 | 25 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
... | ... | @@ -102,9 +104,13 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple |
102 | 104 | payload -> { |
103 | 105 | ChannelFuture channelFuture = parent.writeAndFlush(payload); |
104 | 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 | 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 | 26 | import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; |
27 | 27 | import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration; |
28 | 28 | import org.thingsboard.server.common.data.id.DeviceId; |
29 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | |
29 | 30 | import org.thingsboard.server.common.transport.SessionMsgListener; |
30 | 31 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
31 | 32 | import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; |
... | ... | @@ -142,7 +143,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S |
142 | 143 | public void onToDeviceRpcRequest(UUID sessionId, ToDeviceRpcRequestMsg toDeviceRequest) { |
143 | 144 | log.trace("[{}] Received RPC command to device", sessionId); |
144 | 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 | 149 | @Override | ... | ... |
... | ... | @@ -17,6 +17,7 @@ package org.thingsboard.server.common.transport; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.common.data.DeviceProfile; |
19 | 19 | import org.thingsboard.server.common.data.DeviceTransportType; |
20 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | |
20 | 21 | import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; |
21 | 22 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
22 | 23 | import org.thingsboard.server.common.transport.service.SessionMetaData; |
... | ... | @@ -112,7 +113,7 @@ public interface TransportService { |
112 | 113 | |
113 | 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 | 118 | void process(SessionInfoProto sessionInfo, SubscriptionInfoProto msg, TransportServiceCallback<Void> callback); |
118 | 119 | ... | ... |
... | ... | @@ -580,15 +580,13 @@ public class DefaultTransportService implements TransportService { |
580 | 580 | } |
581 | 581 | |
582 | 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 | 584 | if (msg.getPersisted()) { |
585 | - RpcStatus status = msg.getOneway() ? RpcStatus.SUCCESSFUL : RpcStatus.DELIVERED; | |
586 | - | |
587 | 585 | TransportProtos.ToDevicePersistedRpcResponseMsg responseMsg = TransportProtos.ToDevicePersistedRpcResponseMsg.newBuilder() |
588 | 586 | .setRequestId(msg.getRequestId()) |
589 | 587 | .setRequestIdLSB(msg.getRequestIdLSB()) |
590 | 588 | .setRequestIdMSB(msg.getRequestIdMSB()) |
591 | - .setStatus(status.name()) | |
589 | + .setStatus(rpcStatus.name()) | |
592 | 590 | .build(); |
593 | 591 | |
594 | 592 | if (checkLimits(sessionInfo, responseMsg, callback)) { | ... | ... |