Commit 85fcfef8a5e45b88f359fe3242dbfc2b242123ec

Authored by Volodymyr Babak
1 parent c526d13e

Added support for RPC call for edge devices

... ... @@ -255,12 +255,15 @@ public class ActorSystemContext {
255 255 @Getter
256 256 private TbCoreDeviceRpcService tbCoreDeviceRpcService;
257 257
  258 + @Lazy
258 259 @Autowired(required = false)
259 260 @Getter private EdgeService edgeService;
260 261
  262 + @Lazy
261 263 @Autowired(required = false)
262 264 @Getter private EdgeEventService edgeEventService;
263 265
  266 + @Lazy
264 267 @Autowired(required = false)
265 268 @Getter private EdgeRpcService edgeRpcService;
266 269
... ...
... ... @@ -78,6 +78,7 @@ import org.thingsboard.server.gen.edge.CustomerUpdateMsg;
78 78 import org.thingsboard.server.gen.edge.DashboardUpdateMsg;
79 79 import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg;
80 80 import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
  81 +import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
81 82 import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
82 83 import org.thingsboard.server.gen.edge.DownlinkMsg;
83 84 import org.thingsboard.server.gen.edge.DownlinkResponseMsg;
... ... @@ -333,6 +334,9 @@ public final class EdgeGrpcSession implements Closeable {
333 334 case ENTITY_EXISTS_REQUEST:
334 335 downlinkMsg = processEntityExistsRequestMessage(edgeEvent);
335 336 break;
  337 + case RPC_CALL:
  338 + downlinkMsg = processRpcCallMsg(edgeEvent);
  339 + break;
336 340 }
337 341 if (downlinkMsg != null) {
338 342 result.add(downlinkMsg);
... ... @@ -358,6 +362,15 @@ public final class EdgeGrpcSession implements Closeable {
358 362 return downlinkMsg;
359 363 }
360 364
  365 + private DownlinkMsg processRpcCallMsg(EdgeEvent edgeEvent) {
  366 + log.trace("Executing processRpcCall, edgeEvent [{}]", edgeEvent);
  367 + DeviceRpcCallMsg deviceRpcCallMsg =
  368 + ctx.getDeviceMsgConstructor().constructDeviceRpcCallMsg(edgeEvent.getEntityBody());
  369 + return DownlinkMsg.newBuilder()
  370 + .addAllDeviceRpcCallMsg(Collections.singletonList(deviceRpcCallMsg))
  371 + .build();
  372 + }
  373 +
361 374 private DownlinkMsg processCredentialsRequestMessage(EdgeEvent edgeEvent) {
362 375 DownlinkMsg downlinkMsg = null;
363 376 if (EdgeEventType.DEVICE.equals(edgeEvent.getEdgeEventType())) {
... ... @@ -883,6 +896,11 @@ public final class EdgeGrpcSession implements Closeable {
883 896 result.add(ctx.getSyncEdgeService().processDeviceCredentialsRequestMsg(edge, deviceCredentialsRequestMsg));
884 897 }
885 898 }
  899 + if (uplinkMsg.getDeviceRpcCallMsgList() != null && !uplinkMsg.getDeviceRpcCallMsgList().isEmpty()) {
  900 + for (DeviceRpcCallMsg deviceRpcCallMsg: uplinkMsg.getDeviceRpcCallMsgList()) {
  901 + result.add(ctx.getDeviceProcessor().processDeviceRpcCallResponseMsg(edge.getTenantId(), deviceRpcCallMsg));
  902 + }
  903 + }
886 904 } catch (Exception e) {
887 905 log.error("Can't process uplink msg [{}]", uplinkMsg, e);
888 906 }
... ...
... ... @@ -15,21 +15,27 @@
15 15 */
16 16 package org.thingsboard.server.service.edge.rpc.constructor;
17 17
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.databind.ObjectMapper;
18 20 import lombok.extern.slf4j.Slf4j;
19 21 import org.springframework.stereotype.Component;
  22 +import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest;
20 23 import org.thingsboard.server.common.data.Device;
21 24 import org.thingsboard.server.common.data.id.CustomerId;
22 25 import org.thingsboard.server.common.data.id.DeviceId;
23   -import org.thingsboard.server.common.data.id.EntityId;
24 26 import org.thingsboard.server.common.data.security.DeviceCredentials;
25 27 import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
  28 +import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
26 29 import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
  30 +import org.thingsboard.server.gen.edge.RpcRequestMsg;
27 31 import org.thingsboard.server.gen.edge.UpdateMsgType;
28 32
29 33 @Component
30 34 @Slf4j
31 35 public class DeviceMsgConstructor {
32 36
  37 + protected static final ObjectMapper mapper = new ObjectMapper();
  38 +
33 39 public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device, CustomerId customerId) {
34 40 DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder()
35 41 .setMsgType(msgType)
... ... @@ -67,4 +73,21 @@ public class DeviceMsgConstructor {
67 73 .setIdMSB(deviceId.getId().getMostSignificantBits())
68 74 .setIdLSB(deviceId.getId().getLeastSignificantBits()).build();
69 75 }
  76 +
  77 + public DeviceRpcCallMsg constructDeviceRpcCallMsg(JsonNode body) {
  78 + RuleEngineDeviceRpcRequest request = mapper.convertValue(body, RuleEngineDeviceRpcRequest.class);
  79 + RpcRequestMsg.Builder requestBuilder = RpcRequestMsg.newBuilder();
  80 + requestBuilder.setMethod(request.getMethod());
  81 + requestBuilder.setParams(request.getBody());
  82 + DeviceRpcCallMsg.Builder builder = DeviceRpcCallMsg.newBuilder()
  83 + .setDeviceIdMSB(request.getDeviceId().getId().getMostSignificantBits())
  84 + .setDeviceIdLSB(request.getDeviceId().getId().getLeastSignificantBits())
  85 + .setRequestIdMSB(request.getRequestUUID().getMostSignificantBits())
  86 + .setRequestIdLSB(request.getRequestUUID().getLeastSignificantBits())
  87 + .setExpirationTime(request.getExpirationTime())
  88 + .setOriginServiceId(request.getOriginServiceId())
  89 + .setOneway(request.isOneway())
  90 + .setRequestMsg(requestBuilder.build());
  91 + return builder.build();
  92 + }
70 93 }
... ...
... ... @@ -39,6 +39,7 @@ import org.thingsboard.server.dao.relation.RelationService;
39 39 import org.thingsboard.server.dao.user.UserService;
40 40 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
41 41 import org.thingsboard.server.service.queue.TbClusterService;
  42 +import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
42 43 import org.thingsboard.server.service.state.DeviceStateService;
43 44
44 45 @Slf4j
... ... @@ -47,6 +48,9 @@ public abstract class BaseProcessor {
47 48 protected static final ObjectMapper mapper = new ObjectMapper();
48 49
49 50 @Autowired
  51 + protected TbRuleEngineDeviceRpcService tbDeviceRpcService;
  52 +
  53 + @Autowired
50 54 protected AlarmService alarmService;
51 55
52 56 @Autowired
... ...
... ... @@ -21,7 +21,9 @@ import com.google.common.util.concurrent.Futures;
21 21 import com.google.common.util.concurrent.ListenableFuture;
22 22 import lombok.extern.slf4j.Slf4j;
23 23 import org.apache.commons.lang.RandomStringUtils;
  24 +import org.apache.commons.lang.StringUtils;
24 25 import org.springframework.stereotype.Component;
  26 +import org.thingsboard.rule.engine.api.RpcError;
25 27 import org.thingsboard.server.common.data.DataConstants;
26 28 import org.thingsboard.server.common.data.Device;
27 29 import org.thingsboard.server.common.data.audit.ActionType;
... ... @@ -40,9 +42,11 @@ import org.thingsboard.server.common.msg.TbMsg;
40 42 import org.thingsboard.server.common.msg.TbMsgDataType;
41 43 import org.thingsboard.server.common.msg.TbMsgMetaData;
42 44 import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
  45 +import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
43 46 import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
44 47 import org.thingsboard.server.queue.TbQueueCallback;
45 48 import org.thingsboard.server.queue.TbQueueMsgMetadata;
  49 +import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
46 50
47 51 import java.util.UUID;
48 52 import java.util.concurrent.locks.ReentrantLock;
... ... @@ -213,4 +217,17 @@ public class DeviceProcessor extends BaseProcessor {
213 217 metaData.putValue("edgeName", edge.getName());
214 218 return metaData;
215 219 }
  220 +
  221 + public ListenableFuture<Void> processDeviceRpcCallResponseMsg(TenantId tenantId, DeviceRpcCallMsg deviceRpcCallMsg) {
  222 + UUID uuid = new UUID(deviceRpcCallMsg.getRequestIdMSB(), deviceRpcCallMsg.getRequestIdLSB());
  223 + FromDeviceRpcResponse response;
  224 + if (!StringUtils.isEmpty(deviceRpcCallMsg.getResponseMsg().getError())) {
  225 + response = new FromDeviceRpcResponse(uuid, null, RpcError.valueOf(deviceRpcCallMsg.getResponseMsg().getError()));
  226 + } else {
  227 + response = new FromDeviceRpcResponse(uuid, deviceRpcCallMsg.getResponseMsg().getResponse(), null);
  228 + }
  229 + tbDeviceRpcService.sendRpcResponseToTbCore(deviceRpcCallMsg.getOriginServiceId(), response);
  230 + return Futures.immediateFuture(null);
  231 + }
  232 +
216 233 }
... ...
... ... @@ -151,7 +151,8 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi
151 151 }
152 152 }
153 153
154   - private void sendRpcResponseToTbCore(String originServiceId, FromDeviceRpcResponse response) {
  154 + @Override
  155 + public void sendRpcResponseToTbCore(String originServiceId, FromDeviceRpcResponse response) {
155 156 if (serviceId.equals(originServiceId)) {
156 157 if (tbCoreRpcService.isPresent()) {
157 158 tbCoreRpcService.get().processRpcResponseFromRuleEngine(response);
... ...
... ... @@ -29,4 +29,7 @@ public interface TbRuleEngineDeviceRpcService extends RuleEngineRpcService {
29 29 */
30 30 void processRpcResponseFromDevice(FromDeviceRpcResponse response);
31 31
  32 +
  33 + void sendRpcResponseToTbCore(String originServiceId, FromDeviceRpcResponse response);
  34 +
32 35 }
... ...
... ... @@ -588,7 +588,7 @@ transport:
588 588 # Edges parameters
589 589 edges:
590 590 rpc:
591   - enabled: "${EDGES_RPC_ENABLED:true}"
  591 + enabled: "${EDGES_RPC_ENABLED:false}"
592 592 port: "${EDGES_RPC_PORT:7070}"
593 593 ssl:
594 594 # Enable/disable SSL support
... ...
... ... @@ -322,6 +322,28 @@ message DeviceCredentialsRequestMsg {
322 322 int64 deviceIdLSB = 2;
323 323 }
324 324
  325 +message DeviceRpcCallMsg {
  326 + int64 deviceIdMSB = 1;
  327 + int64 deviceIdLSB = 2;
  328 + int64 requestIdMSB = 3;
  329 + int64 requestIdLSB = 4;
  330 + int64 expirationTime = 5;
  331 + bool oneway = 6;
  332 + string originServiceId = 7;
  333 + RpcRequestMsg requestMsg = 8;
  334 + RpcResponseMsg responseMsg = 9;
  335 +}
  336 +
  337 +message RpcRequestMsg {
  338 + string method = 1;
  339 + string params = 2;
  340 +}
  341 +
  342 +message RpcResponseMsg {
  343 + string response = 1;
  344 + string error = 2;
  345 +}
  346 +
325 347 enum EdgeEntityType {
326 348 DEVICE = 0;
327 349 ASSET = 1;
... ... @@ -343,6 +365,7 @@ message UplinkMsg {
343 365 repeated RelationRequestMsg relationRequestMsg = 9;
344 366 repeated UserCredentialsRequestMsg userCredentialsRequestMsg = 10;
345 367 repeated DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 11;
  368 + repeated DeviceRpcCallMsg deviceRpcCallMsg = 12;
346 369 }
347 370
348 371 message UplinkResponseMsg {
... ... @@ -374,6 +397,6 @@ message DownlinkMsg {
374 397 repeated WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = 16;
375 398 repeated WidgetTypeUpdateMsg widgetTypeUpdateMsg = 17;
376 399 repeated AdminSettingsUpdateMsg adminSettingsUpdateMsg = 18;
377   -
  400 + repeated DeviceRpcCallMsg deviceRpcCallMsg = 19;
378 401 }
379 402
... ...
... ... @@ -425,38 +425,43 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
425 425
426 426 @Override
427 427 public ListenableFuture<List<EdgeId>> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId) {
428   - switch (entityId.getEntityType()) {
429   - case DEVICE:
430   - case ASSET:
431   - case ENTITY_VIEW:
432   - ListenableFuture<List<EntityRelation>> originatorEdgeRelationsFuture =
433   - relationService.findByToAndTypeAsync(tenantId, entityId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
434   - return Futures.transform(originatorEdgeRelationsFuture, originatorEdgeRelations -> {
435   - if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0 &&
436   - originatorEdgeRelations.get(0).getFrom() != null) {
437   - return Collections.singletonList(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId()));
  428 + if (EntityType.TENANT.equals(entityId.getEntityType())) {
  429 + TextPageData<Edge> edgesByTenantId = findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE));
  430 + return Futures.immediateFuture(edgesByTenantId.getData().stream().map(IdBased::getId).collect(Collectors.toList()));
  431 + } else {
  432 + switch (entityId.getEntityType()) {
  433 + case DEVICE:
  434 + case ASSET:
  435 + case ENTITY_VIEW:
  436 + ListenableFuture<List<EntityRelation>> originatorEdgeRelationsFuture =
  437 + relationService.findByToAndTypeAsync(tenantId, entityId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
  438 + return Futures.transform(originatorEdgeRelationsFuture, originatorEdgeRelations -> {
  439 + if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0 &&
  440 + originatorEdgeRelations.get(0).getFrom() != null) {
  441 + return Collections.singletonList(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId()));
  442 + } else {
  443 + return Collections.emptyList();
  444 + }
  445 + }, MoreExecutors.directExecutor());
  446 + case DASHBOARD:
  447 + return convertToEdgeIds(findEdgesByTenantIdAndDashboardId(tenantId, new DashboardId(entityId.getId())));
  448 + case RULE_CHAIN:
  449 + return convertToEdgeIds(findEdgesByTenantIdAndRuleChainId(tenantId, new RuleChainId(entityId.getId())));
  450 + case USER:
  451 + User userById = userService.findUserById(tenantId, new UserId(entityId.getId()));
  452 + if (userById == null) {
  453 + return Futures.immediateFuture(Collections.emptyList());
  454 + }
  455 + TextPageData<Edge> edges;
  456 + if (userById.getCustomerId() == null || userById.getCustomerId().isNullUid()) {
  457 + edges = findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE));
438 458 } else {
439   - return Collections.emptyList();
  459 + edges = findEdgesByTenantIdAndCustomerId(tenantId, new CustomerId(entityId.getId()), new TextPageLink(Integer.MAX_VALUE));
440 460 }
441   - }, MoreExecutors.directExecutor());
442   - case DASHBOARD:
443   - return convertToEdgeIds(findEdgesByTenantIdAndDashboardId(tenantId, new DashboardId(entityId.getId())));
444   - case RULE_CHAIN:
445   - return convertToEdgeIds(findEdgesByTenantIdAndRuleChainId(tenantId, new RuleChainId(entityId.getId())));
446   - case USER:
447   - User userById = userService.findUserById(tenantId, new UserId(entityId.getId()));
448   - if (userById == null) {
  461 + return convertToEdgeIds(Futures.immediateFuture(edges.getData()));
  462 + default:
449 463 return Futures.immediateFuture(Collections.emptyList());
450   - }
451   - TextPageData<Edge> edges;
452   - if (userById.getCustomerId() == null || userById.getCustomerId().isNullUid()) {
453   - edges = findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE));
454   - } else {
455   - edges = findEdgesByTenantIdAndCustomerId(tenantId, new CustomerId(entityId.getId()), new TextPageLink(Integer.MAX_VALUE));
456   - }
457   - return convertToEdgeIds(Futures.immediateFuture(edges.getData()));
458   - default:
459   - return Futures.immediateFuture(Collections.emptyList());
  464 + }
460 465 }
461 466 }
462 467
... ...
... ... @@ -33,29 +33,20 @@ import org.thingsboard.server.common.data.DataConstants;
33 33 import org.thingsboard.server.common.data.EdgeUtils;
34 34 import org.thingsboard.server.common.data.EntityType;
35 35 import org.thingsboard.server.common.data.audit.ActionType;
36   -import org.thingsboard.server.common.data.edge.Edge;
37 36 import org.thingsboard.server.common.data.edge.EdgeEvent;
38 37 import org.thingsboard.server.common.data.edge.EdgeEventType;
39 38 import org.thingsboard.server.common.data.id.EdgeId;
40   -import org.thingsboard.server.common.data.id.EntityId;
41   -import org.thingsboard.server.common.data.id.IdBased;
42 39 import org.thingsboard.server.common.data.id.TenantId;
43   -import org.thingsboard.server.common.data.page.TextPageData;
44   -import org.thingsboard.server.common.data.page.TextPageLink;
45 40 import org.thingsboard.server.common.data.plugin.ComponentType;
46   -import org.thingsboard.server.common.data.relation.EntityRelation;
47   -import org.thingsboard.server.common.data.relation.RelationTypeGroup;
48 41 import org.thingsboard.server.common.data.rule.RuleChainType;
49 42 import org.thingsboard.server.common.msg.TbMsg;
50 43 import org.thingsboard.server.common.msg.session.SessionMsgType;
51 44
52 45 import javax.annotation.Nullable;
53   -import java.util.ArrayList;
54 46 import java.util.HashMap;
55 47 import java.util.List;
56 48 import java.util.Map;
57 49 import java.util.UUID;
58   -import java.util.stream.Collectors;
59 50
60 51 import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
61 52
... ... @@ -86,51 +77,12 @@ public class TbMsgPushToEdgeNode implements TbNode {
86 77 public void onMsg(TbContext ctx, TbMsg msg) {
87 78 if (DataConstants.EDGE_MSG_SOURCE.equalsIgnoreCase(msg.getMetaData().getValue(DataConstants.MSG_SOURCE_KEY))) {
88 79 log.debug("Ignoring msg from the cloud, msg [{}]", msg);
  80 + ctx.ack(msg);
89 81 return;
90 82 }
91 83 if (isSupportedOriginator(msg.getOriginator().getEntityType())) {
92 84 if (isSupportedMsgType(msg.getType())) {
93   - ListenableFuture<List<EdgeId>> getEdgeIdsFuture = getEdgeIdsByOriginatorId(ctx, ctx.getTenantId(), msg.getOriginator());
94   - Futures.addCallback(getEdgeIdsFuture, new FutureCallback<List<EdgeId>>() {
95   - @Override
96   - public void onSuccess(@Nullable List<EdgeId> edgeIds) {
97   - if (edgeIds != null && !edgeIds.isEmpty()) {
98   - for (EdgeId edgeId : edgeIds) {
99   - try {
100   - EdgeEvent edgeEvent = buildEdgeEvent(msg, ctx);
101   - if (edgeEvent == null) {
102   - log.debug("Edge event type is null. Entity Type {}", msg.getOriginator().getEntityType());
103   - ctx.tellFailure(msg, new RuntimeException("Edge event type is null. Entity Type '" + msg.getOriginator().getEntityType() + "'"));
104   - } else {
105   - edgeEvent.setEdgeId(edgeId);
106   - ListenableFuture<EdgeEvent> saveFuture = ctx.getEdgeEventService().saveAsync(edgeEvent);
107   - Futures.addCallback(saveFuture, new FutureCallback<EdgeEvent>() {
108   - @Override
109   - public void onSuccess(@Nullable EdgeEvent event) {
110   - ctx.tellNext(msg, SUCCESS);
111   - }
112   -
113   - @Override
114   - public void onFailure(Throwable th) {
115   - log.error("Could not save edge event", th);
116   - ctx.tellFailure(msg, th);
117   - }
118   - }, ctx.getDbCallbackExecutor());
119   - }
120   - } catch (JsonProcessingException e) {
121   - log.error("Failed to build edge event", e);
122   - ctx.tellFailure(msg, e);
123   - }
124   - }
125   - }
126   - }
127   -
128   - @Override
129   - public void onFailure(Throwable t) {
130   - ctx.tellFailure(msg, t);
131   - }
132   -
133   - }, ctx.getDbCallbackExecutor());
  85 + processMsg(ctx, msg);
134 86 } else {
135 87 log.debug("Unsupported msg type {}", msg.getType());
136 88 ctx.tellFailure(msg, new RuntimeException("Unsupported msg type '" + msg.getType() + "'"));
... ... @@ -141,6 +93,50 @@ public class TbMsgPushToEdgeNode implements TbNode {
141 93 }
142 94 }
143 95
  96 + private void processMsg(TbContext ctx, TbMsg msg) {
  97 + ListenableFuture<List<EdgeId>> getEdgeIdsFuture = ctx.getEdgeService().findRelatedEdgeIdsByEntityId(ctx.getTenantId(), msg.getOriginator());
  98 + Futures.addCallback(getEdgeIdsFuture, new FutureCallback<List<EdgeId>>() {
  99 + @Override
  100 + public void onSuccess(@Nullable List<EdgeId> edgeIds) {
  101 + if (edgeIds != null && !edgeIds.isEmpty()) {
  102 + for (EdgeId edgeId : edgeIds) {
  103 + try {
  104 + EdgeEvent edgeEvent = buildEdgeEvent(msg, ctx);
  105 + if (edgeEvent == null) {
  106 + log.debug("Edge event type is null. Entity Type {}", msg.getOriginator().getEntityType());
  107 + ctx.tellFailure(msg, new RuntimeException("Edge event type is null. Entity Type '" + msg.getOriginator().getEntityType() + "'"));
  108 + } else {
  109 + edgeEvent.setEdgeId(edgeId);
  110 + ListenableFuture<EdgeEvent> saveFuture = ctx.getEdgeEventService().saveAsync(edgeEvent);
  111 + Futures.addCallback(saveFuture, new FutureCallback<EdgeEvent>() {
  112 + @Override
  113 + public void onSuccess(@Nullable EdgeEvent event) {
  114 + ctx.tellNext(msg, SUCCESS);
  115 + }
  116 +
  117 + @Override
  118 + public void onFailure(Throwable th) {
  119 + log.error("Could not save edge event", th);
  120 + ctx.tellFailure(msg, th);
  121 + }
  122 + }, ctx.getDbCallbackExecutor());
  123 + }
  124 + } catch (JsonProcessingException e) {
  125 + log.error("Failed to build edge event", e);
  126 + ctx.tellFailure(msg, e);
  127 + }
  128 + }
  129 + }
  130 + }
  131 +
  132 + @Override
  133 + public void onFailure(Throwable t) {
  134 + ctx.tellFailure(msg, t);
  135 + }
  136 +
  137 + }, ctx.getDbCallbackExecutor());
  138 + }
  139 +
144 140 private EdgeEvent buildEdgeEvent(TbMsg msg, TbContext ctx) throws JsonProcessingException {
145 141 if (DataConstants.ALARM.equals(msg.getType())) {
146 142 return buildEdgeEvent(ctx.getTenantId(), ActionType.ADDED, getUUIDFromMsgData(msg), EdgeEventType.ALARM, null);
... ... @@ -227,15 +223,6 @@ public class TbMsgPushToEdgeNode implements TbNode {
227 223 || DataConstants.ALARM.equals(msgType);
228 224 }
229 225
230   - private ListenableFuture<List<EdgeId>> getEdgeIdsByOriginatorId(TbContext ctx, TenantId tenantId, EntityId originatorId) {
231   - if (EntityType.TENANT.equals(originatorId.getEntityType())) {
232   - TextPageData<Edge> edgesByTenantId = ctx.getEdgeService().findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE));
233   - return Futures.immediateFuture(edgesByTenantId.getData().stream().map(IdBased::getId).collect(Collectors.toList()));
234   - } else {
235   - return ctx.getEdgeService().findRelatedEdgeIdsByEntityId(tenantId, originatorId);
236   - }
237   - }
238   -
239 226 @Override
240 227 public void destroy() {
241 228 }
... ...
... ... @@ -16,13 +16,16 @@
16 16 package org.thingsboard.rule.engine.rpc;
17 17
18 18 import com.datastax.driver.core.utils.UUIDs;
  19 +import com.fasterxml.jackson.databind.ObjectMapper;
  20 +import com.google.common.util.concurrent.FutureCallback;
  21 +import com.google.common.util.concurrent.Futures;
  22 +import com.google.common.util.concurrent.ListenableFuture;
19 23 import com.google.gson.Gson;
20 24 import com.google.gson.JsonElement;
21 25 import com.google.gson.JsonObject;
22 26 import com.google.gson.JsonParser;
23 27 import lombok.extern.slf4j.Slf4j;
24 28 import org.springframework.util.StringUtils;
25   -import org.thingsboard.rule.engine.api.util.TbNodeUtils;
26 29 import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest;
27 30 import org.thingsboard.rule.engine.api.RuleNode;
28 31 import org.thingsboard.rule.engine.api.TbContext;
... ... @@ -30,13 +33,21 @@ import org.thingsboard.rule.engine.api.TbNode;
30 33 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
31 34 import org.thingsboard.rule.engine.api.TbNodeException;
32 35 import org.thingsboard.rule.engine.api.TbRelationTypes;
  36 +import org.thingsboard.rule.engine.api.util.TbNodeUtils;
33 37 import org.thingsboard.server.common.data.DataConstants;
34 38 import org.thingsboard.server.common.data.EntityType;
  39 +import org.thingsboard.server.common.data.audit.ActionType;
  40 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  41 +import org.thingsboard.server.common.data.edge.EdgeEventType;
35 42 import org.thingsboard.server.common.data.id.DeviceId;
  43 +import org.thingsboard.server.common.data.id.EdgeId;
36 44 import org.thingsboard.server.common.data.plugin.ComponentType;
37   -import org.thingsboard.server.common.data.rule.RuleChainType;
  45 +import org.thingsboard.server.common.data.relation.EntityRelation;
  46 +import org.thingsboard.server.common.data.relation.RelationTypeGroup;
38 47 import org.thingsboard.server.common.msg.TbMsg;
39 48
  49 +import javax.annotation.Nullable;
  50 +import java.util.List;
40 51 import java.util.Random;
41 52 import java.util.UUID;
42 53 import java.util.concurrent.TimeUnit;
... ... @@ -55,6 +66,7 @@ import java.util.concurrent.TimeUnit;
55 66 )
56 67 public class TbSendRPCRequestNode implements TbNode {
57 68
  69 + private static final ObjectMapper json = new ObjectMapper();
58 70 private Random random = new Random();
59 71 private Gson gson = new Gson();
60 72 private JsonParser jsonParser = new JsonParser();
... ... @@ -111,19 +123,57 @@ public class TbSendRPCRequestNode implements TbNode {
111 123 .restApiCall(restApiCall)
112 124 .build();
113 125
114   - ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> {
115   - if (!ruleEngineDeviceRpcResponse.getError().isPresent()) {
116   - TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}"));
117   - ctx.enqueueForTellNext(next, TbRelationTypes.SUCCESS);
118   - } else {
119   - TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name()));
120   - ctx.tellFailure(next, new RuntimeException(ruleEngineDeviceRpcResponse.getError().get().name()));
121   - }
122   - });
  126 + EdgeId edgeId = findRelatedEdgeId(ctx, msg);
  127 + if (edgeId != null) {
  128 + sendRpcRequestToEdgeDevice(ctx, msg, edgeId, request);
  129 + } else {
  130 + ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> {
  131 + if (!ruleEngineDeviceRpcResponse.getError().isPresent()) {
  132 + TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}"));
  133 + ctx.enqueueForTellNext(next, TbRelationTypes.SUCCESS);
  134 + } else {
  135 + TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name()));
  136 + ctx.tellFailure(next, new RuntimeException(ruleEngineDeviceRpcResponse.getError().get().name()));
  137 + }
  138 + });
  139 + }
