Commit 6106f4c0bf60ab70ad96c72830ea3714089e36b0
1 parent
6c2a7d6f
Routing RPC calls through rule engine now
Showing
10 changed files
with
137 additions
and
32 deletions
@@ -69,6 +69,18 @@ | @@ -69,6 +69,18 @@ | ||
69 | "configuration": { | 69 | "configuration": { |
70 | "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" | 70 | "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" |
71 | } | 71 | } |
72 | + }, | ||
73 | + { | ||
74 | + "additionalInfo": { | ||
75 | + "layoutX": 825, | ||
76 | + "layoutY": 468 | ||
77 | + }, | ||
78 | + "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", | ||
79 | + "name": "Test", | ||
80 | + "debugMode": true, | ||
81 | + "configuration": { | ||
82 | + "timeoutInSeconds": 60 | ||
83 | + } | ||
72 | } | 84 | } |
73 | ], | 85 | ], |
74 | "connections": [ | 86 | "connections": [ |
@@ -91,6 +103,11 @@ | @@ -91,6 +103,11 @@ | ||
91 | "fromIndex": 2, | 103 | "fromIndex": 2, |
92 | "toIndex": 3, | 104 | "toIndex": 3, |
93 | "type": "RPC Request" | 105 | "type": "RPC Request" |
106 | + }, | ||
107 | + { | ||
108 | + "fromIndex": 2, | ||
109 | + "toIndex": 5, | ||
110 | + "type": "RPC Request to Device" | ||
94 | } | 111 | } |
95 | ], | 112 | ], |
96 | "ruleChainConnections": null | 113 | "ruleChainConnections": null |
@@ -225,9 +225,12 @@ class DefaultTbContext implements TbContext { | @@ -225,9 +225,12 @@ class DefaultTbContext implements TbContext { | ||
225 | 225 | ||
226 | @Override | 226 | @Override |
227 | public void sendRpcRequest(RuleEngineDeviceRpcRequest src, Consumer<RuleEngineDeviceRpcResponse> consumer) { | 227 | public void sendRpcRequest(RuleEngineDeviceRpcRequest src, Consumer<RuleEngineDeviceRpcResponse> consumer) { |
228 | - ToDeviceRpcRequest request = new ToDeviceRpcRequest(UUIDs.timeBased(), nodeCtx.getTenantId(), src.getDeviceId(), | ||
229 | - src.isOneway(), System.currentTimeMillis() + src.getTimeout(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody())); | 228 | + ToDeviceRpcRequest request = new ToDeviceRpcRequest(src.getRequestUUID(), nodeCtx.getTenantId(), src.getDeviceId(), |
229 | + src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody())); | ||
230 | mainCtx.getDeviceRpcService().processRpcRequestToDevice(request, response -> { | 230 | mainCtx.getDeviceRpcService().processRpcRequestToDevice(request, response -> { |
231 | + if (src.isRestApiCall()) { | ||
232 | + mainCtx.getDeviceRpcService().processRestAPIRpcResponseFromRuleEngine(response); | ||
233 | + } | ||
231 | consumer.accept(RuleEngineDeviceRpcResponse.builder() | 234 | consumer.accept(RuleEngineDeviceRpcResponse.builder() |
232 | .deviceId(src.getDeviceId()) | 235 | .deviceId(src.getDeviceId()) |
233 | .requestId(src.getRequestId()) | 236 | .requestId(src.getRequestId()) |
@@ -128,7 +128,7 @@ public class RpcController extends BaseController { | @@ -128,7 +128,7 @@ public class RpcController extends BaseController { | ||
128 | timeout, | 128 | timeout, |
129 | body | 129 | body |
130 | ); | 130 | ); |
131 | - deviceRpcService.processRpcRequestToDevice(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse)); | 131 | + deviceRpcService.processRestAPIRpcRequestToRuleEngine(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse)); |
132 | } | 132 | } |
133 | 133 | ||
134 | @Override | 134 | @Override |
@@ -15,6 +15,10 @@ | @@ -15,6 +15,10 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.service.rpc; | 16 | package org.thingsboard.server.service.rpc; |
17 | 17 | ||
18 | +import com.datastax.driver.core.utils.UUIDs; | ||
19 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
20 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
21 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
18 | import com.google.protobuf.InvalidProtocolBufferException; | 22 | import com.google.protobuf.InvalidProtocolBufferException; |
19 | import lombok.extern.slf4j.Slf4j; | 23 | import lombok.extern.slf4j.Slf4j; |
20 | import org.springframework.beans.factory.annotation.Autowired; | 24 | import org.springframework.beans.factory.annotation.Autowired; |
@@ -22,22 +26,23 @@ import org.springframework.stereotype.Service; | @@ -22,22 +26,23 @@ import org.springframework.stereotype.Service; | ||
22 | import org.thingsboard.rule.engine.api.RpcError; | 26 | import org.thingsboard.rule.engine.api.RpcError; |
23 | import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; | 27 | import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; |
24 | import org.thingsboard.server.actors.service.ActorService; | 28 | import org.thingsboard.server.actors.service.ActorService; |
29 | +import org.thingsboard.server.common.data.DataConstants; | ||
25 | import org.thingsboard.server.common.data.id.DeviceId; | 30 | import org.thingsboard.server.common.data.id.DeviceId; |
26 | import org.thingsboard.server.common.data.id.TenantId; | 31 | import org.thingsboard.server.common.data.id.TenantId; |
32 | +import org.thingsboard.server.common.msg.TbMsg; | ||
33 | +import org.thingsboard.server.common.msg.TbMsgDataType; | ||
34 | +import org.thingsboard.server.common.msg.TbMsgMetaData; | ||
27 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | 35 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; |
28 | import org.thingsboard.server.common.msg.cluster.ServerAddress; | 36 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
29 | import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg; | 37 | import org.thingsboard.server.common.msg.core.ToServerRpcResponseMsg; |
30 | import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | 38 | import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; |
31 | -import org.thingsboard.server.dao.audit.AuditLogService; | 39 | +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; |
32 | import org.thingsboard.server.gen.cluster.ClusterAPIProtos; | 40 | import org.thingsboard.server.gen.cluster.ClusterAPIProtos; |
33 | import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; | 41 | import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; |
34 | import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; | 42 | import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; |
35 | -import org.thingsboard.server.service.encoding.DataDecodingEncodingService; | ||
36 | -import org.thingsboard.server.service.telemetry.sub.Subscription; | ||
37 | 43 | ||
38 | import javax.annotation.PostConstruct; | 44 | import javax.annotation.PostConstruct; |
39 | import javax.annotation.PreDestroy; | 45 | import javax.annotation.PreDestroy; |
40 | -import java.util.Optional; | ||
41 | import java.util.UUID; | 46 | import java.util.UUID; |
42 | import java.util.concurrent.ConcurrentHashMap; | 47 | import java.util.concurrent.ConcurrentHashMap; |
43 | import java.util.concurrent.ConcurrentMap; | 48 | import java.util.concurrent.ConcurrentMap; |
@@ -53,6 +58,8 @@ import java.util.function.Consumer; | @@ -53,6 +58,8 @@ import java.util.function.Consumer; | ||
53 | @Slf4j | 58 | @Slf4j |
54 | public class DefaultDeviceRpcService implements DeviceRpcService { | 59 | public class DefaultDeviceRpcService implements DeviceRpcService { |
55 | 60 | ||
61 | + private static final ObjectMapper json = new ObjectMapper(); | ||
62 | + | ||
56 | @Autowired | 63 | @Autowired |
57 | private ClusterRoutingService routingService; | 64 | private ClusterRoutingService routingService; |
58 | 65 | ||
@@ -64,7 +71,8 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | @@ -64,7 +71,8 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | ||
64 | 71 | ||
65 | private ScheduledExecutorService rpcCallBackExecutor; | 72 | private ScheduledExecutorService rpcCallBackExecutor; |
66 | 73 | ||
67 | - private final ConcurrentMap<UUID, Consumer<FromDeviceRpcResponse>> localRpcRequests = new ConcurrentHashMap<>(); | 74 | + private final ConcurrentMap<UUID, Consumer<FromDeviceRpcResponse>> localToRuleEngineRpcRequests = new ConcurrentHashMap<>(); |
75 | + private final ConcurrentMap<UUID, Consumer<FromDeviceRpcResponse>> localToDeviceRpcRequests = new ConcurrentHashMap<>(); | ||
68 | 76 | ||
69 | @PostConstruct | 77 | @PostConstruct |
70 | public void initExecutor() { | 78 | public void initExecutor() { |
@@ -79,28 +87,40 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | @@ -79,28 +87,40 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | ||
79 | } | 87 | } |
80 | 88 | ||
81 | @Override | 89 | @Override |
90 | + public void processRestAPIRpcRequestToRuleEngine(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer) { | ||
91 | + log.trace("[{}] Processing local rpc call to rule engine [{}]", request.getTenantId(), request.getDeviceId()); | ||
92 | + UUID requestId = request.getId(); | ||
93 | + localToRuleEngineRpcRequests.put(requestId, responseConsumer); | ||
94 | + sendRpcRequestToRuleEngine(request); | ||
95 | + scheduleTimeout(request, requestId, localToRuleEngineRpcRequests); | ||
96 | + } | ||
97 | + | ||
98 | + @Override | ||
99 | + public void processRestAPIRpcResponseFromRuleEngine(FromDeviceRpcResponse response) { | ||
100 | + UUID requestId = response.getId(); | ||
101 | + Consumer<FromDeviceRpcResponse> consumer = localToRuleEngineRpcRequests.remove(requestId); | ||
102 | + if (consumer != null) { | ||
103 | + consumer.accept(response); | ||
104 | + } else { | ||
105 | + log.trace("[{}] Unknown or stale rpc response received [{}]", requestId, response); | ||
106 | + } | ||
107 | + } | ||
108 | + | ||
109 | + @Override | ||
82 | public void processRpcRequestToDevice(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer) { | 110 | public void processRpcRequestToDevice(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer) { |
83 | - log.trace("[{}] Processing local rpc call for device [{}]", request.getTenantId(), request.getDeviceId()); | ||
84 | - sendRpcRequest(request); | 111 | + log.trace("[{}] Processing local rpc call to device [{}]", request.getTenantId(), request.getDeviceId()); |
85 | UUID requestId = request.getId(); | 112 | UUID requestId = request.getId(); |
86 | - localRpcRequests.put(requestId, responseConsumer); | ||
87 | - long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()); | ||
88 | - log.error("[{}] processing the request: [{}]", this.hashCode(), requestId); | ||
89 | - rpcCallBackExecutor.schedule(() -> { | ||
90 | - log.error("[{}] timeout the request: [{}]", this.hashCode(), requestId); | ||
91 | - Consumer<FromDeviceRpcResponse> consumer = localRpcRequests.remove(requestId); | ||
92 | - if (consumer != null) { | ||
93 | - consumer.accept(new FromDeviceRpcResponse(requestId, null, null, RpcError.TIMEOUT)); | ||
94 | - } | ||
95 | - }, timeout, TimeUnit.MILLISECONDS); | 113 | + localToDeviceRpcRequests.put(requestId, responseConsumer); |
114 | + sendRpcRequestToDevice(request); | ||
115 | + scheduleTimeout(request, requestId, localToDeviceRpcRequests); | ||
96 | } | 116 | } |
97 | 117 | ||
98 | @Override | 118 | @Override |
99 | public void processRpcResponseFromDevice(FromDeviceRpcResponse response) { | 119 | public void processRpcResponseFromDevice(FromDeviceRpcResponse response) { |
100 | - log.error("[{}] response to request: [{}]", this.hashCode(), response.getId()); | 120 | + log.trace("[{}] response to request: [{}]", this.hashCode(), response.getId()); |
101 | if (routingService.getCurrentServer().equals(response.getServerAddress())) { | 121 | if (routingService.getCurrentServer().equals(response.getServerAddress())) { |
102 | UUID requestId = response.getId(); | 122 | UUID requestId = response.getId(); |
103 | - Consumer<FromDeviceRpcResponse> consumer = localRpcRequests.remove(requestId); | 123 | + Consumer<FromDeviceRpcResponse> consumer = localToDeviceRpcRequests.remove(requestId); |
104 | if (consumer != null) { | 124 | if (consumer != null) { |
105 | consumer.accept(response); | 125 | consumer.accept(response); |
106 | } else { | 126 | } else { |
@@ -140,7 +160,27 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | @@ -140,7 +160,27 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | ||
140 | forward(deviceId, rpcMsg); | 160 | forward(deviceId, rpcMsg); |
141 | } | 161 | } |
142 | 162 | ||
143 | - private void sendRpcRequest(ToDeviceRpcRequest msg) { | 163 | + private void sendRpcRequestToRuleEngine(ToDeviceRpcRequest msg) { |
164 | + ObjectNode entityNode = json.createObjectNode(); | ||
165 | + TbMsgMetaData metaData = new TbMsgMetaData(); | ||
166 | + metaData.putValue("requestUUID", msg.getId().toString()); | ||
167 | + metaData.putValue("expirationTime", Long.toString(msg.getExpirationTime())); | ||
168 | + metaData.putValue("oneway", Boolean.toString(msg.isOneway())); | ||
169 | + | ||
170 | + entityNode.put("method", msg.getBody().getMethod()); | ||
171 | + entityNode.put("params", msg.getBody().getParams()); | ||
172 | + | ||
173 | + try { | ||
174 | + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), metaData, TbMsgDataType.JSON | ||
175 | + , json.writeValueAsString(entityNode) | ||
176 | + , null, null, 0L); | ||
177 | + actorService.onMsg(new ServiceToRuleEngineMsg(msg.getTenantId(), tbMsg)); | ||
178 | + } catch (JsonProcessingException e) { | ||
179 | + throw new RuntimeException(e); | ||
180 | + } | ||
181 | + } | ||
182 | + | ||
183 | + private void sendRpcRequestToDevice(ToDeviceRpcRequest msg) { | ||
144 | ToDeviceRpcRequestActorMsg rpcMsg = new ToDeviceRpcRequestActorMsg(routingService.getCurrentServer(), msg); | 184 | ToDeviceRpcRequestActorMsg rpcMsg = new ToDeviceRpcRequestActorMsg(routingService.getCurrentServer(), msg); |
145 | log.trace("[{}] Forwarding msg {} to device actor!", msg.getDeviceId(), msg); | 185 | log.trace("[{}] Forwarding msg {} to device actor!", msg.getDeviceId(), msg); |
146 | forward(msg.getDeviceId(), rpcMsg); | 186 | forward(msg.getDeviceId(), rpcMsg); |
@@ -149,4 +189,18 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | @@ -149,4 +189,18 @@ public class DefaultDeviceRpcService implements DeviceRpcService { | ||
149 | private <T extends ToDeviceActorNotificationMsg> void forward(DeviceId deviceId, T msg) { | 189 | private <T extends ToDeviceActorNotificationMsg> void forward(DeviceId deviceId, T msg) { |
150 | actorService.onMsg(new SendToClusterMsg(deviceId, msg)); | 190 | actorService.onMsg(new SendToClusterMsg(deviceId, msg)); |
151 | } | 191 | } |
192 | + | ||
193 | + private void scheduleTimeout(ToDeviceRpcRequest request, UUID requestId, ConcurrentMap<UUID, Consumer<FromDeviceRpcResponse>> requestsMap) { | ||
194 | + long timeout = Math.max(0, request.getExpirationTime() - System.currentTimeMillis()); | ||
195 | + log.trace("[{}] processing the request: [{}]", this.hashCode(), requestId); | ||
196 | + rpcCallBackExecutor.schedule(() -> { | ||
197 | + log.trace("[{}] timeout the request: [{}]", this.hashCode(), requestId); | ||
198 | + Consumer<FromDeviceRpcResponse> consumer = requestsMap.remove(requestId); | ||
199 | + if (consumer != null) { | ||
200 | + consumer.accept(new FromDeviceRpcResponse(requestId, null, null, RpcError.TIMEOUT)); | ||
201 | + } | ||
202 | + }, timeout, TimeUnit.MILLISECONDS); | ||
203 | + } | ||
204 | + | ||
205 | + | ||
152 | } | 206 | } |
@@ -27,6 +27,10 @@ import java.util.function.Consumer; | @@ -27,6 +27,10 @@ import java.util.function.Consumer; | ||
27 | */ | 27 | */ |
28 | public interface DeviceRpcService { | 28 | public interface DeviceRpcService { |
29 | 29 | ||
30 | + void processRestAPIRpcRequestToRuleEngine(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer); | ||
31 | + | ||
32 | + void processRestAPIRpcResponseFromRuleEngine(FromDeviceRpcResponse response); | ||
33 | + | ||
30 | void processRpcRequestToDevice(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer); | 34 | void processRpcRequestToDevice(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer); |
31 | 35 | ||
32 | void processRpcResponseFromDevice(FromDeviceRpcResponse response); | 36 | void processRpcResponseFromDevice(FromDeviceRpcResponse response); |
@@ -58,4 +58,6 @@ public class DataConstants { | @@ -58,4 +58,6 @@ public class DataConstants { | ||
58 | public static final String ATTRIBUTES_UPDATED = "ATTRIBUTES_UPDATED"; | 58 | public static final String ATTRIBUTES_UPDATED = "ATTRIBUTES_UPDATED"; |
59 | public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED"; | 59 | public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED"; |
60 | 60 | ||
61 | + public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE"; | ||
62 | + | ||
61 | } | 63 | } |
@@ -19,6 +19,8 @@ import lombok.Builder; | @@ -19,6 +19,8 @@ import lombok.Builder; | ||
19 | import lombok.Data; | 19 | import lombok.Data; |
20 | import org.thingsboard.server.common.data.id.DeviceId; | 20 | import org.thingsboard.server.common.data.id.DeviceId; |
21 | 21 | ||
22 | +import java.util.UUID; | ||
23 | + | ||
22 | /** | 24 | /** |
23 | * Created by ashvayka on 02.04.18. | 25 | * Created by ashvayka on 02.04.18. |
24 | */ | 26 | */ |
@@ -28,9 +30,11 @@ public final class RuleEngineDeviceRpcRequest { | @@ -28,9 +30,11 @@ public final class RuleEngineDeviceRpcRequest { | ||
28 | 30 | ||
29 | private final DeviceId deviceId; | 31 | private final DeviceId deviceId; |
30 | private final int requestId; | 32 | private final int requestId; |
33 | + private final UUID requestUUID; | ||
31 | private final boolean oneway; | 34 | private final boolean oneway; |
32 | private final String method; | 35 | private final String method; |
33 | private final String body; | 36 | private final String body; |
34 | - private final long timeout; | 37 | + private final long expirationTime; |
38 | + private final boolean restApiCall; | ||
35 | 39 | ||
36 | } | 40 | } |
@@ -28,7 +28,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; | @@ -28,7 +28,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; | ||
28 | type = ComponentType.FILTER, | 28 | type = ComponentType.FILTER, |
29 | name = "message type switch", | 29 | name = "message type switch", |
30 | configClazz = EmptyNodeConfiguration.class, | 30 | configClazz = EmptyNodeConfiguration.class, |
31 | - relationTypes = {"Post attributes", "Post telemetry", "RPC Request", "Activity Event", "Inactivity Event", | 31 | + relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "Activity Event", "Inactivity Event", |
32 | "Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned", | 32 | "Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned", |
33 | "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Other"}, | 33 | "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Other"}, |
34 | nodeDescription = "Route incoming messages by Message Type", | 34 | nodeDescription = "Route incoming messages by Message Type", |
@@ -52,7 +52,7 @@ public class TbMsgTypeSwitchNode implements TbNode { | @@ -52,7 +52,7 @@ public class TbMsgTypeSwitchNode implements TbNode { | ||
52 | } else if (msg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name())) { | 52 | } else if (msg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name())) { |
53 | relationType = "Post telemetry"; | 53 | relationType = "Post telemetry"; |
54 | } else if (msg.getType().equals(SessionMsgType.TO_SERVER_RPC_REQUEST.name())) { | 54 | } else if (msg.getType().equals(SessionMsgType.TO_SERVER_RPC_REQUEST.name())) { |
55 | - relationType = "RPC Request"; | 55 | + relationType = "RPC Request from Device"; |
56 | } else if (msg.getType().equals(DataConstants.ACTIVITY_EVENT)) { | 56 | } else if (msg.getType().equals(DataConstants.ACTIVITY_EVENT)) { |
57 | relationType = "Activity Event"; | 57 | relationType = "Activity Event"; |
58 | } else if (msg.getType().equals(DataConstants.INACTIVITY_EVENT)) { | 58 | } else if (msg.getType().equals(DataConstants.INACTIVITY_EVENT)) { |
@@ -75,6 +75,8 @@ public class TbMsgTypeSwitchNode implements TbNode { | @@ -75,6 +75,8 @@ public class TbMsgTypeSwitchNode implements TbNode { | ||
75 | relationType = "Attributes Updated"; | 75 | relationType = "Attributes Updated"; |
76 | } else if (msg.getType().equals(DataConstants.ATTRIBUTES_DELETED)) { | 76 | } else if (msg.getType().equals(DataConstants.ATTRIBUTES_DELETED)) { |
77 | relationType = "Attributes Deleted"; | 77 | relationType = "Attributes Deleted"; |
78 | + } else if (msg.getType().equals(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE)) { | ||
79 | + relationType = "RPC Request to Device"; | ||
78 | } else { | 80 | } else { |
79 | relationType = "Other"; | 81 | relationType = "Other"; |
80 | } | 82 | } |
@@ -33,7 +33,7 @@ import org.thingsboard.server.common.msg.TbMsg; | @@ -33,7 +33,7 @@ import org.thingsboard.server.common.msg.TbMsg; | ||
33 | type = ComponentType.ACTION, | 33 | type = ComponentType.ACTION, |
34 | name = "rpc call reply", | 34 | name = "rpc call reply", |
35 | configClazz = TbSendRpcReplyNodeConfiguration.class, | 35 | configClazz = TbSendRpcReplyNodeConfiguration.class, |
36 | - nodeDescription = "Sends one-way RPC call to device", | 36 | + nodeDescription = "Sends reply to RPC call from device", |
37 | nodeDetails = "Expects messages with any message type. Will forward message body to the device.", | 37 | nodeDetails = "Expects messages with any message type. Will forward message body to the device.", |
38 | uiResources = {"static/rulenode/rulenode-core-config.js"}, | 38 | uiResources = {"static/rulenode/rulenode-core-config.js"}, |
39 | configDirective = "tbActionNodeRpcReplyConfig", | 39 | configDirective = "tbActionNodeRpcReplyConfig", |
@@ -15,10 +15,12 @@ | @@ -15,10 +15,12 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.rule.engine.rpc; | 16 | package org.thingsboard.rule.engine.rpc; |
17 | 17 | ||
18 | +import com.datastax.driver.core.utils.UUIDs; | ||
18 | import com.google.gson.Gson; | 19 | import com.google.gson.Gson; |
19 | import com.google.gson.JsonObject; | 20 | import com.google.gson.JsonObject; |
20 | import com.google.gson.JsonParser; | 21 | import com.google.gson.JsonParser; |
21 | import lombok.extern.slf4j.Slf4j; | 22 | import lombok.extern.slf4j.Slf4j; |
23 | +import org.springframework.util.StringUtils; | ||
22 | import org.thingsboard.rule.engine.api.util.TbNodeUtils; | 24 | import org.thingsboard.rule.engine.api.util.TbNodeUtils; |
23 | import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest; | 25 | import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest; |
24 | import org.thingsboard.rule.engine.api.RuleNode; | 26 | import org.thingsboard.rule.engine.api.RuleNode; |
@@ -27,12 +29,14 @@ import org.thingsboard.rule.engine.api.TbNode; | @@ -27,12 +29,14 @@ import org.thingsboard.rule.engine.api.TbNode; | ||
27 | import org.thingsboard.rule.engine.api.TbNodeConfiguration; | 29 | import org.thingsboard.rule.engine.api.TbNodeConfiguration; |
28 | import org.thingsboard.rule.engine.api.TbNodeException; | 30 | import org.thingsboard.rule.engine.api.TbNodeException; |
29 | import org.thingsboard.rule.engine.api.TbRelationTypes; | 31 | import org.thingsboard.rule.engine.api.TbRelationTypes; |
32 | +import org.thingsboard.server.common.data.DataConstants; | ||
30 | import org.thingsboard.server.common.data.EntityType; | 33 | import org.thingsboard.server.common.data.EntityType; |
31 | import org.thingsboard.server.common.data.id.DeviceId; | 34 | import org.thingsboard.server.common.data.id.DeviceId; |
32 | import org.thingsboard.server.common.data.plugin.ComponentType; | 35 | import org.thingsboard.server.common.data.plugin.ComponentType; |
33 | import org.thingsboard.server.common.msg.TbMsg; | 36 | import org.thingsboard.server.common.msg.TbMsg; |
34 | 37 | ||
35 | import java.util.Random; | 38 | import java.util.Random; |
39 | +import java.util.UUID; | ||
36 | import java.util.concurrent.TimeUnit; | 40 | import java.util.concurrent.TimeUnit; |
37 | 41 | ||
38 | @Slf4j | 42 | @Slf4j |
@@ -40,8 +44,9 @@ import java.util.concurrent.TimeUnit; | @@ -40,8 +44,9 @@ import java.util.concurrent.TimeUnit; | ||
40 | type = ComponentType.ACTION, | 44 | type = ComponentType.ACTION, |
41 | name = "rpc call request", | 45 | name = "rpc call request", |
42 | configClazz = TbSendRpcRequestNodeConfiguration.class, | 46 | configClazz = TbSendRpcRequestNodeConfiguration.class, |
43 | - nodeDescription = "Sends two-way RPC call to device", | ||
44 | - nodeDetails = "Expects messages with \"method\" and \"params\". Will forward response from device to next nodes.", | 47 | + nodeDescription = "Sends RPC call to device", |
48 | + nodeDetails = "Expects messages with \"method\" and \"params\". Will forward response from device to next nodes." + | ||
49 | + "If the RPC call request is originated by REST API call from user, will forward the response to user immediately.", | ||
45 | uiResources = {"static/rulenode/rulenode-core-config.js"}, | 50 | uiResources = {"static/rulenode/rulenode-core-config.js"}, |
46 | configDirective = "tbActionNodeRpcRequestConfig", | 51 | configDirective = "tbActionNodeRpcRequestConfig", |
47 | icon = "call_made" | 52 | icon = "call_made" |
@@ -61,7 +66,7 @@ public class TbSendRPCRequestNode implements TbNode { | @@ -61,7 +66,7 @@ public class TbSendRPCRequestNode implements TbNode { | ||
61 | @Override | 66 | @Override |
62 | public void onMsg(TbContext ctx, TbMsg msg) { | 67 | public void onMsg(TbContext ctx, TbMsg msg) { |
63 | JsonObject json = jsonParser.parse(msg.getData()).getAsJsonObject(); | 68 | JsonObject json = jsonParser.parse(msg.getData()).getAsJsonObject(); |
64 | - | 69 | + String tmp; |
65 | if (msg.getOriginator().getEntityType() != EntityType.DEVICE) { | 70 | if (msg.getOriginator().getEntityType() != EntityType.DEVICE) { |
66 | ctx.tellFailure(msg, new RuntimeException("Message originator is not a device entity!")); | 71 | ctx.tellFailure(msg, new RuntimeException("Message originator is not a device entity!")); |
67 | } else if (!json.has("method")) { | 72 | } else if (!json.has("method")) { |
@@ -70,17 +75,31 @@ public class TbSendRPCRequestNode implements TbNode { | @@ -70,17 +75,31 @@ public class TbSendRPCRequestNode implements TbNode { | ||
70 | ctx.tellFailure(msg, new RuntimeException("Params are not present in the message!")); | 75 | ctx.tellFailure(msg, new RuntimeException("Params are not present in the message!")); |
71 | } else { | 76 | } else { |
72 | int requestId = json.has("requestId") ? json.get("requestId").getAsInt() : random.nextInt(); | 77 | int requestId = json.has("requestId") ? json.get("requestId").getAsInt() : random.nextInt(); |
78 | + boolean restApiCall = msg.getType().equals(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE); | ||
79 | + | ||
80 | + tmp = msg.getMetaData().getValue("oneway"); | ||
81 | + boolean oneway = !StringUtils.isEmpty(tmp) && Boolean.parseBoolean(tmp); | ||
82 | + | ||
83 | + tmp = msg.getMetaData().getValue("requestUUID"); | ||
84 | + UUID requestUUID = !StringUtils.isEmpty(tmp) ? UUID.fromString(tmp) : UUIDs.timeBased(); | ||
85 | + | ||
86 | + tmp = msg.getMetaData().getValue("expirationTime"); | ||
87 | + long expirationTime = !StringUtils.isEmpty(tmp) ? Long.parseLong(tmp) : (System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(config.getTimeoutInSeconds())); | ||
88 | + | ||
73 | RuleEngineDeviceRpcRequest request = RuleEngineDeviceRpcRequest.builder() | 89 | RuleEngineDeviceRpcRequest request = RuleEngineDeviceRpcRequest.builder() |
90 | + .oneway(oneway) | ||
74 | .method(json.get("method").getAsString()) | 91 | .method(json.get("method").getAsString()) |
75 | .body(gson.toJson(json.get("params"))) | 92 | .body(gson.toJson(json.get("params"))) |
76 | .deviceId(new DeviceId(msg.getOriginator().getId())) | 93 | .deviceId(new DeviceId(msg.getOriginator().getId())) |
77 | .requestId(requestId) | 94 | .requestId(requestId) |
78 | - .timeout(TimeUnit.SECONDS.toMillis(config.getTimeoutInSeconds())) | 95 | + .requestUUID(requestUUID) |
96 | + .expirationTime(expirationTime) | ||
97 | + .restApiCall(restApiCall) | ||
79 | .build(); | 98 | .build(); |
80 | 99 | ||
81 | ctx.getRpcService().sendRpcRequest(request, ruleEngineDeviceRpcResponse -> { | 100 | ctx.getRpcService().sendRpcRequest(request, ruleEngineDeviceRpcResponse -> { |
82 | if (!ruleEngineDeviceRpcResponse.getError().isPresent()) { | 101 | if (!ruleEngineDeviceRpcResponse.getError().isPresent()) { |
83 | - TbMsg next = ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().get()); | 102 | + TbMsg next = ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}")); |
84 | ctx.tellNext(next, TbRelationTypes.SUCCESS); | 103 | ctx.tellNext(next, TbRelationTypes.SUCCESS); |
85 | } else { | 104 | } else { |
86 | TbMsg next = ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name())); | 105 | TbMsg next = ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name())); |