Commit 85fcfef8a5e45b88f359fe3242dbfc2b242123ec
1 parent
c526d13e
Added support for RPC call for edge devices
Showing
12 changed files
with
237 additions
and
103 deletions
... | ... | @@ -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 | } | ... | ... |
... | ... | @@ -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 | } | ... | ... |