123 140 ctx.ack(msg);
124 141 }
125 142 }
126 143
  144 + private EdgeId findRelatedEdgeId(TbContext ctx, TbMsg msg) {
  145 + List<EntityRelation> result =
  146 + ctx.getRelationService().findByToAndType(ctx.getTenantId(), msg.getOriginator(), EntityRelation.EDGE_TYPE, RelationTypeGroup.COMMON);
  147 + if (result != null && result.size() > 0) {
  148 + return new EdgeId(result.get(0).getFrom().getId());
  149 + } else {
  150 + return null;
  151 + }
  152 + }
  153 +
  154 + private void sendRpcRequestToEdgeDevice(TbContext ctx, TbMsg msg, EdgeId edgeId, RuleEngineDeviceRpcRequest request) {
  155 + EdgeEvent edgeEvent = new EdgeEvent();
  156 + edgeEvent.setTenantId(ctx.getTenantId());
  157 + edgeEvent.setEdgeEventAction(ActionType.RPC_CALL.name());
  158 + edgeEvent.setEntityId(request.getDeviceId().getId());
  159 + edgeEvent.setEdgeEventType(EdgeEventType.DEVICE);
  160 + edgeEvent.setEntityBody(json.valueToTree(request));
  161 + edgeEvent.setEdgeId(edgeId);
  162 + ListenableFuture<EdgeEvent> saveFuture = ctx.getEdgeEventService().saveAsync(edgeEvent);
  163 + Futures.addCallback(saveFuture, new FutureCallback<EdgeEvent>() {
  164 + @Override
  165 + public void onSuccess(@Nullable EdgeEvent event) {
  166 + ctx.tellSuccess(msg);
  167 + }
  168 +
  169 + @Override
  170 + public void onFailure(Throwable th) {
  171 + log.error("Could not save edge event", th);
  172 + ctx.tellFailure(msg, th);
  173 + }
  174 + }, ctx.getDbCallbackExecutor());
  175 + }
  176 +
127 177 @Override
128 178 public void destroy() {
129 179 }
... ...