Merge branch 'master' into feature/slider-widget
This commit is contained in:
commit
bd8a43ceb2
@ -66,7 +66,6 @@ import org.thingsboard.server.common.msg.rule.engine.DeviceCredentialsUpdateNoti
|
|||||||
import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg;
|
import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg;
|
||||||
import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg;
|
import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg;
|
||||||
import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
|
import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
|
||||||
import org.thingsboard.server.gen.transport.TransportProtos;
|
|
||||||
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
|
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
|
||||||
import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
|
import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
|
||||||
import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
|
import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
|
||||||
@ -90,7 +89,9 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
|
|||||||
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
|
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
|
||||||
import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
|
import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
|
||||||
import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
|
import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
|
||||||
|
import org.thingsboard.server.gen.transport.TransportProtos.UplinkNotificationMsg;
|
||||||
import org.thingsboard.server.service.rpc.RpcSubmitStrategy;
|
import org.thingsboard.server.service.rpc.RpcSubmitStrategy;
|
||||||
|
import org.thingsboard.server.service.state.DefaultDeviceStateService;
|
||||||
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
|
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -173,7 +174,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
|
|||||||
|
|
||||||
private EdgeId findRelatedEdgeId() {
|
private EdgeId findRelatedEdgeId() {
|
||||||
List<EntityRelation> result =
|
List<EntityRelation> result =
|
||||||
systemContext.getRelationService().findByToAndType(tenantId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON);
|
systemContext.getRelationService().findByToAndType(tenantId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
|
||||||
if (result != null && result.size() > 0) {
|
if (result != null && result.size() > 0) {
|
||||||
EntityRelation relationToEdge = result.get(0);
|
EntityRelation relationToEdge = result.get(0);
|
||||||
if (relationToEdge.getFrom() != null && relationToEdge.getFrom().getId() != null) {
|
if (relationToEdge.getFrom() != null && relationToEdge.getFrom().getId() != null) {
|
||||||
@ -212,8 +213,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
|
|||||||
if (systemContext.isEdgesEnabled() && edgeId != null) {
|
if (systemContext.isEdgesEnabled() && edgeId != null) {
|
||||||
log.debug("[{}][{}] device is related to edge: [{}]. Saving RPC request: [{}][{}] to edge queue", tenantId, deviceId, edgeId.getId(), rpcId, requestId);
|
log.debug("[{}][{}] device is related to edge: [{}]. Saving RPC request: [{}][{}] to edge queue", tenantId, deviceId, edgeId.getId(), rpcId, requestId);
|
||||||
try {
|
try {
|
||||||
|
if (systemContext.getEdgeService().isEdgeActiveAsync(tenantId, edgeId, DefaultDeviceStateService.ACTIVITY_STATE).get()) {
|
||||||
saveRpcRequestToEdgeQueue(request, requestId).get();
|
saveRpcRequestToEdgeQueue(request, requestId).get();
|
||||||
sent = true;
|
} else {
|
||||||
|
log.error("[{}][{}][{}] Failed to save RPC request to edge queue {}. The Edge is currently offline or unreachable", tenantId, deviceId, edgeId.getId(), request);
|
||||||
|
}
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
log.error("[{}][{}][{}] Failed to save RPC request to edge queue {}", tenantId, deviceId, edgeId.getId(), request, e);
|
log.error("[{}][{}][{}] Failed to save RPC request to edge queue {}", tenantId, deviceId, edgeId.getId(), request, e);
|
||||||
}
|
}
|
||||||
@ -470,7 +474,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
|
|||||||
callback.onSuccess();
|
callback.onSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processUplinkNotificationMsg(SessionInfoProto sessionInfo, TransportProtos.UplinkNotificationMsg uplinkNotificationMsg) {
|
private void processUplinkNotificationMsg(SessionInfoProto sessionInfo, UplinkNotificationMsg uplinkNotificationMsg) {
|
||||||
String nodeId = sessionInfo.getNodeId();
|
String nodeId = sessionInfo.getNodeId();
|
||||||
sessions.entrySet().stream()
|
sessions.entrySet().stream()
|
||||||
.filter(kv -> kv.getValue().getSessionInfo().getNodeId().equals(nodeId) && (kv.getValue().isSubscribedToAttributes() || kv.getValue().isSubscribedToRPC()))
|
.filter(kv -> kv.getValue().getSessionInfo().getNodeId().equals(nodeId) && (kv.getValue().isSubscribedToAttributes() || kv.getValue().isSubscribedToRPC()))
|
||||||
|
|||||||
@ -16,7 +16,6 @@
|
|||||||
package org.thingsboard.server.actors.ruleChain;
|
package org.thingsboard.server.actors.ruleChain;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
|
|
||||||
import org.thingsboard.server.actors.ActorSystemContext;
|
import org.thingsboard.server.actors.ActorSystemContext;
|
||||||
import org.thingsboard.server.actors.TbActorCtx;
|
import org.thingsboard.server.actors.TbActorCtx;
|
||||||
import org.thingsboard.server.actors.TbActorRef;
|
import org.thingsboard.server.actors.TbActorRef;
|
||||||
@ -29,6 +28,7 @@ import org.thingsboard.server.common.data.id.EntityId;
|
|||||||
import org.thingsboard.server.common.data.id.RuleChainId;
|
import org.thingsboard.server.common.data.id.RuleChainId;
|
||||||
import org.thingsboard.server.common.data.id.RuleNodeId;
|
import org.thingsboard.server.common.data.id.RuleNodeId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
|
||||||
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
|
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
|
||||||
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
|
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
|
||||||
import org.thingsboard.server.common.data.relation.EntityRelation;
|
import org.thingsboard.server.common.data.relation.EntityRelation;
|
||||||
|
|||||||
@ -477,6 +477,8 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
|
|||||||
TbMsgMetaData md = new TbMsgMetaData();
|
TbMsgMetaData md = new TbMsgMetaData();
|
||||||
if (!persistToTelemetry) {
|
if (!persistToTelemetry) {
|
||||||
md.putValue(DataConstants.SCOPE, DataConstants.SERVER_SCOPE);
|
md.putValue(DataConstants.SCOPE, DataConstants.SERVER_SCOPE);
|
||||||
|
md.putValue("edgeName", edge.getName());
|
||||||
|
md.putValue("edgeType", edge.getType());
|
||||||
}
|
}
|
||||||
TbMsg tbMsg = TbMsg.newMsg(msgType, edgeId, md, TbMsgDataType.JSON, data);
|
TbMsg tbMsg = TbMsg.newMsg(msgType, edgeId, md, TbMsgDataType.JSON, data);
|
||||||
clusterService.pushMsgToRuleEngine(tenantId, edgeId, tbMsg, null);
|
clusterService.pushMsgToRuleEngine(tenantId, edgeId, tbMsg, null);
|
||||||
|
|||||||
@ -18,6 +18,7 @@ package org.thingsboard.server.service.edge.rpc.processor.rule;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.thingsboard.server.common.data.EdgeUtils;
|
import org.thingsboard.server.common.data.EdgeUtils;
|
||||||
|
import org.thingsboard.server.common.data.edge.Edge;
|
||||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
|
import org.thingsboard.server.common.data.edge.EdgeEvent;
|
||||||
import org.thingsboard.server.common.data.id.RuleChainId;
|
import org.thingsboard.server.common.data.id.RuleChainId;
|
||||||
import org.thingsboard.server.common.data.rule.RuleChain;
|
import org.thingsboard.server.common.data.rule.RuleChain;
|
||||||
@ -53,6 +54,10 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor {
|
|||||||
isRoot = Boolean.parseBoolean(edgeEvent.getBody().get(EDGE_IS_ROOT_BODY_KEY).asText());
|
isRoot = Boolean.parseBoolean(edgeEvent.getBody().get(EDGE_IS_ROOT_BODY_KEY).asText());
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
}
|
}
|
||||||
|
if (!isRoot) {
|
||||||
|
Edge edge = edgeService.findEdgeById(edgeEvent.getTenantId(), edgeEvent.getEdgeId());
|
||||||
|
isRoot = edge.getRootRuleChainId().equals(ruleChainId);
|
||||||
|
}
|
||||||
UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction());
|
UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction());
|
||||||
RuleChainUpdateMsg ruleChainUpdateMsg = ((RuleChainMsgConstructor)
|
RuleChainUpdateMsg ruleChainUpdateMsg = ((RuleChainMsgConstructor)
|
||||||
ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion))
|
ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion))
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.id.TenantId;
|
|||||||
import org.thingsboard.server.common.msg.queue.RuleEngineException;
|
import org.thingsboard.server.common.msg.queue.RuleEngineException;
|
||||||
import org.thingsboard.server.common.msg.queue.RuleNodeInfo;
|
import org.thingsboard.server.common.msg.queue.RuleNodeInfo;
|
||||||
import org.thingsboard.server.common.msg.queue.TbMsgCallback;
|
import org.thingsboard.server.common.msg.queue.TbMsgCallback;
|
||||||
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|||||||
@ -711,7 +711,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest {
|
|||||||
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
|
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
|
||||||
Assert.assertTrue(edgeImitator.waitForResponses());
|
Assert.assertTrue(edgeImitator.waitForResponses());
|
||||||
|
|
||||||
Assert.assertTrue(onUpdateCallback.getSubscribeLatch().await(30, TimeUnit.SECONDS));
|
Assert.assertTrue(onUpdateCallback.getSubscribeLatch().await(TIMEOUT, TimeUnit.SECONDS));
|
||||||
|
|
||||||
Assert.assertEquals(JacksonUtil.newObjectNode().put(attrKey, attrValue),
|
Assert.assertEquals(JacksonUtil.newObjectNode().put(attrKey, attrValue),
|
||||||
JacksonUtil.fromBytes(onUpdateCallback.getPayloadBytes()));
|
JacksonUtil.fromBytes(onUpdateCallback.getPayloadBytes()));
|
||||||
@ -798,7 +798,21 @@ public class DeviceEdgeTest extends AbstractEdgeTest {
|
|||||||
// clean up stored edge events
|
// clean up stored edge events
|
||||||
edgeEventService.cleanupEvents(1);
|
edgeEventService.cleanupEvents(1);
|
||||||
|
|
||||||
// perform rpc call to verify edgeId in DeviceActorMessageProcessor updated properly
|
// edge is disconnected: perform rpc call - no edge event saved
|
||||||
|
doPostAsync(
|
||||||
|
"/api/rpc/oneway/" + device.getId().getId().toString(),
|
||||||
|
JacksonUtil.toString(createDefaultRpc()),
|
||||||
|
String.class,
|
||||||
|
status().isOk());
|
||||||
|
Awaitility.await()
|
||||||
|
.atMost(TIMEOUT, TimeUnit.SECONDS)
|
||||||
|
.until(() -> {
|
||||||
|
PageData<EdgeEvent> result = edgeEventService.findEdgeEvents(tenantId, tmpEdge.getId(), 0L, null, new TimePageLink(1));
|
||||||
|
return result.getTotalElements() == 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
// edge is connected: perform rpc call to verify edgeId in DeviceActorMessageProcessor updated properly
|
||||||
|
simulateEdgeActivation(tmpEdge);
|
||||||
doPostAsync(
|
doPostAsync(
|
||||||
"/api/rpc/oneway/" + device.getId().getId().toString(),
|
"/api/rpc/oneway/" + device.getId().getId().toString(),
|
||||||
JacksonUtil.toString(createDefaultRpc()),
|
JacksonUtil.toString(createDefaultRpc()),
|
||||||
@ -857,4 +871,23 @@ public class DeviceEdgeTest extends AbstractEdgeTest {
|
|||||||
|
|
||||||
return rpc;
|
return rpc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void simulateEdgeActivation(Edge edge) throws Exception {
|
||||||
|
ObjectNode attributes = JacksonUtil.newObjectNode();
|
||||||
|
attributes.put("active", true);
|
||||||
|
doPost("/api/plugins/telemetry/EDGE/" + edge.getId() + "/attributes/" + DataConstants.SERVER_SCOPE, attributes);
|
||||||
|
Awaitility.await()
|
||||||
|
.atMost(TIMEOUT, TimeUnit.SECONDS)
|
||||||
|
.until(() -> {
|
||||||
|
List<Map<String, Object>> values = doGetAsyncTyped("/api/plugins/telemetry/EDGE/" + edge.getId() +
|
||||||
|
"/values/attributes/SERVER_SCOPE", new TypeReference<>() {});
|
||||||
|
Optional<Map<String, Object>> activeAttrOpt = values.stream().filter(att -> att.get("key").equals("active")).findFirst();
|
||||||
|
if (activeAttrOpt.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Map<String, Object> activeAttr = activeAttrOpt.get();
|
||||||
|
return "true".equals(activeAttr.get("value").toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -146,7 +146,7 @@ public class RuleChainEdgeTest extends AbstractEdgeTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createRuleChainMetadata(RuleChain ruleChain) {
|
private RuleChainMetaData createRuleChainMetadata(RuleChain ruleChain) {
|
||||||
RuleChainMetaData ruleChainMetaData = new RuleChainMetaData();
|
RuleChainMetaData ruleChainMetaData = new RuleChainMetaData();
|
||||||
ruleChainMetaData.setRuleChainId(ruleChain.getId());
|
ruleChainMetaData.setRuleChainId(ruleChain.getId());
|
||||||
|
|
||||||
@ -182,7 +182,7 @@ public class RuleChainEdgeTest extends AbstractEdgeTest {
|
|||||||
ruleChainMetaData.addConnectionInfo(0, 2, "fail");
|
ruleChainMetaData.addConnectionInfo(0, 2, "fail");
|
||||||
ruleChainMetaData.addConnectionInfo(1, 2, "success");
|
ruleChainMetaData.addConnectionInfo(1, 2, "success");
|
||||||
|
|
||||||
doPost("/api/ruleChain/metadata", ruleChainMetaData, RuleChainMetaData.class);
|
return doPost("/api/ruleChain/metadata", ruleChainMetaData, RuleChainMetaData.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -193,9 +193,10 @@ public class RuleChainEdgeTest extends AbstractEdgeTest {
|
|||||||
ruleChain.setType(RuleChainType.EDGE);
|
ruleChain.setType(RuleChainType.EDGE);
|
||||||
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
|
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
|
||||||
|
|
||||||
edgeImitator.expectMessageAmount(1);
|
edgeImitator.expectMessageAmount(2);
|
||||||
doPost("/api/edge/" + edge.getUuidId()
|
doPost("/api/edge/" + edge.getUuidId()
|
||||||
+ "/ruleChain/" + savedRuleChain.getUuidId(), RuleChain.class);
|
+ "/ruleChain/" + savedRuleChain.getUuidId(), RuleChain.class);
|
||||||
|
RuleChainMetaData metaData = createRuleChainMetadata(savedRuleChain);
|
||||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||||
|
|
||||||
// set new rule chain as root
|
// set new rule chain as root
|
||||||
@ -213,6 +214,22 @@ public class RuleChainEdgeTest extends AbstractEdgeTest {
|
|||||||
Assert.assertTrue(ruleChainMsg.isRoot());
|
Assert.assertTrue(ruleChainMsg.isRoot());
|
||||||
Assert.assertEquals(savedRuleChain.getId(), ruleChainMsg.getId());
|
Assert.assertEquals(savedRuleChain.getId(), ruleChainMsg.getId());
|
||||||
|
|
||||||
|
// update metadata for root rule chain
|
||||||
|
edgeImitator.expectMessageAmount(1);
|
||||||
|
metaData.getNodes().forEach(n -> n.setDebugMode(true));
|
||||||
|
doPost("/api/ruleChain/metadata", metaData, RuleChainMetaData.class);
|
||||||
|
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||||
|
ruleChainUpdateMsgOpt = edgeImitator.findMessageByType(RuleChainUpdateMsg.class);
|
||||||
|
Assert.assertTrue(ruleChainUpdateMsgOpt.isPresent());
|
||||||
|
ruleChainUpdateMsg = ruleChainUpdateMsgOpt.get();
|
||||||
|
ruleChainMsg = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true);
|
||||||
|
Assert.assertNotNull(ruleChainMsg);
|
||||||
|
Assert.assertTrue(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(ruleChainUpdateMsg.getMsgType()) ||
|
||||||
|
UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(ruleChainUpdateMsg.getMsgType()));
|
||||||
|
Assert.assertEquals(savedRuleChain.getId(), ruleChainMsg.getId());
|
||||||
|
Assert.assertEquals(savedRuleChain.getName(), ruleChainMsg.getName());
|
||||||
|
Assert.assertTrue(ruleChainMsg.isRoot());
|
||||||
|
|
||||||
// revert root rule chain
|
// revert root rule chain
|
||||||
edgeImitator.expectMessageAmount(1);
|
edgeImitator.expectMessageAmount(1);
|
||||||
doPost("/api/edge/" + edge.getUuidId()
|
doPost("/api/edge/" + edge.getUuidId()
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.thingsboard.edge.rpc.EdgeGrpcClient;
|
import org.thingsboard.edge.rpc.EdgeGrpcClient;
|
||||||
import org.thingsboard.edge.rpc.EdgeRpcClient;
|
import org.thingsboard.edge.rpc.EdgeRpcClient;
|
||||||
|
import org.thingsboard.server.controller.AbstractWebTest;
|
||||||
import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg;
|
import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg;
|
||||||
import org.thingsboard.server.gen.edge.v1.AlarmCommentUpdateMsg;
|
import org.thingsboard.server.gen.edge.v1.AlarmCommentUpdateMsg;
|
||||||
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
|
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
|
||||||
@ -72,8 +73,6 @@ import java.util.stream.Collectors;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class EdgeImitator {
|
public class EdgeImitator {
|
||||||
|
|
||||||
public static final int TIMEOUT_IN_SECONDS = 30;
|
|
||||||
|
|
||||||
private String routingKey;
|
private String routingKey;
|
||||||
private String routingSecret;
|
private String routingSecret;
|
||||||
|
|
||||||
@ -344,7 +343,7 @@ public class EdgeImitator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean waitForMessages() throws InterruptedException {
|
public boolean waitForMessages() throws InterruptedException {
|
||||||
return waitForMessages(TIMEOUT_IN_SECONDS);
|
return waitForMessages(AbstractWebTest.TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean waitForMessages(int timeoutInSeconds) throws InterruptedException {
|
public boolean waitForMessages(int timeoutInSeconds) throws InterruptedException {
|
||||||
@ -359,7 +358,7 @@ public class EdgeImitator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean waitForResponses() throws InterruptedException {
|
public boolean waitForResponses() throws InterruptedException {
|
||||||
return responsesLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
|
return responsesLatch.await(AbstractWebTest.TIMEOUT, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expectResponsesAmount(int messageAmount) {
|
public void expectResponsesAmount(int messageAmount) {
|
||||||
|
|||||||
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.service.edge.rpc.constructor;
|
package org.thingsboard.server.service.edge.rpc.constructor;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
@ -61,7 +60,7 @@ public class RuleChainMsgConstructorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstructRuleChainMetadataUpdatedMsg_V_3_4_0() throws JsonProcessingException {
|
public void testConstructRuleChainMetadataUpdatedMsg_V_3_4_0() {
|
||||||
RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID());
|
RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID());
|
||||||
RuleChainMetaData ruleChainMetaData = createRuleChainMetaData(
|
RuleChainMetaData ruleChainMetaData = createRuleChainMetaData(
|
||||||
ruleChainId, 3, createRuleNodes(ruleChainId), createConnections());
|
ruleChainId, 3, createRuleNodes(ruleChainId), createConnections());
|
||||||
@ -80,7 +79,7 @@ public class RuleChainMsgConstructorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_3() throws JsonProcessingException {
|
public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_3() {
|
||||||
RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID());
|
RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID());
|
||||||
RuleChainMetaData ruleChainMetaData = createRuleChainMetaData(
|
RuleChainMetaData ruleChainMetaData = createRuleChainMetaData(
|
||||||
ruleChainId, 3, createRuleNodes(ruleChainId), createConnections());
|
ruleChainId, 3, createRuleNodes(ruleChainId), createConnections());
|
||||||
@ -120,7 +119,7 @@ public class RuleChainMsgConstructorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_0() throws JsonProcessingException {
|
public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_0() {
|
||||||
RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID());
|
RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID());
|
||||||
RuleChainMetaData ruleChainMetaData = createRuleChainMetaData(ruleChainId, 3, createRuleNodes(ruleChainId), createConnections());
|
RuleChainMetaData ruleChainMetaData = createRuleChainMetaData(ruleChainId, 3, createRuleNodes(ruleChainId), createConnections());
|
||||||
RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg =
|
RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg =
|
||||||
@ -161,7 +160,7 @@ public class RuleChainMsgConstructorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_0_inDifferentOrder() throws JsonProcessingException {
|
public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_0_inDifferentOrder() {
|
||||||
// same rule chain metadata, but different order of rule nodes
|
// same rule chain metadata, but different order of rule nodes
|
||||||
RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID());
|
RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID());
|
||||||
RuleChainMetaData ruleChainMetaData1 = createRuleChainMetaData(ruleChainId, 8, createRuleNodesInDifferentOrder(ruleChainId), createConnectionsInDifferentOrder());
|
RuleChainMetaData ruleChainMetaData1 = createRuleChainMetaData(ruleChainId, 8, createRuleNodesInDifferentOrder(ruleChainId), createConnectionsInDifferentOrder());
|
||||||
@ -254,7 +253,7 @@ public class RuleChainMsgConstructorTest {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<RuleNode> createRuleNodes(RuleChainId ruleChainId) throws JsonProcessingException {
|
private List<RuleNode> createRuleNodes(RuleChainId ruleChainId) {
|
||||||
List<RuleNode> result = new ArrayList<>();
|
List<RuleNode> result = new ArrayList<>();
|
||||||
result.add(getOutputNode(ruleChainId));
|
result.add(getOutputNode(ruleChainId));
|
||||||
result.add(getAcknowledgeNode(ruleChainId));
|
result.add(getAcknowledgeNode(ruleChainId));
|
||||||
@ -301,7 +300,7 @@ public class RuleChainMsgConstructorTest {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<RuleNode> createRuleNodesInDifferentOrder(RuleChainId ruleChainId) throws JsonProcessingException {
|
private List<RuleNode> createRuleNodesInDifferentOrder(RuleChainId ruleChainId) {
|
||||||
List<RuleNode> result = new ArrayList<>();
|
List<RuleNode> result = new ArrayList<>();
|
||||||
result.add(getPushToAnalyticsNode(ruleChainId));
|
result.add(getPushToAnalyticsNode(ruleChainId));
|
||||||
result.add(getPushToCloudNode(ruleChainId));
|
result.add(getPushToCloudNode(ruleChainId));
|
||||||
@ -319,99 +318,99 @@ public class RuleChainMsgConstructorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private RuleNode getOutputNode(RuleChainId ruleChainId) throws JsonProcessingException {
|
private RuleNode getOutputNode(RuleChainId ruleChainId) {
|
||||||
return createRuleNode(ruleChainId,
|
return createRuleNode(ruleChainId,
|
||||||
"org.thingsboard.rule.engine.flow.TbRuleChainOutputNode",
|
"org.thingsboard.rule.engine.flow.TbRuleChainOutputNode",
|
||||||
"Output node",
|
"Output node",
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"version\":0}"),
|
JacksonUtil.toJsonNode("{\"version\":0}"),
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"description\":\"\",\"layoutX\":178,\"layoutY\":592}"));
|
JacksonUtil.toJsonNode("{\"description\":\"\",\"layoutX\":178,\"layoutY\":592}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuleNode getCheckpointNode(RuleChainId ruleChainId) throws JsonProcessingException {
|
private RuleNode getCheckpointNode(RuleChainId ruleChainId) {
|
||||||
return createRuleNode(ruleChainId,
|
return createRuleNode(ruleChainId,
|
||||||
"org.thingsboard.rule.engine.flow.TbCheckpointNode",
|
"org.thingsboard.rule.engine.flow.TbCheckpointNode",
|
||||||
"Checkpoint node",
|
"Checkpoint node",
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"queueName\":\"HighPriority\"}"),
|
JacksonUtil.toJsonNode("{\"queueName\":\"HighPriority\"}"),
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"description\":\"\",\"layoutX\":178,\"layoutY\":647}"));
|
JacksonUtil.toJsonNode("{\"description\":\"\",\"layoutX\":178,\"layoutY\":647}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuleNode getSaveTimeSeriesNode(RuleChainId ruleChainId) throws JsonProcessingException {
|
private RuleNode getSaveTimeSeriesNode(RuleChainId ruleChainId) {
|
||||||
return createRuleNode(ruleChainId,
|
return createRuleNode(ruleChainId,
|
||||||
"org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
|
"org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
|
||||||
"Save Timeseries",
|
"Save Timeseries",
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"defaultTTL\":0}"),
|
JacksonUtil.toJsonNode("{\"defaultTTL\":0}"),
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":823,\"layoutY\":157}"));
|
JacksonUtil.toJsonNode("{\"layoutX\":823,\"layoutY\":157}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuleNode getMessageTypeSwitchNode(RuleChainId ruleChainId) throws JsonProcessingException {
|
private RuleNode getMessageTypeSwitchNode(RuleChainId ruleChainId) {
|
||||||
return createRuleNode(ruleChainId,
|
return createRuleNode(ruleChainId,
|
||||||
"org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
|
"org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
|
||||||
"Message Type Switch",
|
"Message Type Switch",
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"version\":0}"),
|
JacksonUtil.toJsonNode("{\"version\":0}"),
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":347,\"layoutY\":149}"));
|
JacksonUtil.toJsonNode("{\"layoutX\":347,\"layoutY\":149}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuleNode getLogOtherNode(RuleChainId ruleChainId) throws JsonProcessingException {
|
private RuleNode getLogOtherNode(RuleChainId ruleChainId) {
|
||||||
return createRuleNode(ruleChainId,
|
return createRuleNode(ruleChainId,
|
||||||
"org.thingsboard.rule.engine.action.TbLogNode",
|
"org.thingsboard.rule.engine.action.TbLogNode",
|
||||||
"Log Other",
|
"Log Other",
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"jsScript\":\"return '\\\\nIncoming message:\\\\n' + JSON.stringify(msg) + '\\\\nIncoming metadata:\\\\n' + JSON.stringify(metadata);\"}"),
|
JacksonUtil.toJsonNode("{\"jsScript\":\"return '\\\\nIncoming message:\\\\n' + JSON.stringify(msg) + '\\\\nIncoming metadata:\\\\n' + JSON.stringify(metadata);\"}"),
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":824,\"layoutY\":378}"));
|
JacksonUtil.toJsonNode("{\"layoutX\":824,\"layoutY\":378}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuleNode getPushToCloudNode(RuleChainId ruleChainId) throws JsonProcessingException {
|
private RuleNode getPushToCloudNode(RuleChainId ruleChainId) {
|
||||||
return createRuleNode(ruleChainId,
|
return createRuleNode(ruleChainId,
|
||||||
"org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
|
"org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
|
||||||
"Push to cloud",
|
"Push to cloud",
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"scope\":\"SERVER_SCOPE\"}"),
|
JacksonUtil.toJsonNode("{\"scope\":\"SERVER_SCOPE\"}"),
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":1129,\"layoutY\":52}"));
|
JacksonUtil.toJsonNode("{\"layoutX\":1129,\"layoutY\":52}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuleNode getAcknowledgeNode(RuleChainId ruleChainId) throws JsonProcessingException {
|
private RuleNode getAcknowledgeNode(RuleChainId ruleChainId) {
|
||||||
return createRuleNode(ruleChainId,
|
return createRuleNode(ruleChainId,
|
||||||
"org.thingsboard.rule.engine.flow.TbAckNode",
|
"org.thingsboard.rule.engine.flow.TbAckNode",
|
||||||
"Acknowledge node",
|
"Acknowledge node",
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"version\":0}"),
|
JacksonUtil.toJsonNode("{\"version\":0}"),
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"description\":\"\",\"layoutX\":177,\"layoutY\":703}"));
|
JacksonUtil.toJsonNode("{\"description\":\"\",\"layoutX\":177,\"layoutY\":703}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuleNode getDeviceProfileNode(RuleChainId ruleChainId) throws JsonProcessingException {
|
private RuleNode getDeviceProfileNode(RuleChainId ruleChainId) {
|
||||||
return createRuleNode(ruleChainId,
|
return createRuleNode(ruleChainId,
|
||||||
"org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
|
"org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
|
||||||
"Device Profile Node",
|
"Device Profile Node",
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"persistAlarmRulesState\":false,\"fetchAlarmRulesStateOnStart\":false}"),
|
JacksonUtil.toJsonNode("{\"persistAlarmRulesState\":false,\"fetchAlarmRulesStateOnStart\":false}"),
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"description\":\"Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \\\"Success\\\" relation type.\",\"layoutX\":187,\"layoutY\":468}"));
|
JacksonUtil.toJsonNode("{\"description\":\"Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \\\"Success\\\" relation type.\",\"layoutX\":187,\"layoutY\":468}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuleNode getSaveClientAttributesNode(RuleChainId ruleChainId) throws JsonProcessingException {
|
private RuleNode getSaveClientAttributesNode(RuleChainId ruleChainId) {
|
||||||
return createRuleNode(ruleChainId,
|
return createRuleNode(ruleChainId,
|
||||||
"org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
|
"org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
|
||||||
"Save Client Attributes",
|
"Save Client Attributes",
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"scope\":\"CLIENT_SCOPE\"}"),
|
JacksonUtil.toJsonNode("{\"scope\":\"CLIENT_SCOPE\"}"),
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":824,\"layoutY\":52}"));
|
JacksonUtil.toJsonNode("{\"layoutX\":824,\"layoutY\":52}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuleNode getLogRpcFromDeviceNode(RuleChainId ruleChainId) throws JsonProcessingException {
|
private RuleNode getLogRpcFromDeviceNode(RuleChainId ruleChainId) {
|
||||||
return createRuleNode(ruleChainId,
|
return createRuleNode(ruleChainId,
|
||||||
"org.thingsboard.rule.engine.action.TbLogNode",
|
"org.thingsboard.rule.engine.action.TbLogNode",
|
||||||
"Log RPC from Device",
|
"Log RPC from Device",
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"jsScript\":\"return '\\\\nIncoming message:\\\\n' + JSON.stringify(msg) + '\\\\nIncoming metadata:\\\\n' + JSON.stringify(metadata);\"}"),
|
JacksonUtil.toJsonNode("{\"jsScript\":\"return '\\\\nIncoming message:\\\\n' + JSON.stringify(msg) + '\\\\nIncoming metadata:\\\\n' + JSON.stringify(metadata);\"}"),
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":825,\"layoutY\":266}"));
|
JacksonUtil.toJsonNode("{\"layoutX\":825,\"layoutY\":266}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuleNode getRpcCallRequestNode(RuleChainId ruleChainId) throws JsonProcessingException {
|
private RuleNode getRpcCallRequestNode(RuleChainId ruleChainId) {
|
||||||
return createRuleNode(ruleChainId,
|
return createRuleNode(ruleChainId,
|
||||||
"org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
|
"org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
|
||||||
"RPC Call Request",
|
"RPC Call Request",
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"timeoutInSeconds\":60}"),
|
JacksonUtil.toJsonNode("{\"timeoutInSeconds\":60}"),
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":824,\"layoutY\":466}"));
|
JacksonUtil.toJsonNode("{\"layoutX\":824,\"layoutY\":466}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuleNode getPushToAnalyticsNode(RuleChainId ruleChainId) throws JsonProcessingException {
|
private RuleNode getPushToAnalyticsNode(RuleChainId ruleChainId) {
|
||||||
return createRuleNode(ruleChainId,
|
return createRuleNode(ruleChainId,
|
||||||
"org.thingsboard.rule.engine.flow.TbRuleChainInputNode",
|
"org.thingsboard.rule.engine.flow.TbRuleChainInputNode",
|
||||||
"Push to Analytics",
|
"Push to Analytics",
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"ruleChainId\":\"af588000-6c7c-11ec-bafd-c9a47a5c8d99\"}"),
|
JacksonUtil.toJsonNode("{\"ruleChainId\":\"af588000-6c7c-11ec-bafd-c9a47a5c8d99\"}"),
|
||||||
JacksonUtil.OBJECT_MAPPER.readTree("{\"description\":\"\",\"layoutX\":477,\"layoutY\":560}"));
|
JacksonUtil.toJsonNode("{\"description\":\"\",\"layoutX\":477,\"layoutY\":560}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,6 +55,7 @@ import org.thingsboard.server.dao.rule.RuleChainService;
|
|||||||
import org.thingsboard.server.dao.service.DataValidator;
|
import org.thingsboard.server.dao.service.DataValidator;
|
||||||
import org.thingsboard.server.dao.tenant.TenantProfileService;
|
import org.thingsboard.server.dao.tenant.TenantProfileService;
|
||||||
import org.thingsboard.server.dao.tenant.TenantService;
|
import org.thingsboard.server.dao.tenant.TenantService;
|
||||||
|
import org.thingsboard.server.dao.timeseries.TimeseriesService;
|
||||||
import org.thingsboard.server.dao.user.UserService;
|
import org.thingsboard.server.dao.user.UserService;
|
||||||
import org.thingsboard.server.dao.widget.WidgetTypeService;
|
import org.thingsboard.server.dao.widget.WidgetTypeService;
|
||||||
import org.thingsboard.server.dao.widget.WidgetsBundleService;
|
import org.thingsboard.server.dao.widget.WidgetsBundleService;
|
||||||
@ -209,6 +210,9 @@ public abstract class BaseEdgeProcessorTest {
|
|||||||
@MockBean
|
@MockBean
|
||||||
protected AttributesService attributesService;
|
protected AttributesService attributesService;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
protected TimeseriesService timeseriesService;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
protected TbClusterService tbClusterService;
|
protected TbClusterService tbClusterService;
|
||||||
|
|
||||||
|
|||||||
@ -91,4 +91,6 @@ public interface EdgeService extends EntityDaoService {
|
|||||||
PageData<EdgeId> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink);
|
PageData<EdgeId> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink);
|
||||||
|
|
||||||
String findMissingToRelatedRuleChains(TenantId tenantId, EdgeId edgeId, String tbRuleChainInputNodeClassName);
|
String findMissingToRelatedRuleChains(TenantId tenantId, EdgeId edgeId, String tbRuleChainInputNodeClassName);
|
||||||
|
|
||||||
|
ListenableFuture<Boolean> isEdgeActiveAsync(TenantId tenantId, EdgeId edgeId, String activityState);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,10 +30,10 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.transaction.event.TransactionalEventListener;
|
import org.springframework.transaction.event.TransactionalEventListener;
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
|
import org.thingsboard.server.common.data.DataConstants;
|
||||||
import org.thingsboard.server.common.data.EntitySubtype;
|
import org.thingsboard.server.common.data.EntitySubtype;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.StringUtils;
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
import org.thingsboard.server.common.data.Tenant;
|
|
||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.audit.ActionType;
|
import org.thingsboard.server.common.data.audit.ActionType;
|
||||||
import org.thingsboard.server.common.data.edge.Edge;
|
import org.thingsboard.server.common.data.edge.Edge;
|
||||||
@ -48,14 +48,15 @@ import org.thingsboard.server.common.data.id.RuleChainId;
|
|||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.id.TenantProfileId;
|
import org.thingsboard.server.common.data.id.TenantProfileId;
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
|
import org.thingsboard.server.common.data.kv.KvEntry;
|
||||||
import org.thingsboard.server.common.data.page.PageData;
|
import org.thingsboard.server.common.data.page.PageData;
|
||||||
import org.thingsboard.server.common.data.page.PageDataIterable;
|
|
||||||
import org.thingsboard.server.common.data.page.PageDataIterableByTenantIdEntityId;
|
import org.thingsboard.server.common.data.page.PageDataIterableByTenantIdEntityId;
|
||||||
import org.thingsboard.server.common.data.page.PageLink;
|
import org.thingsboard.server.common.data.page.PageLink;
|
||||||
import org.thingsboard.server.common.data.relation.EntityRelation;
|
import org.thingsboard.server.common.data.relation.EntityRelation;
|
||||||
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
|
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
|
||||||
import org.thingsboard.server.common.data.rule.RuleChain;
|
import org.thingsboard.server.common.data.rule.RuleChain;
|
||||||
import org.thingsboard.server.common.data.rule.RuleNode;
|
import org.thingsboard.server.common.data.rule.RuleNode;
|
||||||
|
import org.thingsboard.server.dao.attributes.AttributesService;
|
||||||
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
|
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
|
||||||
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
|
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
|
||||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||||
@ -64,7 +65,7 @@ import org.thingsboard.server.dao.rule.RuleChainService;
|
|||||||
import org.thingsboard.server.dao.service.DataValidator;
|
import org.thingsboard.server.dao.service.DataValidator;
|
||||||
import org.thingsboard.server.dao.service.PaginatedRemover;
|
import org.thingsboard.server.dao.service.PaginatedRemover;
|
||||||
import org.thingsboard.server.dao.service.Validator;
|
import org.thingsboard.server.dao.service.Validator;
|
||||||
import org.thingsboard.server.dao.tenant.TenantService;
|
import org.thingsboard.server.dao.timeseries.TimeseriesService;
|
||||||
import org.thingsboard.server.dao.user.UserService;
|
import org.thingsboard.server.dao.user.UserService;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -105,7 +106,10 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
|
|||||||
private RelationService relationService;
|
private RelationService relationService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TenantService tenantService;
|
private TimeseriesService timeseriesService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AttributesService attributesService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private DataValidator<Edge> edgeValidator;
|
private DataValidator<Edge> edgeValidator;
|
||||||
@ -113,6 +117,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
|
|||||||
@Value("${edges.enabled}")
|
@Value("${edges.enabled}")
|
||||||
@Getter
|
@Getter
|
||||||
private boolean edgesEnabled;
|
private boolean edgesEnabled;
|
||||||
|
@Value("${edges.state.persistToTelemetry:false}")
|
||||||
|
private boolean persistToTelemetry;
|
||||||
|
|
||||||
@TransactionalEventListener(classes = EdgeCacheEvictEvent.class)
|
@TransactionalEventListener(classes = EdgeCacheEvictEvent.class)
|
||||||
@Override
|
@Override
|
||||||
@ -530,6 +536,18 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
|
|||||||
return result.toString();
|
return result.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListenableFuture<Boolean> isEdgeActiveAsync(TenantId tenantId, EdgeId edgeId, String key) {
|
||||||
|
ListenableFuture<? extends Optional<? extends KvEntry>> futureKvEntry;
|
||||||
|
if (persistToTelemetry) {
|
||||||
|
futureKvEntry = timeseriesService.findLatest(tenantId, edgeId, key);
|
||||||
|
} else {
|
||||||
|
futureKvEntry = attributesService.find(tenantId, edgeId, DataConstants.SERVER_SCOPE, key);
|
||||||
|
}
|
||||||
|
return Futures.transformAsync(futureKvEntry, kvEntryOpt ->
|
||||||
|
Futures.immediateFuture(kvEntryOpt.flatMap(KvEntry::getBooleanValue).orElse(false)), MoreExecutors.directExecutor());
|
||||||
|
}
|
||||||
|
|
||||||
private List<RuleChain> findEdgeRuleChains(TenantId tenantId, EdgeId edgeId) {
|
private List<RuleChain> findEdgeRuleChains(TenantId tenantId, EdgeId edgeId) {
|
||||||
List<RuleChain> result = new ArrayList<>();
|
List<RuleChain> result = new ArrayList<>();
|
||||||
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
|
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
|
||||||
|
|||||||
@ -40,9 +40,9 @@ import org.thingsboard.server.common.data.notification.rule.trigger.config.Alarm
|
|||||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.ApiUsageLimitNotificationRuleTriggerConfig;
|
import org.thingsboard.server.common.data.notification.rule.trigger.config.ApiUsageLimitNotificationRuleTriggerConfig;
|
||||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig;
|
import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig;
|
||||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig.DeviceEvent;
|
import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig.DeviceEvent;
|
||||||
|
import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeCommunicationFailureNotificationRuleTriggerConfig;
|
||||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectionNotificationRuleTriggerConfig;
|
import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectionNotificationRuleTriggerConfig;
|
||||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectionNotificationRuleTriggerConfig.EdgeConnectivityEvent;
|
import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectionNotificationRuleTriggerConfig.EdgeConnectivityEvent;
|
||||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeCommunicationFailureNotificationRuleTriggerConfig;
|
|
||||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.EntitiesLimitNotificationRuleTriggerConfig;
|
import org.thingsboard.server.common.data.notification.rule.trigger.config.EntitiesLimitNotificationRuleTriggerConfig;
|
||||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.EntityActionNotificationRuleTriggerConfig;
|
import org.thingsboard.server.common.data.notification.rule.trigger.config.EntityActionNotificationRuleTriggerConfig;
|
||||||
import org.thingsboard.server.common.data.notification.rule.trigger.config.NewPlatformVersionNotificationRuleTriggerConfig;
|
import org.thingsboard.server.common.data.notification.rule.trigger.config.NewPlatformVersionNotificationRuleTriggerConfig;
|
||||||
@ -347,7 +347,7 @@ public class DefaultNotifications {
|
|||||||
public static final DefaultNotification edgeCommunicationFailures = DefaultNotification.builder()
|
public static final DefaultNotification edgeCommunicationFailures = DefaultNotification.builder()
|
||||||
.name("Edge communication failure notification")
|
.name("Edge communication failure notification")
|
||||||
.type(NotificationType.EDGE_COMMUNICATION_FAILURE)
|
.type(NotificationType.EDGE_COMMUNICATION_FAILURE)
|
||||||
.subject("Edge '${edgeName}' communication failure occured")
|
.subject("Edge '${edgeName}' communication failure occurred")
|
||||||
.text("Failure message: '${failureMsg}'")
|
.text("Failure message: '${failureMsg}'")
|
||||||
.icon("error").color(RED_COLOR)
|
.icon("error").color(RED_COLOR)
|
||||||
.button("Go to Edge").link("/edgeManagement/instances/${edgeId}")
|
.button("Go to Edge").link("/edgeManagement/instances/${edgeId}")
|
||||||
|
|||||||
@ -525,12 +525,11 @@ public class RestClient implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PageData<AlarmCommentInfo> getAlarmComments(AlarmId alarmId, PageLink pageLink) {
|
public PageData<AlarmCommentInfo> getAlarmComments(AlarmId alarmId, PageLink pageLink) {
|
||||||
String urlSecondPart = "/api/alarm/{alarmId}/comment";
|
|
||||||
Map<String, String> params = new HashMap<>();
|
Map<String, String> params = new HashMap<>();
|
||||||
params.put("alarmId", alarmId.getId().toString());
|
params.put("alarmId", alarmId.getId().toString());
|
||||||
|
addPageLinkToParam(params, pageLink);
|
||||||
return restTemplate.exchange(
|
return restTemplate.exchange(
|
||||||
baseURL + urlSecondPart + "&" + getUrlParams(pageLink),
|
baseURL + "/api/alarm/{alarmId}/comment?" + getUrlParams(pageLink),
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
HttpEntity.EMPTY,
|
HttpEntity.EMPTY,
|
||||||
new ParameterizedTypeReference<PageData<AlarmCommentInfo>>() {
|
new ParameterizedTypeReference<PageData<AlarmCommentInfo>>() {
|
||||||
|
|||||||
@ -47,6 +47,7 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.INACTIVITY_EVENT;
|
|||||||
import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_REQUEST;
|
import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_REQUEST;
|
||||||
import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST;
|
import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST;
|
||||||
import static org.thingsboard.server.common.data.msg.TbMsgType.TIMESERIES_UPDATED;
|
import static org.thingsboard.server.common.data.msg.TbMsgType.TIMESERIES_UPDATED;
|
||||||
|
import static org.thingsboard.server.common.data.msg.TbMsgType.TO_SERVER_RPC_REQUEST;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class AbstractTbMsgPushNode<T extends BaseTbMsgPushNodeConfiguration, S, U> implements TbNode {
|
public abstract class AbstractTbMsgPushNode<T extends BaseTbMsgPushNodeConfiguration, S, U> implements TbNode {
|
||||||
@ -176,6 +177,6 @@ public abstract class AbstractTbMsgPushNode<T extends BaseTbMsgPushNodeConfigura
|
|||||||
|
|
||||||
protected boolean isSupportedMsgType(TbMsg msg) {
|
protected boolean isSupportedMsgType(TbMsg msg) {
|
||||||
return msg.isTypeOneOf(POST_TELEMETRY_REQUEST, POST_ATTRIBUTES_REQUEST, ATTRIBUTES_UPDATED, ATTRIBUTES_DELETED, TIMESERIES_UPDATED,
|
return msg.isTypeOneOf(POST_TELEMETRY_REQUEST, POST_ATTRIBUTES_REQUEST, ATTRIBUTES_UPDATED, ATTRIBUTES_DELETED, TIMESERIES_UPDATED,
|
||||||
ALARM, CONNECT_EVENT, DISCONNECT_EVENT, ACTIVITY_EVENT, INACTIVITY_EVENT);
|
ALARM, CONNECT_EVENT, DISCONNECT_EVENT, ACTIVITY_EVENT, INACTIVITY_EVENT, TO_SERVER_RPC_REQUEST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.rule.engine.geo;
|
package org.thingsboard.rule.engine.geo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
@ -28,6 +30,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry;
|
|||||||
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
|
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
|
||||||
import org.thingsboard.server.common.data.kv.StringDataEntry;
|
import org.thingsboard.server.common.data.kv.StringDataEntry;
|
||||||
import org.thingsboard.server.common.data.plugin.ComponentType;
|
import org.thingsboard.server.common.data.plugin.ComponentType;
|
||||||
|
import org.thingsboard.server.common.data.util.TbPair;
|
||||||
import org.thingsboard.server.common.msg.TbMsg;
|
import org.thingsboard.server.common.msg.TbMsg;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -39,6 +42,11 @@ import java.util.concurrent.ExecutionException;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.ENTERED;
|
||||||
|
import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.INSIDE;
|
||||||
|
import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.LEFT;
|
||||||
|
import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.OUTSIDE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by ashvayka on 19.01.18.
|
* Created by ashvayka on 19.01.18.
|
||||||
*/
|
*/
|
||||||
@ -46,15 +54,24 @@ import java.util.concurrent.TimeoutException;
|
|||||||
@RuleNode(
|
@RuleNode(
|
||||||
type = ComponentType.ACTION,
|
type = ComponentType.ACTION,
|
||||||
name = "gps geofencing events",
|
name = "gps geofencing events",
|
||||||
|
version = 1,
|
||||||
configClazz = TbGpsGeofencingActionNodeConfiguration.class,
|
configClazz = TbGpsGeofencingActionNodeConfiguration.class,
|
||||||
relationTypes = {"Success", "Entered", "Left", "Inside", "Outside"},
|
relationTypes = {"Success", "Entered", "Left", "Inside", "Outside"},
|
||||||
nodeDescription = "Produces incoming messages using GPS based geofencing",
|
nodeDescription = "Produces incoming messages using GPS based geofencing",
|
||||||
nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns different events based on configuration parameters",
|
nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns different events based on configuration parameters. " +
|
||||||
|
"<br><br>" +
|
||||||
|
"If an object with coordinates extracted from incoming message enters the geofence, sends a message with the type <code>Entered</code>. " +
|
||||||
|
"If an object leaves the geofence, sends a message with the type <code>Left</code>. " +
|
||||||
|
"If the presence monitoring strategy <b>\"On first message\"</b> is selected, sends messages via rule node connection type <code>Inside</code> or <code>Outside</code> only the first time the geofencing and duration conditions are satisfied; otherwise sends messages via rule node connection type <code>Success</code>. " +
|
||||||
|
"If the presence monitoring strategy <b>\"On each message\"</b> is selected, sends messages via rule node connection type <code>Inside</code> or <code>Outside</code> every time the geofencing condition is satisfied. " +
|
||||||
|
"<br><br>" +
|
||||||
|
"Output connections: <code>Entered</code>, <code>Left</code>, <code>Inside</code>, <code>Outside</code>, <code>Success</code>",
|
||||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||||
configDirective = "tbActionNodeGpsGeofencingConfig"
|
configDirective = "tbActionNodeGpsGeofencingConfig"
|
||||||
)
|
)
|
||||||
public class TbGpsGeofencingActionNode extends AbstractGeofencingNode<TbGpsGeofencingActionNodeConfiguration> {
|
public class TbGpsGeofencingActionNode extends AbstractGeofencingNode<TbGpsGeofencingActionNodeConfiguration> {
|
||||||
|
|
||||||
|
private static final String REPORT_PRESENCE_STATUS_ON_EACH_MESSAGE = "reportPresenceStatusOnEachMessage";
|
||||||
private final Map<EntityId, EntityGeofencingState> entityStates = new HashMap<>();
|
private final Map<EntityId, EntityGeofencingState> entityStates = new HashMap<>();
|
||||||
private final Gson gson = new Gson();
|
private final Gson gson = new Gson();
|
||||||
private final JsonParser parser = new JsonParser();
|
private final JsonParser parser = new JsonParser();
|
||||||
@ -80,26 +97,33 @@ public class TbGpsGeofencingActionNode extends AbstractGeofencingNode<TbGpsGeofe
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
boolean told = false;
|
|
||||||
if (entityState.getStateSwitchTime() == 0L || entityState.isInside() != matches) {
|
if (entityState.getStateSwitchTime() == 0L || entityState.isInside() != matches) {
|
||||||
switchState(ctx, msg.getOriginator(), entityState, matches, ts);
|
switchState(ctx, msg.getOriginator(), entityState, matches, ts);
|
||||||
ctx.tellNext(msg, matches ? "Entered" : "Left");
|
ctx.tellNext(msg, matches ? ENTERED : LEFT);
|
||||||
told = true;
|
return;
|
||||||
} else {
|
}
|
||||||
if (!entityState.isStayed()) {
|
|
||||||
|
if (config.isReportPresenceStatusOnEachMessage()) {
|
||||||
|
ctx.tellNext(msg, entityState.isInside() ? INSIDE : OUTSIDE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entityState.isStayed()) {
|
||||||
|
ctx.tellSuccess(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
long stayTime = ts - entityState.getStateSwitchTime();
|
long stayTime = ts - entityState.getStateSwitchTime();
|
||||||
if (stayTime > (entityState.isInside() ?
|
if (stayTime > (entityState.isInside() ?
|
||||||
TimeUnit.valueOf(config.getMinInsideDurationTimeUnit()).toMillis(config.getMinInsideDuration()) : TimeUnit.valueOf(config.getMinOutsideDurationTimeUnit()).toMillis(config.getMinOutsideDuration()))) {
|
TimeUnit.valueOf(config.getMinInsideDurationTimeUnit()).toMillis(config.getMinInsideDuration()) :
|
||||||
|
TimeUnit.valueOf(config.getMinOutsideDurationTimeUnit()).toMillis(config.getMinOutsideDuration()))) {
|
||||||
setStaid(ctx, msg.getOriginator(), entityState);
|
setStaid(ctx, msg.getOriginator(), entityState);
|
||||||
ctx.tellNext(msg, entityState.isInside() ? "Inside" : "Outside");
|
ctx.tellNext(msg, entityState.isInside() ? INSIDE : OUTSIDE);
|
||||||
told = true;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!told) {
|
|
||||||
ctx.tellSuccess(msg);
|
ctx.tellSuccess(msg);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void switchState(TbContext ctx, EntityId entityId, EntityGeofencingState entityState, boolean matches, long ts) {
|
private void switchState(TbContext ctx, EntityId entityId, EntityGeofencingState entityState, boolean matches, long ts) {
|
||||||
entityState.setInside(matches);
|
entityState.setInside(matches);
|
||||||
@ -127,4 +151,17 @@ public class TbGpsGeofencingActionNode extends AbstractGeofencingNode<TbGpsGeofe
|
|||||||
protected Class<TbGpsGeofencingActionNodeConfiguration> getConfigClazz() {
|
protected Class<TbGpsGeofencingActionNodeConfiguration> getConfigClazz() {
|
||||||
return TbGpsGeofencingActionNodeConfiguration.class;
|
return TbGpsGeofencingActionNodeConfiguration.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
|
||||||
|
boolean hasChanges = false;
|
||||||
|
if (fromVersion == 0) {
|
||||||
|
if (!oldConfiguration.has(REPORT_PRESENCE_STATUS_ON_EACH_MESSAGE)) {
|
||||||
|
hasChanges = true;
|
||||||
|
((ObjectNode) oldConfiguration).put(REPORT_PRESENCE_STATUS_ON_EACH_MESSAGE, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new TbPair<>(hasChanges, oldConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,8 @@ public class TbGpsGeofencingActionNodeConfiguration extends TbGpsGeofencingFilte
|
|||||||
private String minInsideDurationTimeUnit;
|
private String minInsideDurationTimeUnit;
|
||||||
private String minOutsideDurationTimeUnit;
|
private String minOutsideDurationTimeUnit;
|
||||||
|
|
||||||
|
private boolean reportPresenceStatusOnEachMessage;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TbGpsGeofencingActionNodeConfiguration defaultConfiguration() {
|
public TbGpsGeofencingActionNodeConfiguration defaultConfiguration() {
|
||||||
TbGpsGeofencingActionNodeConfiguration configuration = new TbGpsGeofencingActionNodeConfiguration();
|
TbGpsGeofencingActionNodeConfiguration configuration = new TbGpsGeofencingActionNodeConfiguration();
|
||||||
@ -43,6 +45,7 @@ public class TbGpsGeofencingActionNodeConfiguration extends TbGpsGeofencingFilte
|
|||||||
configuration.setMinOutsideDurationTimeUnit(TimeUnit.MINUTES.name());
|
configuration.setMinOutsideDurationTimeUnit(TimeUnit.MINUTES.name());
|
||||||
configuration.setMinInsideDuration(1);
|
configuration.setMinInsideDuration(1);
|
||||||
configuration.setMinOutsideDuration(1);
|
configuration.setMinOutsideDuration(1);
|
||||||
|
configuration.setReportPresenceStatusOnEachMessage(true);
|
||||||
return configuration;
|
return configuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.AlarmId;
|
|||||||
import org.thingsboard.server.common.data.id.AssetId;
|
import org.thingsboard.server.common.data.id.AssetId;
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
import org.thingsboard.server.common.data.id.CustomerId;
|
||||||
import org.thingsboard.server.common.data.id.DeviceId;
|
import org.thingsboard.server.common.data.id.DeviceId;
|
||||||
|
import org.thingsboard.server.common.data.id.EdgeId;
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
import org.thingsboard.server.common.data.id.EntityViewId;
|
import org.thingsboard.server.common.data.id.EntityViewId;
|
||||||
import org.thingsboard.server.common.data.id.RuleChainId;
|
import org.thingsboard.server.common.data.id.RuleChainId;
|
||||||
@ -63,6 +64,9 @@ public class EntitiesFieldsAsyncLoader {
|
|||||||
case ENTITY_VIEW:
|
case ENTITY_VIEW:
|
||||||
return toEntityFieldsDataAsync(ctx.getEntityViewService().findEntityViewByIdAsync(ctx.getTenantId(), (EntityViewId) originatorId),
|
return toEntityFieldsDataAsync(ctx.getEntityViewService().findEntityViewByIdAsync(ctx.getTenantId(), (EntityViewId) originatorId),
|
||||||
EntityFieldsData::new, ctx);
|
EntityFieldsData::new, ctx);
|
||||||
|
case EDGE:
|
||||||
|
return toEntityFieldsDataAsync(ctx.getEdgeService().findEdgeByIdAsync(ctx.getTenantId(), (EdgeId) originatorId),
|
||||||
|
EntityFieldsData::new, ctx);
|
||||||
default:
|
default:
|
||||||
return Futures.immediateFailedFuture(new TbNodeException("Unexpected originator EntityType: " + originatorId.getEntityType()));
|
return Futures.immediateFailedFuture(new TbNodeException("Unexpected originator EntityType: " + originatorId.getEntityType()));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2024 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.rule.engine.util;
|
||||||
|
|
||||||
|
public class GpsGeofencingEvents {
|
||||||
|
public static final String ENTERED = "Entered";
|
||||||
|
public static final String INSIDE = "Inside";
|
||||||
|
public static final String LEFT = "Left";
|
||||||
|
public static final String OUTSIDE = "Outside";
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2024 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.rule.engine.geo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class GpsGeofencingActionTestCase {
|
||||||
|
|
||||||
|
private EntityId entityId;
|
||||||
|
private Map<EntityId, EntityGeofencingState> entityStates;
|
||||||
|
private boolean msgInside;
|
||||||
|
private boolean reportPresenceStatusOnEachMessage;
|
||||||
|
|
||||||
|
public GpsGeofencingActionTestCase(EntityId entityId, boolean msgInside, boolean reportPresenceStatusOnEachMessage, EntityGeofencingState entityGeofencingState) {
|
||||||
|
this.entityId = entityId;
|
||||||
|
this.msgInside = msgInside;
|
||||||
|
this.reportPresenceStatusOnEachMessage = reportPresenceStatusOnEachMessage;
|
||||||
|
this.entityStates = new HashMap<>();
|
||||||
|
this.entityStates.put(entityId, entityGeofencingState);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,259 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2024 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.rule.engine.geo;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
|
import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest;
|
||||||
|
import org.thingsboard.rule.engine.api.TbContext;
|
||||||
|
import org.thingsboard.rule.engine.api.TbNode;
|
||||||
|
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
|
||||||
|
import org.thingsboard.rule.engine.api.TbNodeException;
|
||||||
|
import org.thingsboard.server.common.data.id.DeviceId;
|
||||||
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
|
import org.thingsboard.server.common.data.msg.TbMsgType;
|
||||||
|
import org.thingsboard.server.common.msg.TbMsg;
|
||||||
|
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||||
|
import org.thingsboard.server.dao.attributes.AttributesService;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.ENTERED;
|
||||||
|
import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.INSIDE;
|
||||||
|
import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.LEFT;
|
||||||
|
import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.OUTSIDE;
|
||||||
|
import static org.thingsboard.server.common.data.msg.TbNodeConnectionType.SUCCESS;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class TbGpsGeofencingActionNodeTest extends AbstractRuleNodeUpgradeTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TbContext ctx;
|
||||||
|
@Mock
|
||||||
|
private AttributesService attributesService;
|
||||||
|
private TbGpsGeofencingActionNode node;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
node = spy(new TbGpsGeofencingActionNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() {
|
||||||
|
node.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<Arguments> givenReportPresenceStatusOnEachMessage_whenOnMsg_thenVerifyOutputMsgType() {
|
||||||
|
DeviceId deviceId = new DeviceId(UUID.randomUUID());
|
||||||
|
long tsNow = System.currentTimeMillis();
|
||||||
|
long tsNowMinusMinuteAndMillis = tsNow - Duration.ofMinutes(1).plusMillis(1).toMillis();
|
||||||
|
return Stream.of(
|
||||||
|
// default config with presenceMonitoringStrategyOnEachMessage false and msgInside true
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, false,
|
||||||
|
new EntityGeofencingState(false, 0, false)), ENTERED),
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, false,
|
||||||
|
new EntityGeofencingState(true, tsNow, false)), SUCCESS),
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, false,
|
||||||
|
new EntityGeofencingState(true, tsNowMinusMinuteAndMillis, false)), INSIDE),
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, false,
|
||||||
|
new EntityGeofencingState(true, tsNow, true)), SUCCESS),
|
||||||
|
// default config with presenceMonitoringStrategyOnEachMessage false and msgInside false
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, false,
|
||||||
|
new EntityGeofencingState(false, 0, false)), LEFT),
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, false,
|
||||||
|
new EntityGeofencingState(false, tsNow, false)), SUCCESS),
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, false,
|
||||||
|
new EntityGeofencingState(false, tsNowMinusMinuteAndMillis, false)), OUTSIDE),
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, false,
|
||||||
|
new EntityGeofencingState(false, tsNow, true)), SUCCESS),
|
||||||
|
// default config with presenceMonitoringStrategyOnEachMessage true and msgInside true
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, true,
|
||||||
|
new EntityGeofencingState(false, 0, false)), ENTERED),
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, true,
|
||||||
|
new EntityGeofencingState(true, tsNow, false)), INSIDE),
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, true,
|
||||||
|
new EntityGeofencingState(true, tsNowMinusMinuteAndMillis, false)), INSIDE),
|
||||||
|
// default config with presenceMonitoringStrategyOnEachMessage true and msgInside false
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, true,
|
||||||
|
new EntityGeofencingState(false, 0, false)), LEFT),
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, true,
|
||||||
|
new EntityGeofencingState(false, tsNow, false)), OUTSIDE),
|
||||||
|
Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, true,
|
||||||
|
new EntityGeofencingState(false, tsNowMinusMinuteAndMillis, false)), OUTSIDE)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource
|
||||||
|
void givenReportPresenceStatusOnEachMessage_whenOnMsg_thenVerifyOutputMsgType(
|
||||||
|
GpsGeofencingActionTestCase gpsGeofencingActionTestCase,
|
||||||
|
String expectedOutput
|
||||||
|
) throws TbNodeException {
|
||||||
|
// GIVEN
|
||||||
|
var config = new TbGpsGeofencingActionNodeConfiguration().defaultConfiguration();
|
||||||
|
config.setReportPresenceStatusOnEachMessage(gpsGeofencingActionTestCase.isReportPresenceStatusOnEachMessage());
|
||||||
|
|
||||||
|
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
|
||||||
|
|
||||||
|
TbMsg msg = gpsGeofencingActionTestCase.isMsgInside() ?
|
||||||
|
getInsideRectangleTbMsg(gpsGeofencingActionTestCase.getEntityId()) :
|
||||||
|
getOutsideRectangleTbMsg(gpsGeofencingActionTestCase.getEntityId());
|
||||||
|
|
||||||
|
when(ctx.getAttributesService()).thenReturn(attributesService);
|
||||||
|
|
||||||
|
ReflectionTestUtils.setField(node, "entityStates", gpsGeofencingActionTestCase.getEntityStates());
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
node.onMsg(ctx, msg);
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
verify(ctx.getAttributesService(), never()).find(any(), any(), any(), anyString());
|
||||||
|
verify(ctx, never()).tellFailure(any(), any(Throwable.class));
|
||||||
|
verify(ctx, never()).enqueueForTellNext(any(), eq(expectedOutput), any(), any());
|
||||||
|
verify(ctx, never()).ack(any());
|
||||||
|
|
||||||
|
if (SUCCESS.equals(expectedOutput)) {
|
||||||
|
verify(ctx).tellSuccess(eq(msg));
|
||||||
|
} else {
|
||||||
|
verify(ctx).tellNext(eq(msg), eq(expectedOutput));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TbMsg getOutsideRectangleTbMsg(EntityId entityId) {
|
||||||
|
return getTbMsg(entityId, getMetadataForNewVersionPolygonPerimeter(),
|
||||||
|
GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLatitude(),
|
||||||
|
GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLongitude());
|
||||||
|
}
|
||||||
|
|
||||||
|
private TbMsg getInsideRectangleTbMsg(EntityId entityId) {
|
||||||
|
return getTbMsg(entityId, getMetadataForNewVersionPolygonPerimeter(),
|
||||||
|
GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLatitude(),
|
||||||
|
GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLongitude());
|
||||||
|
}
|
||||||
|
|
||||||
|
private TbMsg getTbMsg(EntityId entityId, TbMsgMetaData metadata, double latitude, double longitude) {
|
||||||
|
String data = "{\"latitude\": " + latitude + ", \"longitude\": " + longitude + "}";
|
||||||
|
return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, metadata, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TbMsgMetaData getMetadataForNewVersionPolygonPerimeter() {
|
||||||
|
var metadata = new TbMsgMetaData();
|
||||||
|
metadata.putValue("ss_perimeter", GeoUtilTest.SIMPLE_RECT);
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rule nodes upgrade
|
||||||
|
private static Stream<Arguments> givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() {
|
||||||
|
return Stream.of(
|
||||||
|
// default config for version 0
|
||||||
|
Arguments.of(0,
|
||||||
|
"{\n" +
|
||||||
|
" \"minInsideDuration\": 1,\n" +
|
||||||
|
" \"minOutsideDuration\": 1,\n" +
|
||||||
|
" \"minInsideDurationTimeUnit\": \"MINUTES\",\n" +
|
||||||
|
" \"minOutsideDurationTimeUnit\": \"MINUTES\",\n" +
|
||||||
|
" \"latitudeKeyName\": \"latitude\",\n" +
|
||||||
|
" \"longitudeKeyName\": \"longitude\",\n" +
|
||||||
|
" \"perimeterType\": \"POLYGON\",\n" +
|
||||||
|
" \"fetchPerimeterInfoFromMessageMetadata\": true,\n" +
|
||||||
|
" \"perimeterKeyName\": \"ss_perimeter\",\n" +
|
||||||
|
" \"polygonsDefinition\": null,\n" +
|
||||||
|
" \"centerLatitude\": null,\n" +
|
||||||
|
" \"centerLongitude\": null,\n" +
|
||||||
|
" \"range\": null,\n" +
|
||||||
|
" \"rangeUnit\": null\n" +
|
||||||
|
"}\n",
|
||||||
|
true,
|
||||||
|
"{\n" +
|
||||||
|
" \"minInsideDuration\": 1,\n" +
|
||||||
|
" \"minOutsideDuration\": 1,\n" +
|
||||||
|
" \"minInsideDurationTimeUnit\": \"MINUTES\",\n" +
|
||||||
|
" \"minOutsideDurationTimeUnit\": \"MINUTES\",\n" +
|
||||||
|
" \"reportPresenceStatusOnEachMessage\": false,\n" +
|
||||||
|
" \"latitudeKeyName\": \"latitude\",\n" +
|
||||||
|
" \"longitudeKeyName\": \"longitude\",\n" +
|
||||||
|
" \"perimeterType\": \"POLYGON\",\n" +
|
||||||
|
" \"fetchPerimeterInfoFromMessageMetadata\": true,\n" +
|
||||||
|
" \"perimeterKeyName\": \"ss_perimeter\",\n" +
|
||||||
|
" \"polygonsDefinition\": null,\n" +
|
||||||
|
" \"centerLatitude\": null,\n" +
|
||||||
|
" \"centerLongitude\": null,\n" +
|
||||||
|
" \"range\": null,\n" +
|
||||||
|
" \"rangeUnit\": null\n" +
|
||||||
|
"}\n"),
|
||||||
|
// default config for version 1 with upgrade from version 0
|
||||||
|
Arguments.of(0,
|
||||||
|
"{\n" +
|
||||||
|
" \"minInsideDuration\": 1,\n" +
|
||||||
|
" \"minOutsideDuration\": 1,\n" +
|
||||||
|
" \"minInsideDurationTimeUnit\": \"MINUTES\",\n" +
|
||||||
|
" \"minOutsideDurationTimeUnit\": \"MINUTES\",\n" +
|
||||||
|
" \"reportPresenceStatusOnEachMessage\": false,\n" +
|
||||||
|
" \"latitudeKeyName\": \"latitude\",\n" +
|
||||||
|
" \"longitudeKeyName\": \"longitude\",\n" +
|
||||||
|
" \"perimeterType\": \"POLYGON\",\n" +
|
||||||
|
" \"fetchPerimeterInfoFromMessageMetadata\": true,\n" +
|
||||||
|
" \"perimeterKeyName\": \"ss_perimeter\",\n" +
|
||||||
|
" \"polygonsDefinition\": null,\n" +
|
||||||
|
" \"centerLatitude\": null,\n" +
|
||||||
|
" \"centerLongitude\": null,\n" +
|
||||||
|
" \"range\": null,\n" +
|
||||||
|
" \"rangeUnit\": null\n" +
|
||||||
|
"}\n",
|
||||||
|
false,
|
||||||
|
"{\n" +
|
||||||
|
" \"minInsideDuration\": 1,\n" +
|
||||||
|
" \"minOutsideDuration\": 1,\n" +
|
||||||
|
" \"minInsideDurationTimeUnit\": \"MINUTES\",\n" +
|
||||||
|
" \"minOutsideDurationTimeUnit\": \"MINUTES\",\n" +
|
||||||
|
" \"reportPresenceStatusOnEachMessage\": false,\n" +
|
||||||
|
" \"latitudeKeyName\": \"latitude\",\n" +
|
||||||
|
" \"longitudeKeyName\": \"longitude\",\n" +
|
||||||
|
" \"perimeterType\": \"POLYGON\",\n" +
|
||||||
|
" \"fetchPerimeterInfoFromMessageMetadata\": true,\n" +
|
||||||
|
" \"perimeterKeyName\": \"ss_perimeter\",\n" +
|
||||||
|
" \"polygonsDefinition\": null,\n" +
|
||||||
|
" \"centerLatitude\": null,\n" +
|
||||||
|
" \"centerLongitude\": null,\n" +
|
||||||
|
" \"range\": null,\n" +
|
||||||
|
" \"rangeUnit\": null\n" +
|
||||||
|
"}\n")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TbNode getTestNode() {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -37,10 +37,12 @@ import org.thingsboard.server.common.data.Tenant;
|
|||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||||
import org.thingsboard.server.common.data.asset.Asset;
|
import org.thingsboard.server.common.data.asset.Asset;
|
||||||
|
import org.thingsboard.server.common.data.edge.Edge;
|
||||||
import org.thingsboard.server.common.data.id.AlarmId;
|
import org.thingsboard.server.common.data.id.AlarmId;
|
||||||
import org.thingsboard.server.common.data.id.AssetId;
|
import org.thingsboard.server.common.data.id.AssetId;
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
import org.thingsboard.server.common.data.id.CustomerId;
|
||||||
import org.thingsboard.server.common.data.id.DeviceId;
|
import org.thingsboard.server.common.data.id.DeviceId;
|
||||||
|
import org.thingsboard.server.common.data.id.EdgeId;
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
||||||
import org.thingsboard.server.common.data.id.EntityViewId;
|
import org.thingsboard.server.common.data.id.EntityViewId;
|
||||||
@ -52,6 +54,7 @@ import org.thingsboard.server.common.data.rule.RuleChain;
|
|||||||
import org.thingsboard.server.dao.asset.AssetService;
|
import org.thingsboard.server.dao.asset.AssetService;
|
||||||
import org.thingsboard.server.dao.customer.CustomerService;
|
import org.thingsboard.server.dao.customer.CustomerService;
|
||||||
import org.thingsboard.server.dao.device.DeviceService;
|
import org.thingsboard.server.dao.device.DeviceService;
|
||||||
|
import org.thingsboard.server.dao.edge.EdgeService;
|
||||||
import org.thingsboard.server.dao.entityview.EntityViewService;
|
import org.thingsboard.server.dao.entityview.EntityViewService;
|
||||||
import org.thingsboard.server.dao.rule.RuleChainService;
|
import org.thingsboard.server.dao.rule.RuleChainService;
|
||||||
import org.thingsboard.server.dao.tenant.TenantService;
|
import org.thingsboard.server.dao.tenant.TenantService;
|
||||||
@ -95,6 +98,8 @@ public class EntitiesFieldsAsyncLoaderTest {
|
|||||||
private RuleChainService ruleChainServiceMock;
|
private RuleChainService ruleChainServiceMock;
|
||||||
@Mock
|
@Mock
|
||||||
private EntityViewService entityViewServiceMock;
|
private EntityViewService entityViewServiceMock;
|
||||||
|
@Mock
|
||||||
|
private EdgeService edgeServiceMock;
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void setup() {
|
public static void setup() {
|
||||||
@ -108,7 +113,8 @@ public class EntitiesFieldsAsyncLoaderTest {
|
|||||||
EntityType.DEVICE,
|
EntityType.DEVICE,
|
||||||
EntityType.ALARM,
|
EntityType.ALARM,
|
||||||
EntityType.RULE_CHAIN,
|
EntityType.RULE_CHAIN,
|
||||||
EntityType.ENTITY_VIEW
|
EntityType.ENTITY_VIEW,
|
||||||
|
EntityType.EDGE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,6 +234,14 @@ public class EntitiesFieldsAsyncLoaderTest {
|
|||||||
when(ctxMock.getEntityViewService()).thenReturn(entityViewServiceMock);
|
when(ctxMock.getEntityViewService()).thenReturn(entityViewServiceMock);
|
||||||
doReturn(entityView).when(entityViewServiceMock).findEntityViewByIdAsync(eq(TENANT_ID), any());
|
doReturn(entityView).when(entityViewServiceMock).findEntityViewByIdAsync(eq(TENANT_ID), any());
|
||||||
|
|
||||||
|
break;
|
||||||
|
case EDGE:
|
||||||
|
var edge = Futures.immediateFuture(entityDoesNotExist ? null : new Edge(new EdgeId(RANDOM_UUID)));
|
||||||
|
|
||||||
|
when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR);
|
||||||
|
when(ctxMock.getEdgeService()).thenReturn(edgeServiceMock);
|
||||||
|
doReturn(edge).when(edgeServiceMock).findEdgeByIdAsync(eq(TENANT_ID), any());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("Unexpected EntityType: " + entityType);
|
throw new RuntimeException("Unexpected EntityType: " + entityType);
|
||||||
@ -252,6 +266,8 @@ public class EntitiesFieldsAsyncLoaderTest {
|
|||||||
return new RuleChain((RuleChainId) entityId);
|
return new RuleChain((RuleChainId) entityId);
|
||||||
case ENTITY_VIEW:
|
case ENTITY_VIEW:
|
||||||
return new EntityView((EntityViewId) entityId);
|
return new EntityView((EntityViewId) entityId);
|
||||||
|
case EDGE:
|
||||||
|
return new Edge((EdgeId) entityId);
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("Unexpected EntityType: " + entityId.getEntityType());
|
throw new RuntimeException("Unexpected EntityType: " + entityId.getEntityType());
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user