Added support for RPC call for edge devices
This commit is contained in:
		
							parent
							
								
									c526d13e45
								
							
						
					
					
						commit
						85fcfef8a5
					
				@ -255,12 +255,15 @@ public class ActorSystemContext {
 | 
			
		||||
    @Getter
 | 
			
		||||
    private TbCoreDeviceRpcService tbCoreDeviceRpcService;
 | 
			
		||||
 | 
			
		||||
    @Lazy
 | 
			
		||||
    @Autowired(required = false)
 | 
			
		||||
    @Getter private EdgeService edgeService;
 | 
			
		||||
 | 
			
		||||
    @Lazy
 | 
			
		||||
    @Autowired(required = false)
 | 
			
		||||
    @Getter private EdgeEventService edgeEventService;
 | 
			
		||||
 | 
			
		||||
    @Lazy
 | 
			
		||||
    @Autowired(required = false)
 | 
			
		||||
    @Getter private EdgeRpcService edgeRpcService;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -78,6 +78,7 @@ import org.thingsboard.server.gen.edge.CustomerUpdateMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DashboardUpdateMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DownlinkMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DownlinkResponseMsg;
 | 
			
		||||
@ -333,6 +334,9 @@ public final class EdgeGrpcSession implements Closeable {
 | 
			
		||||
                    case ENTITY_EXISTS_REQUEST:
 | 
			
		||||
                        downlinkMsg = processEntityExistsRequestMessage(edgeEvent);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case RPC_CALL:
 | 
			
		||||
                        downlinkMsg = processRpcCallMsg(edgeEvent);
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
                if (downlinkMsg != null) {
 | 
			
		||||
                    result.add(downlinkMsg);
 | 
			
		||||
@ -358,6 +362,15 @@ public final class EdgeGrpcSession implements Closeable {
 | 
			
		||||
        return downlinkMsg;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private DownlinkMsg processRpcCallMsg(EdgeEvent edgeEvent) {
 | 
			
		||||
        log.trace("Executing processRpcCall, edgeEvent [{}]", edgeEvent);
 | 
			
		||||
        DeviceRpcCallMsg deviceRpcCallMsg =
 | 
			
		||||
                ctx.getDeviceMsgConstructor().constructDeviceRpcCallMsg(edgeEvent.getEntityBody());
 | 
			
		||||
        return DownlinkMsg.newBuilder()
 | 
			
		||||
                .addAllDeviceRpcCallMsg(Collections.singletonList(deviceRpcCallMsg))
 | 
			
		||||
                .build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private DownlinkMsg processCredentialsRequestMessage(EdgeEvent edgeEvent) {
 | 
			
		||||
        DownlinkMsg downlinkMsg = null;
 | 
			
		||||
        if (EdgeEventType.DEVICE.equals(edgeEvent.getEdgeEventType())) {
 | 
			
		||||
@ -883,6 +896,11 @@ public final class EdgeGrpcSession implements Closeable {
 | 
			
		||||
                    result.add(ctx.getSyncEdgeService().processDeviceCredentialsRequestMsg(edge, deviceCredentialsRequestMsg));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (uplinkMsg.getDeviceRpcCallMsgList() != null && !uplinkMsg.getDeviceRpcCallMsgList().isEmpty()) {
 | 
			
		||||
                for (DeviceRpcCallMsg deviceRpcCallMsg: uplinkMsg.getDeviceRpcCallMsgList()) {
 | 
			
		||||
                    result.add(ctx.getDeviceProcessor().processDeviceRpcCallResponseMsg(edge.getTenantId(), deviceRpcCallMsg));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.error("Can't process uplink msg [{}]", uplinkMsg, e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -15,21 +15,27 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.service.edge.rpc.constructor;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.JsonNode;
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest;
 | 
			
		||||
import org.thingsboard.server.common.data.Device;
 | 
			
		||||
import org.thingsboard.server.common.data.id.CustomerId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DeviceId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
import org.thingsboard.server.common.data.security.DeviceCredentials;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.RpcRequestMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.UpdateMsgType;
 | 
			
		||||
 | 
			
		||||
@Component
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class DeviceMsgConstructor {
 | 
			
		||||
 | 
			
		||||
    protected static final ObjectMapper mapper = new ObjectMapper();
 | 
			
		||||
 | 
			
		||||
    public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device, CustomerId customerId) {
 | 
			
		||||
        DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder()
 | 
			
		||||
                .setMsgType(msgType)
 | 
			
		||||
@ -67,4 +73,21 @@ public class DeviceMsgConstructor {
 | 
			
		||||
                .setIdMSB(deviceId.getId().getMostSignificantBits())
 | 
			
		||||
                .setIdLSB(deviceId.getId().getLeastSignificantBits()).build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public DeviceRpcCallMsg constructDeviceRpcCallMsg(JsonNode body) {
 | 
			
		||||
        RuleEngineDeviceRpcRequest request = mapper.convertValue(body, RuleEngineDeviceRpcRequest.class);
 | 
			
		||||
        RpcRequestMsg.Builder requestBuilder = RpcRequestMsg.newBuilder();
 | 
			
		||||
        requestBuilder.setMethod(request.getMethod());
 | 
			
		||||
        requestBuilder.setParams(request.getBody());
 | 
			
		||||
        DeviceRpcCallMsg.Builder builder = DeviceRpcCallMsg.newBuilder()
 | 
			
		||||
                .setDeviceIdMSB(request.getDeviceId().getId().getMostSignificantBits())
 | 
			
		||||
                .setDeviceIdLSB(request.getDeviceId().getId().getLeastSignificantBits())
 | 
			
		||||
                .setRequestIdMSB(request.getRequestUUID().getMostSignificantBits())
 | 
			
		||||
                .setRequestIdLSB(request.getRequestUUID().getLeastSignificantBits())
 | 
			
		||||
                .setExpirationTime(request.getExpirationTime())
 | 
			
		||||
                .setOriginServiceId(request.getOriginServiceId())
 | 
			
		||||
                .setOneway(request.isOneway())
 | 
			
		||||
                .setRequestMsg(requestBuilder.build());
 | 
			
		||||
        return builder.build();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -39,6 +39,7 @@ import org.thingsboard.server.dao.relation.RelationService;
 | 
			
		||||
import org.thingsboard.server.dao.user.UserService;
 | 
			
		||||
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
 | 
			
		||||
import org.thingsboard.server.service.queue.TbClusterService;
 | 
			
		||||
import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
 | 
			
		||||
import org.thingsboard.server.service.state.DeviceStateService;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
@ -46,6 +47,9 @@ public abstract class BaseProcessor {
 | 
			
		||||
 | 
			
		||||
    protected static final ObjectMapper mapper = new ObjectMapper();
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    protected TbRuleEngineDeviceRpcService tbDeviceRpcService;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    protected AlarmService alarmService;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,9 @@ import com.google.common.util.concurrent.Futures;
 | 
			
		||||
import com.google.common.util.concurrent.ListenableFuture;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.apache.commons.lang.RandomStringUtils;
 | 
			
		||||
import org.apache.commons.lang.StringUtils;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.rule.engine.api.RpcError;
 | 
			
		||||
import org.thingsboard.server.common.data.DataConstants;
 | 
			
		||||
import org.thingsboard.server.common.data.Device;
 | 
			
		||||
import org.thingsboard.server.common.data.audit.ActionType;
 | 
			
		||||
@ -40,9 +42,11 @@ import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsgDataType;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
 | 
			
		||||
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueCallback;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueMsgMetadata;
 | 
			
		||||
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
 | 
			
		||||
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.concurrent.locks.ReentrantLock;
 | 
			
		||||
@ -213,4 +217,17 @@ public class DeviceProcessor extends BaseProcessor {
 | 
			
		||||
        metaData.putValue("edgeName", edge.getName());
 | 
			
		||||
        return metaData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ListenableFuture<Void> processDeviceRpcCallResponseMsg(TenantId tenantId, DeviceRpcCallMsg deviceRpcCallMsg) {
 | 
			
		||||
        UUID uuid = new UUID(deviceRpcCallMsg.getRequestIdMSB(), deviceRpcCallMsg.getRequestIdLSB());
 | 
			
		||||
        FromDeviceRpcResponse response;
 | 
			
		||||
        if (!StringUtils.isEmpty(deviceRpcCallMsg.getResponseMsg().getError())) {
 | 
			
		||||
            response = new FromDeviceRpcResponse(uuid, null, RpcError.valueOf(deviceRpcCallMsg.getResponseMsg().getError()));
 | 
			
		||||
        } else {
 | 
			
		||||
            response = new FromDeviceRpcResponse(uuid, deviceRpcCallMsg.getResponseMsg().getResponse(), null);
 | 
			
		||||
        }
 | 
			
		||||
        tbDeviceRpcService.sendRpcResponseToTbCore(deviceRpcCallMsg.getOriginServiceId(), response);
 | 
			
		||||
        return Futures.immediateFuture(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -151,7 +151,8 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void sendRpcResponseToTbCore(String originServiceId, FromDeviceRpcResponse response) {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void sendRpcResponseToTbCore(String originServiceId, FromDeviceRpcResponse response) {
 | 
			
		||||
        if (serviceId.equals(originServiceId)) {
 | 
			
		||||
            if (tbCoreRpcService.isPresent()) {
 | 
			
		||||
                tbCoreRpcService.get().processRpcResponseFromRuleEngine(response);
 | 
			
		||||
 | 
			
		||||
@ -29,4 +29,7 @@ public interface TbRuleEngineDeviceRpcService extends RuleEngineRpcService {
 | 
			
		||||
     */
 | 
			
		||||
    void processRpcResponseFromDevice(FromDeviceRpcResponse response);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void sendRpcResponseToTbCore(String originServiceId, FromDeviceRpcResponse response);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -588,7 +588,7 @@ transport:
 | 
			
		||||
# Edges parameters
 | 
			
		||||
edges:
 | 
			
		||||
  rpc:
 | 
			
		||||
    enabled: "${EDGES_RPC_ENABLED:true}"
 | 
			
		||||
    enabled: "${EDGES_RPC_ENABLED:false}"
 | 
			
		||||
    port: "${EDGES_RPC_PORT:7070}"
 | 
			
		||||
    ssl:
 | 
			
		||||
      # Enable/disable SSL support
 | 
			
		||||
 | 
			
		||||
@ -322,6 +322,28 @@ message DeviceCredentialsRequestMsg {
 | 
			
		||||
  int64 deviceIdLSB = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message DeviceRpcCallMsg {
 | 
			
		||||
  int64 deviceIdMSB = 1;
 | 
			
		||||
  int64 deviceIdLSB = 2;
 | 
			
		||||
  int64 requestIdMSB = 3;
 | 
			
		||||
  int64 requestIdLSB = 4;
 | 
			
		||||
  int64 expirationTime = 5;
 | 
			
		||||
  bool oneway = 6;
 | 
			
		||||
  string originServiceId = 7;
 | 
			
		||||
  RpcRequestMsg requestMsg = 8;
 | 
			
		||||
  RpcResponseMsg responseMsg = 9;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message RpcRequestMsg {
 | 
			
		||||
  string method = 1;
 | 
			
		||||
  string params = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message RpcResponseMsg {
 | 
			
		||||
  string response = 1;
 | 
			
		||||
  string error = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum EdgeEntityType {
 | 
			
		||||
  DEVICE = 0;
 | 
			
		||||
  ASSET = 1;
 | 
			
		||||
@ -343,6 +365,7 @@ message UplinkMsg {
 | 
			
		||||
  repeated RelationRequestMsg relationRequestMsg = 9;
 | 
			
		||||
  repeated UserCredentialsRequestMsg userCredentialsRequestMsg = 10;
 | 
			
		||||
  repeated DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 11;
 | 
			
		||||
  repeated DeviceRpcCallMsg deviceRpcCallMsg = 12;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message UplinkResponseMsg {
 | 
			
		||||
@ -374,6 +397,6 @@ message DownlinkMsg {
 | 
			
		||||
  repeated WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = 16;
 | 
			
		||||
  repeated WidgetTypeUpdateMsg widgetTypeUpdateMsg = 17;
 | 
			
		||||
  repeated AdminSettingsUpdateMsg adminSettingsUpdateMsg = 18;
 | 
			
		||||
 | 
			
		||||
  repeated DeviceRpcCallMsg deviceRpcCallMsg = 19;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -425,38 +425,43 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public ListenableFuture<List<EdgeId>> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId) {
 | 
			
		||||
        switch (entityId.getEntityType()) {
 | 
			
		||||
            case DEVICE:
 | 
			
		||||
            case ASSET:
 | 
			
		||||
            case ENTITY_VIEW:
 | 
			
		||||
                ListenableFuture<List<EntityRelation>> originatorEdgeRelationsFuture =
 | 
			
		||||
                        relationService.findByToAndTypeAsync(tenantId, entityId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
 | 
			
		||||
                return Futures.transform(originatorEdgeRelationsFuture, originatorEdgeRelations -> {
 | 
			
		||||
                    if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0 &&
 | 
			
		||||
                            originatorEdgeRelations.get(0).getFrom() != null) {
 | 
			
		||||
                        return Collections.singletonList(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId()));
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return Collections.emptyList();
 | 
			
		||||
        if (EntityType.TENANT.equals(entityId.getEntityType())) {
 | 
			
		||||
            TextPageData<Edge> edgesByTenantId = findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE));
 | 
			
		||||
            return Futures.immediateFuture(edgesByTenantId.getData().stream().map(IdBased::getId).collect(Collectors.toList()));
 | 
			
		||||
        } else {
 | 
			
		||||
            switch (entityId.getEntityType()) {
 | 
			
		||||
                case DEVICE:
 | 
			
		||||
                case ASSET:
 | 
			
		||||
                case ENTITY_VIEW:
 | 
			
		||||
                    ListenableFuture<List<EntityRelation>> originatorEdgeRelationsFuture =
 | 
			
		||||
                            relationService.findByToAndTypeAsync(tenantId, entityId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
 | 
			
		||||
                    return Futures.transform(originatorEdgeRelationsFuture, originatorEdgeRelations -> {
 | 
			
		||||
                        if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0 &&
 | 
			
		||||
                                originatorEdgeRelations.get(0).getFrom() != null) {
 | 
			
		||||
                            return Collections.singletonList(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId()));
 | 
			
		||||
                        } else {
 | 
			
		||||
                            return Collections.emptyList();
 | 
			
		||||
                        }
 | 
			
		||||
                    }, MoreExecutors.directExecutor());
 | 
			
		||||
                case DASHBOARD:
 | 
			
		||||
                    return convertToEdgeIds(findEdgesByTenantIdAndDashboardId(tenantId, new DashboardId(entityId.getId())));
 | 
			
		||||
                case RULE_CHAIN:
 | 
			
		||||
                    return convertToEdgeIds(findEdgesByTenantIdAndRuleChainId(tenantId, new RuleChainId(entityId.getId())));
 | 
			
		||||
                case USER:
 | 
			
		||||
                    User userById = userService.findUserById(tenantId, new UserId(entityId.getId()));
 | 
			
		||||
                    if (userById == null) {
 | 
			
		||||
                        return Futures.immediateFuture(Collections.emptyList());
 | 
			
		||||
                    }
 | 
			
		||||
                }, MoreExecutors.directExecutor());
 | 
			
		||||
            case DASHBOARD:
 | 
			
		||||
                return convertToEdgeIds(findEdgesByTenantIdAndDashboardId(tenantId, new DashboardId(entityId.getId())));
 | 
			
		||||
            case RULE_CHAIN:
 | 
			
		||||
                return convertToEdgeIds(findEdgesByTenantIdAndRuleChainId(tenantId, new RuleChainId(entityId.getId())));
 | 
			
		||||
            case USER:
 | 
			
		||||
                User userById = userService.findUserById(tenantId, new UserId(entityId.getId()));
 | 
			
		||||
                if (userById == null) {
 | 
			
		||||
                    TextPageData<Edge> edges;
 | 
			
		||||
                    if (userById.getCustomerId() == null || userById.getCustomerId().isNullUid()) {
 | 
			
		||||
                        edges = findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE));
 | 
			
		||||
                    } else {
 | 
			
		||||
                        edges = findEdgesByTenantIdAndCustomerId(tenantId, new CustomerId(entityId.getId()), new TextPageLink(Integer.MAX_VALUE));
 | 
			
		||||
                    }
 | 
			
		||||
                    return convertToEdgeIds(Futures.immediateFuture(edges.getData()));
 | 
			
		||||
                default:
 | 
			
		||||
                    return Futures.immediateFuture(Collections.emptyList());
 | 
			
		||||
                }
 | 
			
		||||
                TextPageData<Edge> edges;
 | 
			
		||||
                if (userById.getCustomerId() == null || userById.getCustomerId().isNullUid()) {
 | 
			
		||||
                    edges = findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE));
 | 
			
		||||
                } else {
 | 
			
		||||
                    edges = findEdgesByTenantIdAndCustomerId(tenantId, new CustomerId(entityId.getId()), new TextPageLink(Integer.MAX_VALUE));
 | 
			
		||||
                }
 | 
			
		||||
                return convertToEdgeIds(Futures.immediateFuture(edges.getData()));
 | 
			
		||||
            default:
 | 
			
		||||
                return Futures.immediateFuture(Collections.emptyList());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -33,29 +33,20 @@ import org.thingsboard.server.common.data.DataConstants;
 | 
			
		||||
import org.thingsboard.server.common.data.EdgeUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.audit.ActionType;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.Edge;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.EdgeEventType;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EdgeId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.IdBased;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.page.TextPageData;
 | 
			
		||||
import org.thingsboard.server.common.data.page.TextPageLink;
 | 
			
		||||
import org.thingsboard.server.common.data.plugin.ComponentType;
 | 
			
		||||
import org.thingsboard.server.common.data.relation.EntityRelation;
 | 
			
		||||
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
 | 
			
		||||
import org.thingsboard.server.common.data.rule.RuleChainType;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
import org.thingsboard.server.common.msg.session.SessionMsgType;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
 | 
			
		||||
 | 
			
		||||
@ -86,51 +77,12 @@ public class TbMsgPushToEdgeNode implements TbNode {
 | 
			
		||||
    public void onMsg(TbContext ctx, TbMsg msg) {
 | 
			
		||||
        if (DataConstants.EDGE_MSG_SOURCE.equalsIgnoreCase(msg.getMetaData().getValue(DataConstants.MSG_SOURCE_KEY))) {
 | 
			
		||||
            log.debug("Ignoring msg from the cloud, msg [{}]", msg);
 | 
			
		||||
            ctx.ack(msg);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (isSupportedOriginator(msg.getOriginator().getEntityType())) {
 | 
			
		||||
            if (isSupportedMsgType(msg.getType())) {
 | 
			
		||||
                ListenableFuture<List<EdgeId>> getEdgeIdsFuture = getEdgeIdsByOriginatorId(ctx, ctx.getTenantId(), msg.getOriginator());
 | 
			
		||||
                Futures.addCallback(getEdgeIdsFuture, new FutureCallback<List<EdgeId>>() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void onSuccess(@Nullable List<EdgeId> edgeIds) {
 | 
			
		||||
                        if (edgeIds != null && !edgeIds.isEmpty()) {
 | 
			
		||||
                            for (EdgeId edgeId : edgeIds) {
 | 
			
		||||
                                try {
 | 
			
		||||
                                    EdgeEvent edgeEvent = buildEdgeEvent(msg, ctx);
 | 
			
		||||
                                    if (edgeEvent == null) {
 | 
			
		||||
                                        log.debug("Edge event type is null. Entity Type {}", msg.getOriginator().getEntityType());
 | 
			
		||||
                                        ctx.tellFailure(msg, new RuntimeException("Edge event type is null. Entity Type '" + msg.getOriginator().getEntityType() + "'"));
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        edgeEvent.setEdgeId(edgeId);
 | 
			
		||||
                                        ListenableFuture<EdgeEvent> saveFuture = ctx.getEdgeEventService().saveAsync(edgeEvent);
 | 
			
		||||
                                        Futures.addCallback(saveFuture, new FutureCallback<EdgeEvent>() {
 | 
			
		||||
                                            @Override
 | 
			
		||||
                                            public void onSuccess(@Nullable EdgeEvent event) {
 | 
			
		||||
                                                ctx.tellNext(msg, SUCCESS);
 | 
			
		||||
                                            }
 | 
			
		||||
 | 
			
		||||
                                            @Override
 | 
			
		||||
                                            public void onFailure(Throwable th) {
 | 
			
		||||
                                                log.error("Could not save edge event", th);
 | 
			
		||||
                                                ctx.tellFailure(msg, th);
 | 
			
		||||
                                            }
 | 
			
		||||
                                        }, ctx.getDbCallbackExecutor());
 | 
			
		||||
                                    }
 | 
			
		||||
                                } catch (JsonProcessingException e) {
 | 
			
		||||
                                    log.error("Failed to build edge event", e);
 | 
			
		||||
                                    ctx.tellFailure(msg, e);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void onFailure(Throwable t) {
 | 
			
		||||
                        ctx.tellFailure(msg, t);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }, ctx.getDbCallbackExecutor());
 | 
			
		||||
                processMsg(ctx, msg);
 | 
			
		||||
            } else {
 | 
			
		||||
                log.debug("Unsupported msg type {}", msg.getType());
 | 
			
		||||
                ctx.tellFailure(msg, new RuntimeException("Unsupported msg type '" + msg.getType() + "'"));
 | 
			
		||||
@ -141,6 +93,50 @@ public class TbMsgPushToEdgeNode implements TbNode {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void processMsg(TbContext ctx, TbMsg msg) {
 | 
			
		||||
        ListenableFuture<List<EdgeId>> getEdgeIdsFuture = ctx.getEdgeService().findRelatedEdgeIdsByEntityId(ctx.getTenantId(), msg.getOriginator());
 | 
			
		||||
        Futures.addCallback(getEdgeIdsFuture, new FutureCallback<List<EdgeId>>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onSuccess(@Nullable List<EdgeId> edgeIds) {
 | 
			
		||||
                if (edgeIds != null && !edgeIds.isEmpty()) {
 | 
			
		||||
                    for (EdgeId edgeId : edgeIds) {
 | 
			
		||||
                        try {
 | 
			
		||||
                            EdgeEvent edgeEvent = buildEdgeEvent(msg, ctx);
 | 
			
		||||
                            if (edgeEvent == null) {
 | 
			
		||||
                                log.debug("Edge event type is null. Entity Type {}", msg.getOriginator().getEntityType());
 | 
			
		||||
                                ctx.tellFailure(msg, new RuntimeException("Edge event type is null. Entity Type '" + msg.getOriginator().getEntityType() + "'"));
 | 
			
		||||
                            } else {
 | 
			
		||||
                                edgeEvent.setEdgeId(edgeId);
 | 
			
		||||
                                ListenableFuture<EdgeEvent> saveFuture = ctx.getEdgeEventService().saveAsync(edgeEvent);
 | 
			
		||||
                                Futures.addCallback(saveFuture, new FutureCallback<EdgeEvent>() {
 | 
			
		||||
                                    @Override
 | 
			
		||||
                                    public void onSuccess(@Nullable EdgeEvent event) {
 | 
			
		||||
                                        ctx.tellNext(msg, SUCCESS);
 | 
			
		||||
                                    }
 | 
			
		||||
 | 
			
		||||
                                    @Override
 | 
			
		||||
                                    public void onFailure(Throwable th) {
 | 
			
		||||
                                        log.error("Could not save edge event", th);
 | 
			
		||||
                                        ctx.tellFailure(msg, th);
 | 
			
		||||
                                    }
 | 
			
		||||
                                }, ctx.getDbCallbackExecutor());
 | 
			
		||||
                            }
 | 
			
		||||
                        } catch (JsonProcessingException e) {
 | 
			
		||||
                            log.error("Failed to build edge event", e);
 | 
			
		||||
                            ctx.tellFailure(msg, e);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onFailure(Throwable t) {
 | 
			
		||||
                ctx.tellFailure(msg, t);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }, ctx.getDbCallbackExecutor());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private EdgeEvent buildEdgeEvent(TbMsg msg, TbContext ctx) throws JsonProcessingException {
 | 
			
		||||
        if (DataConstants.ALARM.equals(msg.getType())) {
 | 
			
		||||
            return buildEdgeEvent(ctx.getTenantId(), ActionType.ADDED, getUUIDFromMsgData(msg), EdgeEventType.ALARM, null);
 | 
			
		||||
@ -227,15 +223,6 @@ public class TbMsgPushToEdgeNode implements TbNode {
 | 
			
		||||
                || DataConstants.ALARM.equals(msgType);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ListenableFuture<List<EdgeId>> getEdgeIdsByOriginatorId(TbContext ctx, TenantId tenantId, EntityId originatorId) {
 | 
			
		||||
        if (EntityType.TENANT.equals(originatorId.getEntityType())) {
 | 
			
		||||
            TextPageData<Edge> edgesByTenantId = ctx.getEdgeService().findEdgesByTenantId(tenantId, new TextPageLink(Integer.MAX_VALUE));
 | 
			
		||||
            return Futures.immediateFuture(edgesByTenantId.getData().stream().map(IdBased::getId).collect(Collectors.toList()));
 | 
			
		||||
        } else {
 | 
			
		||||
            return ctx.getEdgeService().findRelatedEdgeIdsByEntityId(tenantId, originatorId);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void destroy() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -16,13 +16,16 @@
 | 
			
		||||
package org.thingsboard.rule.engine.rpc;
 | 
			
		||||
 | 
			
		||||
import com.datastax.driver.core.utils.UUIDs;
 | 
			
		||||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import com.google.common.util.concurrent.FutureCallback;
 | 
			
		||||
import com.google.common.util.concurrent.Futures;
 | 
			
		||||
import com.google.common.util.concurrent.ListenableFuture;
 | 
			
		||||
import com.google.gson.Gson;
 | 
			
		||||
import com.google.gson.JsonElement;
 | 
			
		||||
import com.google.gson.JsonObject;
 | 
			
		||||
import com.google.gson.JsonParser;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.util.StringUtils;
 | 
			
		||||
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
 | 
			
		||||
import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest;
 | 
			
		||||
import org.thingsboard.rule.engine.api.RuleNode;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbContext;
 | 
			
		||||
@ -30,13 +33,21 @@ import org.thingsboard.rule.engine.api.TbNode;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbRelationTypes;
 | 
			
		||||
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.DataConstants;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.audit.ActionType;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.EdgeEventType;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DeviceId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EdgeId;
 | 
			
		||||
import org.thingsboard.server.common.data.plugin.ComponentType;
 | 
			
		||||
import org.thingsboard.server.common.data.rule.RuleChainType;
 | 
			
		||||
import org.thingsboard.server.common.data.relation.EntityRelation;
 | 
			
		||||
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
@ -55,6 +66,7 @@ import java.util.concurrent.TimeUnit;
 | 
			
		||||
)
 | 
			
		||||
public class TbSendRPCRequestNode implements TbNode {
 | 
			
		||||
 | 
			
		||||
    private static final ObjectMapper json = new ObjectMapper();
 | 
			
		||||
    private Random random = new Random();
 | 
			
		||||
    private Gson gson = new Gson();
 | 
			
		||||
    private JsonParser jsonParser = new JsonParser();
 | 
			
		||||
@ -111,19 +123,57 @@ public class TbSendRPCRequestNode implements TbNode {
 | 
			
		||||
                    .restApiCall(restApiCall)
 | 
			
		||||
                    .build();
 | 
			
		||||
 | 
			
		||||
            ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> {
 | 
			
		||||
                if (!ruleEngineDeviceRpcResponse.getError().isPresent()) {
 | 
			
		||||
                    TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}"));
 | 
			
		||||
                    ctx.enqueueForTellNext(next, TbRelationTypes.SUCCESS);
 | 
			
		||||
                } else {
 | 
			
		||||
                    TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name()));
 | 
			
		||||
                    ctx.tellFailure(next, new RuntimeException(ruleEngineDeviceRpcResponse.getError().get().name()));
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            EdgeId edgeId = findRelatedEdgeId(ctx, msg);
 | 
			
		||||
            if (edgeId != null) {
 | 
			
		||||
                sendRpcRequestToEdgeDevice(ctx, msg, edgeId, request);
 | 
			
		||||
            } else {
 | 
			
		||||
                ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> {
 | 
			
		||||
                    if (!ruleEngineDeviceRpcResponse.getError().isPresent()) {
 | 
			
		||||
                        TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}"));
 | 
			
		||||
                        ctx.enqueueForTellNext(next, TbRelationTypes.SUCCESS);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name()));
 | 
			
		||||
                        ctx.tellFailure(next, new RuntimeException(ruleEngineDeviceRpcResponse.getError().get().name()));
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            ctx.ack(msg);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private EdgeId findRelatedEdgeId(TbContext ctx, TbMsg msg) {
 | 
			
		||||
        List<EntityRelation> result =
 | 
			
		||||
                ctx.getRelationService().findByToAndType(ctx.getTenantId(), msg.getOriginator(), EntityRelation.EDGE_TYPE, RelationTypeGroup.COMMON);
 | 
			
		||||
        if (result != null && result.size() > 0) {
 | 
			
		||||
            return new EdgeId(result.get(0).getFrom().getId());
 | 
			
		||||
        } else {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void sendRpcRequestToEdgeDevice(TbContext ctx, TbMsg msg, EdgeId edgeId, RuleEngineDeviceRpcRequest request) {
 | 
			
		||||
        EdgeEvent edgeEvent = new EdgeEvent();
 | 
			
		||||
        edgeEvent.setTenantId(ctx.getTenantId());
 | 
			
		||||
        edgeEvent.setEdgeEventAction(ActionType.RPC_CALL.name());
 | 
			
		||||
        edgeEvent.setEntityId(request.getDeviceId().getId());
 | 
			
		||||
        edgeEvent.setEdgeEventType(EdgeEventType.DEVICE);
 | 
			
		||||
        edgeEvent.setEntityBody(json.valueToTree(request));
 | 
			
		||||
        edgeEvent.setEdgeId(edgeId);
 | 
			
		||||
        ListenableFuture<EdgeEvent> saveFuture = ctx.getEdgeEventService().saveAsync(edgeEvent);
 | 
			
		||||
        Futures.addCallback(saveFuture, new FutureCallback<EdgeEvent>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onSuccess(@Nullable EdgeEvent event) {
 | 
			
		||||
                ctx.tellSuccess(msg);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onFailure(Throwable th) {
 | 
			
		||||
                log.error("Could not save edge event", th);
 | 
			
		||||
                ctx.tellFailure(msg, th);
 | 
			
		||||
            }
 | 
			
		||||
        }, ctx.getDbCallbackExecutor());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void destroy() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user