7 changed files with 757 additions and 2 deletions
@ -0,0 +1,156 @@ |
|||
/** |
|||
* Copyright © 2016-2020 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.server.msa.edge; |
|||
|
|||
import com.google.common.util.concurrent.FutureCallback; |
|||
import com.google.common.util.concurrent.Futures; |
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import com.google.common.util.concurrent.MoreExecutors; |
|||
import lombok.Getter; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.checkerframework.checker.nullness.qual.Nullable; |
|||
import org.thingsboard.edge.rpc.EdgeGrpcClient; |
|||
import org.thingsboard.edge.rpc.EdgeRpcClient; |
|||
import org.thingsboard.server.common.data.edge.EdgeEventType; |
|||
import org.thingsboard.server.gen.edge.AlarmUpdateMsg; |
|||
import org.thingsboard.server.gen.edge.AssetUpdateMsg; |
|||
import org.thingsboard.server.gen.edge.DashboardUpdateMsg; |
|||
import org.thingsboard.server.gen.edge.DeviceUpdateMsg; |
|||
import org.thingsboard.server.gen.edge.DownlinkMsg; |
|||
import org.thingsboard.server.gen.edge.DownlinkResponseMsg; |
|||
import org.thingsboard.server.gen.edge.EdgeConfiguration; |
|||
import org.thingsboard.server.gen.edge.EntityDataProto; |
|||
import org.thingsboard.server.gen.edge.RelationUpdateMsg; |
|||
import org.thingsboard.server.gen.edge.RuleChainUpdateMsg; |
|||
import org.thingsboard.server.gen.edge.UplinkResponseMsg; |
|||
|
|||
import java.lang.reflect.Field; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
|
|||
@Slf4j |
|||
public class EdgeImitator { |
|||
|
|||
private String routingKey; |
|||
private String routingSecret; |
|||
|
|||
private EdgeRpcClient edgeRpcClient; |
|||
|
|||
@Getter |
|||
private EdgeStorage storage; |
|||
|
|||
|
|||
public EdgeImitator(String host, int port, String routingKey, String routingSecret) throws NoSuchFieldException, IllegalAccessException { |
|||
edgeRpcClient = new EdgeGrpcClient(); |
|||
storage = new EdgeStorage(); |
|||
this.routingKey = routingKey; |
|||
this.routingSecret = routingSecret; |
|||
setEdgeCredentials("rpcHost", host); |
|||
setEdgeCredentials("rpcPort", port); |
|||
} |
|||
|
|||
private void setEdgeCredentials(String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException { |
|||
Field fieldToSet = edgeRpcClient.getClass().getDeclaredField(fieldName); |
|||
fieldToSet.setAccessible(true); |
|||
fieldToSet.set(edgeRpcClient, value); |
|||
fieldToSet.setAccessible(false); |
|||
} |
|||
|
|||
public void connect() { |
|||
edgeRpcClient.connect(routingKey, routingSecret, |
|||
this::onUplinkResponse, |
|||
this::onEdgeUpdate, |
|||
this::onDownlink, |
|||
this::onError); |
|||
} |
|||
|
|||
public void disconnect() throws InterruptedException { |
|||
edgeRpcClient.disconnect(); |
|||
} |
|||
|
|||
private void onUplinkResponse(UplinkResponseMsg msg) { |
|||
log.info("onUplinkResponse: {}", msg); |
|||
} |
|||
|
|||
private void onEdgeUpdate(EdgeConfiguration edgeConfiguration) { |
|||
storage.setConfiguration(edgeConfiguration); |
|||
} |
|||
|
|||
private void onDownlink(DownlinkMsg downlinkMsg) { |
|||
ListenableFuture<List<Void>> future = processDownlinkMsg(downlinkMsg); |
|||
Futures.addCallback(future, new FutureCallback<List<Void>>() { |
|||
@Override |
|||
public void onSuccess(@Nullable List<Void> result) { |
|||
DownlinkResponseMsg downlinkResponseMsg = DownlinkResponseMsg.newBuilder().setSuccess(true).build(); |
|||
edgeRpcClient.sendDownlinkResponseMsg(downlinkResponseMsg); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(Throwable t) { |
|||
DownlinkResponseMsg downlinkResponseMsg = DownlinkResponseMsg.newBuilder().setSuccess(false).setErrorMsg(t.getMessage()).build(); |
|||
edgeRpcClient.sendDownlinkResponseMsg(downlinkResponseMsg); |
|||
} |
|||
}, MoreExecutors.directExecutor()); |
|||
} |
|||
|
|||
private void onError(Exception e) { |
|||
log.error("Error during Edge lifecycle: ", e); |
|||
} |
|||
|
|||
private ListenableFuture<List<Void>> processDownlinkMsg(DownlinkMsg downlinkMsg) { |
|||
List<ListenableFuture<Void>> result = new ArrayList<>(); |
|||
if (downlinkMsg.getDeviceUpdateMsgList() != null && !downlinkMsg.getDeviceUpdateMsgList().isEmpty()) { |
|||
for (DeviceUpdateMsg deviceUpdateMsg: downlinkMsg.getDeviceUpdateMsgList()) { |
|||
result.add(storage.processEntity(deviceUpdateMsg.getMsgType(), EdgeEventType.DEVICE, new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()))); |
|||
} |
|||
} |
|||
if (downlinkMsg.getAssetUpdateMsgList() != null && !downlinkMsg.getAssetUpdateMsgList().isEmpty()) { |
|||
for (AssetUpdateMsg assetUpdateMsg: downlinkMsg.getAssetUpdateMsgList()) { |
|||
result.add(storage.processEntity(assetUpdateMsg.getMsgType(), EdgeEventType.ASSET, new UUID(assetUpdateMsg.getIdMSB(), assetUpdateMsg.getIdLSB()))); |
|||
} |
|||
} |
|||
if (downlinkMsg.getRuleChainUpdateMsgList() != null && !downlinkMsg.getRuleChainUpdateMsgList().isEmpty()) { |
|||
for (RuleChainUpdateMsg ruleChainUpdateMsg: downlinkMsg.getRuleChainUpdateMsgList()) { |
|||
result.add(storage.processEntity(ruleChainUpdateMsg.getMsgType(), EdgeEventType.RULE_CHAIN, new UUID(ruleChainUpdateMsg.getIdMSB(), ruleChainUpdateMsg.getIdLSB()))); |
|||
} |
|||
} |
|||
if (downlinkMsg.getDashboardUpdateMsgList() != null && !downlinkMsg.getDashboardUpdateMsgList().isEmpty()) { |
|||
for (DashboardUpdateMsg dashboardUpdateMsg: downlinkMsg.getDashboardUpdateMsgList()) { |
|||
result.add(storage.processEntity(dashboardUpdateMsg.getMsgType(), EdgeEventType.DASHBOARD, new UUID(dashboardUpdateMsg.getIdMSB(), dashboardUpdateMsg.getIdLSB()))); |
|||
} |
|||
} |
|||
if (downlinkMsg.getRelationUpdateMsgList() != null && !downlinkMsg.getRelationUpdateMsgList().isEmpty()) { |
|||
for (RelationUpdateMsg relationUpdateMsg: downlinkMsg.getRelationUpdateMsgList()) { |
|||
result.add(storage.processRelation(relationUpdateMsg)); |
|||
} |
|||
} |
|||
if (downlinkMsg.getAlarmUpdateMsgList() != null && !downlinkMsg.getAlarmUpdateMsgList().isEmpty()) { |
|||
for (AlarmUpdateMsg alarmUpdateMsg: downlinkMsg.getAlarmUpdateMsgList()) { |
|||
result.add(storage.processAlarm(alarmUpdateMsg)); |
|||
} |
|||
} |
|||
if (downlinkMsg.getEntityDataList() != null && !downlinkMsg.getEntityDataList().isEmpty()) { |
|||
for (EntityDataProto entityDataProto: downlinkMsg.getEntityDataList()) { |
|||
if (entityDataProto.hasPostTelemetryMsg()) { |
|||
result.add(storage.processTelemetry(new UUID(entityDataProto.getEntityIdMSB(), entityDataProto.getEntityIdLSB()), entityDataProto.getPostTelemetryMsg())); |
|||
} |
|||
} |
|||
} |
|||
return Futures.allAsList(result); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,120 @@ |
|||
/** |
|||
* Copyright © 2016-2020 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.server.msa.edge; |
|||
|
|||
import com.google.common.util.concurrent.Futures; |
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.thingsboard.server.common.data.alarm.AlarmStatus; |
|||
import org.thingsboard.server.common.data.edge.EdgeEventType; |
|||
import org.thingsboard.server.common.data.id.EntityIdFactory; |
|||
import org.thingsboard.server.common.data.relation.EntityRelation; |
|||
import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
|||
import org.thingsboard.server.gen.edge.AlarmUpdateMsg; |
|||
import org.thingsboard.server.gen.edge.EdgeConfiguration; |
|||
import org.thingsboard.server.gen.edge.RelationUpdateMsg; |
|||
import org.thingsboard.server.gen.edge.UpdateMsgType; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
import java.util.UUID; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@Slf4j |
|||
@Getter |
|||
@Setter |
|||
public class EdgeStorage { |
|||
|
|||
private EdgeConfiguration configuration; |
|||
|
|||
private Map<UUID, EdgeEventType> entities; |
|||
private Map<String, AlarmStatus> alarms; |
|||
private List<EntityRelation> relations; |
|||
private Map<UUID, TransportProtos.PostTelemetryMsg> latestTelemetry; |
|||
|
|||
|
|||
public EdgeStorage() { |
|||
entities = new HashMap<>(); |
|||
alarms = new HashMap<>(); |
|||
relations = new ArrayList<>(); |
|||
latestTelemetry = new HashMap<>(); |
|||
} |
|||
|
|||
public ListenableFuture<Void> processEntity(UpdateMsgType msgType, EdgeEventType type, UUID uuid) { |
|||
switch (msgType) { |
|||
case ENTITY_CREATED_RPC_MESSAGE: |
|||
case ENTITY_UPDATED_RPC_MESSAGE: |
|||
entities.put(uuid, type); |
|||
break; |
|||
case ENTITY_DELETED_RPC_MESSAGE: |
|||
entities.remove(uuid); |
|||
break; |
|||
} |
|||
return Futures.immediateFuture(null); |
|||
} |
|||
|
|||
public ListenableFuture<Void> processRelation(RelationUpdateMsg relationMsg) { |
|||
EntityRelation relation = new EntityRelation(); |
|||
relation.setType(relationMsg.getType()); |
|||
relation.setTypeGroup(RelationTypeGroup.valueOf(relationMsg.getTypeGroup())); |
|||
relation.setTo(EntityIdFactory.getByTypeAndUuid(relationMsg.getToEntityType(), new UUID(relationMsg.getToIdMSB(), relationMsg.getToIdLSB()))); |
|||
relation.setFrom(EntityIdFactory.getByTypeAndUuid(relationMsg.getFromEntityType(), new UUID(relationMsg.getFromIdMSB(), relationMsg.getFromIdLSB()))); |
|||
switch (relationMsg.getMsgType()) { |
|||
case ENTITY_CREATED_RPC_MESSAGE: |
|||
case ENTITY_UPDATED_RPC_MESSAGE: |
|||
relations.add(relation); |
|||
break; |
|||
case ENTITY_DELETED_RPC_MESSAGE: |
|||
relations.remove(relation); |
|||
break; |
|||
} |
|||
return Futures.immediateFuture(null); |
|||
} |
|||
|
|||
public ListenableFuture<Void> processAlarm(AlarmUpdateMsg alarmMsg) { |
|||
switch (alarmMsg.getMsgType()) { |
|||
case ENTITY_CREATED_RPC_MESSAGE: |
|||
case ENTITY_UPDATED_RPC_MESSAGE: |
|||
case ALARM_ACK_RPC_MESSAGE: |
|||
case ALARM_CLEAR_RPC_MESSAGE: |
|||
alarms.put(alarmMsg.getType(), AlarmStatus.valueOf(alarmMsg.getStatus())); |
|||
break; |
|||
case ENTITY_DELETED_RPC_MESSAGE: |
|||
alarms.remove(alarmMsg.getName()); |
|||
break; |
|||
} |
|||
return Futures.immediateFuture(null); |
|||
} |
|||
|
|||
public ListenableFuture<Void> processTelemetry(UUID uuid, TransportProtos.PostTelemetryMsg telemetryMsg) { |
|||
latestTelemetry.put(uuid, telemetryMsg); |
|||
return Futures.immediateFuture(null); |
|||
} |
|||
|
|||
public Set<UUID> getEntitiesByType(EdgeEventType type) { |
|||
Map<UUID, EdgeEventType> filtered = entities.entrySet().stream() |
|||
.filter(entry -> entry.getValue().equals(type)) |
|||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
|||
return filtered.keySet(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,341 @@ |
|||
/** |
|||
* Copyright © 2016-2020 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.server.msa.edge; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.junit.*; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.alarm.Alarm; |
|||
import org.thingsboard.server.common.data.alarm.AlarmInfo; |
|||
import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
|||
import org.thingsboard.server.common.data.alarm.AlarmStatus; |
|||
import org.thingsboard.server.common.data.asset.Asset; |
|||
import org.thingsboard.server.common.data.edge.Edge; |
|||
import org.thingsboard.server.common.data.edge.EdgeEventType; |
|||
import org.thingsboard.server.common.data.kv.TsKvEntry; |
|||
import org.thingsboard.server.common.data.page.TextPageLink; |
|||
import org.thingsboard.server.common.data.page.TimePageLink; |
|||
import org.thingsboard.server.common.data.relation.EntityRelation; |
|||
import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
|||
import org.thingsboard.server.common.data.rule.RuleChain; |
|||
import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
|||
import org.thingsboard.server.common.data.rule.RuleChainType; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentials; |
|||
import org.thingsboard.server.gen.edge.EdgeConfiguration; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.msa.AbstractContainerTest; |
|||
|
|||
import java.io.IOException; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
import java.util.UUID; |
|||
|
|||
@Slf4j |
|||
public class EdgeTest extends AbstractContainerTest { |
|||
|
|||
private static EdgeImitator edgeImitator; |
|||
|
|||
@BeforeClass |
|||
public static void init() throws NoSuchFieldException, IllegalAccessException, InterruptedException, IOException { |
|||
restClient.login("tenant@thingsboard.org", "tenant"); |
|||
installation(); |
|||
edgeImitator = new EdgeImitator("localhost", 7070, "routing", "secret"); |
|||
edgeImitator.connect(); |
|||
Thread.sleep(10000); |
|||
} |
|||
|
|||
@Test |
|||
public void testReceivedData() { |
|||
Edge edge = restClient.getTenantEdge("Edge1").get(); |
|||
|
|||
EdgeConfiguration configuration = edgeImitator.getStorage().getConfiguration(); |
|||
Assert.assertNotNull(configuration); |
|||
|
|||
Map<UUID, EdgeEventType> entities = edgeImitator.getStorage().getEntities(); |
|||
Assert.assertFalse(entities.isEmpty()); |
|||
|
|||
Set<UUID> devices = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.DEVICE); |
|||
Assert.assertEquals(1, devices.size()); |
|||
for (Device device: restClient.getEdgeDevices(edge.getId(), new TextPageLink(1)).getData()) { |
|||
Assert.assertTrue(devices.contains(device.getUuidId())); |
|||
} |
|||
|
|||
Set<UUID> ruleChains = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.RULE_CHAIN); |
|||
Assert.assertEquals(1, ruleChains.size()); |
|||
for (RuleChain ruleChain: restClient.getEdgeRuleChains(edge.getId(), new TimePageLink(1)).getData()) { |
|||
Assert.assertTrue(ruleChains.contains(ruleChain.getUuidId())); |
|||
} |
|||
|
|||
Set<UUID> assets = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.ASSET); |
|||
Assert.assertEquals(1, assets.size()); |
|||
for (Asset asset: restClient.getEdgeAssets(edge.getId(), new TextPageLink(1)).getData()) { |
|||
Assert.assertTrue(assets.contains(asset.getUuidId())); |
|||
} |
|||
} |
|||
|
|||
@Test |
|||
public void testDevices() throws Exception { |
|||
Edge edge = restClient.getTenantEdge("Edge1").get(); |
|||
|
|||
Device device = new Device(); |
|||
device.setName("Edge Device 2"); |
|||
device.setType("test"); |
|||
Device savedDevice = restClient.saveDevice(device); |
|||
restClient.assignDeviceToEdge(edge.getId(), savedDevice.getId()); |
|||
|
|||
Thread.sleep(1000); |
|||
Assert.assertTrue(restClient.getEdgeDevices(edge.getId(), new TextPageLink(2)).getData().contains(savedDevice)); |
|||
Set<UUID> devices = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.DEVICE); |
|||
Assert.assertEquals(2, devices.size()); |
|||
Assert.assertTrue(devices.contains(savedDevice.getUuidId())); |
|||
|
|||
restClient.unassignDeviceFromEdge(edge.getId(), savedDevice.getId()); |
|||
Thread.sleep(1000); |
|||
Assert.assertFalse(restClient.getEdgeDevices(edge.getId(), new TextPageLink(2)).getData().contains(savedDevice)); |
|||
devices = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.DEVICE); |
|||
Assert.assertEquals(1, devices.size()); |
|||
Assert.assertFalse(devices.contains(savedDevice.getUuidId())); |
|||
|
|||
restClient.deleteDevice(savedDevice.getId()); |
|||
} |
|||
|
|||
@Test |
|||
public void testAssets() throws Exception { |
|||
Edge edge = restClient.getTenantEdge("Edge1").get(); |
|||
|
|||
Asset asset = new Asset(); |
|||
asset.setName("Edge Asset 2"); |
|||
asset.setType("test"); |
|||
Asset savedAsset = restClient.saveAsset(asset); |
|||
restClient.assignAssetToEdge(edge.getId(), savedAsset.getId()); |
|||
|
|||
Thread.sleep(1000); |
|||
Assert.assertTrue(restClient.getEdgeAssets(edge.getId(), new TextPageLink(2)).getData().contains(savedAsset)); |
|||
Set<UUID> assets = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.ASSET); |
|||
Assert.assertEquals(2, assets.size()); |
|||
Assert.assertTrue(assets.contains(savedAsset.getUuidId())); |
|||
|
|||
restClient.unassignAssetFromEdge(edge.getId(), savedAsset.getId()); |
|||
Thread.sleep(1000); |
|||
Assert.assertFalse(restClient.getEdgeAssets(edge.getId(), new TextPageLink(2)).getData().contains(savedAsset)); |
|||
assets = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.ASSET); |
|||
Assert.assertEquals(1, assets.size()); |
|||
Assert.assertFalse(assets.contains(savedAsset.getUuidId())); |
|||
|
|||
restClient.deleteAsset(savedAsset.getId()); |
|||
} |
|||
|
|||
@Test |
|||
public void testRuleChains() throws Exception { |
|||
Edge edge = restClient.getTenantEdge("Edge1").get(); |
|||
|
|||
RuleChain ruleChain = new RuleChain(); |
|||
ruleChain.setName("Edge Test Rule Chain"); |
|||
ruleChain.setType(RuleChainType.EDGE); |
|||
RuleChain savedRuleChain = restClient.saveRuleChain(ruleChain); |
|||
restClient.assignRuleChainToEdge(edge.getId(), savedRuleChain.getId()); |
|||
|
|||
Thread.sleep(1000); |
|||
Assert.assertTrue(restClient.getEdgeRuleChains(edge.getId(), new TimePageLink(2)).getData().contains(savedRuleChain)); |
|||
Set<UUID> ruleChains = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.RULE_CHAIN); |
|||
Assert.assertEquals(2, ruleChains.size()); |
|||
Assert.assertTrue(ruleChains.contains(savedRuleChain.getUuidId())); |
|||
|
|||
restClient.unassignRuleChainFromEdge(edge.getId(), savedRuleChain.getId()); |
|||
Thread.sleep(1000); |
|||
Assert.assertFalse(restClient.getEdgeRuleChains(edge.getId(), new TimePageLink(2)).getData().contains(savedRuleChain)); |
|||
ruleChains = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.RULE_CHAIN); |
|||
Assert.assertEquals(1, ruleChains.size()); |
|||
Assert.assertFalse(ruleChains.contains(savedRuleChain.getUuidId())); |
|||
|
|||
restClient.deleteRuleChain(savedRuleChain.getId()); |
|||
|
|||
} |
|||
|
|||
@Test |
|||
public void testDashboards() throws Exception { |
|||
Edge edge = restClient.getTenantEdge("Edge1").get(); |
|||
|
|||
Dashboard dashboard = new Dashboard(); |
|||
dashboard.setTitle("Edge Test Dashboard"); |
|||
Dashboard savedDashboard = restClient.saveDashboard(dashboard); |
|||
restClient.assignDashboardToEdge(edge.getId(), savedDashboard.getId()); |
|||
|
|||
Thread.sleep(1000); |
|||
Assert.assertTrue(restClient.getEdgeDashboards(edge.getId(), new TimePageLink(2)).getData().stream().allMatch(dashboardInfo -> dashboardInfo.getUuidId().equals(savedDashboard.getUuidId()))); |
|||
Set<UUID> dashboards = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.DASHBOARD); |
|||
Assert.assertEquals(1, dashboards.size()); |
|||
Assert.assertTrue(dashboards.contains(savedDashboard.getUuidId())); |
|||
|
|||
restClient.unassignDashboardFromEdge(edge.getId(), savedDashboard.getId()); |
|||
Thread.sleep(1000); |
|||
Assert.assertFalse(restClient.getEdgeDashboards(edge.getId(), new TimePageLink(2)).getData().stream().anyMatch(dashboardInfo -> dashboardInfo.getUuidId().equals(savedDashboard.getUuidId()))); |
|||
dashboards = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.DASHBOARD); |
|||
Assert.assertEquals(0, dashboards.size()); |
|||
Assert.assertFalse(dashboards.contains(savedDashboard.getUuidId())); |
|||
|
|||
restClient.deleteDashboard(savedDashboard.getId()); |
|||
|
|||
} |
|||
|
|||
@Test |
|||
public void testRelations() throws InterruptedException { |
|||
Device device = restClient.getTenantDevice("Edge Device 1").get(); |
|||
Asset asset = restClient.getTenantAsset("Edge Asset 1").get(); |
|||
|
|||
EntityRelation relation = new EntityRelation(); |
|||
relation.setType("test"); |
|||
relation.setFrom(device.getId()); |
|||
relation.setTo(asset.getId()); |
|||
relation.setTypeGroup(RelationTypeGroup.COMMON); |
|||
restClient.saveRelation(relation); |
|||
|
|||
Thread.sleep(1000); |
|||
List<EntityRelation> relations = edgeImitator.getStorage().getRelations(); |
|||
Assert.assertEquals(1, relations.size()); |
|||
Assert.assertTrue(relations.contains(relation)); |
|||
restClient.deleteRelation(relation.getFrom(), relation.getType(), relation.getTypeGroup(), relation.getTo()); |
|||
|
|||
Thread.sleep(1000); |
|||
relations = edgeImitator.getStorage().getRelations(); |
|||
Assert.assertEquals(0, relations.size()); |
|||
Assert.assertFalse(relations.contains(relation)); |
|||
} |
|||
|
|||
@Test |
|||
public void testAlarms() throws Exception { |
|||
Device device = restClient.getTenantDevice("Edge Device 1").get(); |
|||
Alarm alarm = new Alarm(); |
|||
alarm.setOriginator(device.getId()); |
|||
alarm.setStatus(AlarmStatus.ACTIVE_UNACK); |
|||
alarm.setType("alarm"); |
|||
alarm.setSeverity(AlarmSeverity.CRITICAL); |
|||
|
|||
Alarm savedAlarm = restClient.saveAlarm(alarm); |
|||
AlarmInfo alarmInfo = restClient.getAlarmInfoById(savedAlarm.getId()).get(); |
|||
Thread.sleep(1000); |
|||
|
|||
Assert.assertEquals(1, edgeImitator.getStorage().getAlarms().size()); |
|||
Assert.assertTrue(edgeImitator.getStorage().getAlarms().containsKey(alarmInfo.getType())); |
|||
Assert.assertEquals(edgeImitator.getStorage().getAlarms().get(alarmInfo.getType()), alarmInfo.getStatus()); |
|||
restClient.ackAlarm(savedAlarm.getId()); |
|||
|
|||
Thread.sleep(1000); |
|||
alarmInfo = restClient.getAlarmInfoById(savedAlarm.getId()).get(); |
|||
Assert.assertTrue(edgeImitator.getStorage().getAlarms().get(alarmInfo.getType()).isAck()); |
|||
Assert.assertEquals(edgeImitator.getStorage().getAlarms().get(alarmInfo.getType()), alarmInfo.getStatus()); |
|||
restClient.clearAlarm(savedAlarm.getId()); |
|||
|
|||
Thread.sleep(1000); |
|||
alarmInfo = restClient.getAlarmInfoById(savedAlarm.getId()).get(); |
|||
Assert.assertTrue(edgeImitator.getStorage().getAlarms().get(alarmInfo.getType()).isAck()); |
|||
Assert.assertTrue(edgeImitator.getStorage().getAlarms().get(alarmInfo.getType()).isCleared()); |
|||
Assert.assertEquals(edgeImitator.getStorage().getAlarms().get(alarmInfo.getType()), alarmInfo.getStatus()); |
|||
|
|||
restClient.deleteAlarm(savedAlarm.getId()); |
|||
|
|||
} |
|||
|
|||
@Ignore |
|||
@Test |
|||
public void testTelemetry() throws Exception { |
|||
Device device = restClient.getTenantDevice("Edge Device 1").get(); |
|||
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get(); |
|||
ResponseEntity response = restClient.getRestTemplate() |
|||
.postForEntity(HTTPS_URL + "/api/v1/{credentialsId}/telemetry", |
|||
"{'test': 25}", |
|||
ResponseEntity.class, |
|||
deviceCredentials.getCredentialsId()); |
|||
Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); |
|||
Thread.sleep(1000); |
|||
List<String> keys = restClient.getTimeseriesKeys(device.getId()); |
|||
List<TsKvEntry> latestTimeseries = restClient.getLatestTimeseries(device.getId(), keys); |
|||
Assert.assertEquals(1, latestTimeseries.size()); |
|||
TsKvEntry tsKvEntry = latestTimeseries.get(0); |
|||
Map<UUID, TransportProtos.PostTelemetryMsg> telemetry = edgeImitator.getStorage().getLatestTelemetry(); |
|||
Assert.assertEquals(1, telemetry.size()); |
|||
Assert.assertTrue(telemetry.containsKey(device.getUuidId())); |
|||
TransportProtos.PostTelemetryMsg telemetryMsg = telemetry.get(device.getUuidId()); |
|||
Assert.assertEquals(1, telemetryMsg.getTsKvListCount()); |
|||
TransportProtos.TsKvListProto tsKv = telemetryMsg.getTsKvListList().get(0); |
|||
Assert.assertEquals(tsKvEntry.getTs(), tsKv.getTs()); |
|||
Assert.assertEquals(1, tsKv.getKvCount()); |
|||
TransportProtos.KeyValueProto keyValue = tsKv.getKvList().get(0); |
|||
Assert.assertEquals(tsKvEntry.getKey(), keyValue.getKey()); |
|||
Assert.assertEquals(tsKvEntry.getValueAsString(), Long.toString(keyValue.getLongV())); |
|||
} |
|||
|
|||
@AfterClass |
|||
public static void destroy() throws InterruptedException { |
|||
uninstallation(); |
|||
edgeImitator.disconnect(); |
|||
} |
|||
|
|||
private static void installation() throws IOException { |
|||
Edge edge = new Edge(); |
|||
edge.setName("Edge1"); |
|||
edge.setType("test"); |
|||
edge.setRoutingKey("routing"); |
|||
edge.setSecret("secret"); |
|||
Edge savedEdge = restClient.saveEdge(edge); |
|||
|
|||
Device device = new Device(); |
|||
device.setName("Edge Device 1"); |
|||
device.setType("test"); |
|||
Device savedDevice = restClient.saveDevice(device); |
|||
restClient.assignDeviceToEdge(savedEdge.getId(), savedDevice.getId()); |
|||
|
|||
Asset asset = new Asset(); |
|||
asset.setName("Edge Asset 1"); |
|||
asset.setType("test"); |
|||
Asset savedAsset = restClient.saveAsset(asset); |
|||
restClient.assignAssetToEdge(savedEdge.getId(), savedAsset.getId()); |
|||
|
|||
ObjectMapper mapper = new ObjectMapper(); |
|||
Class edgeTestClass = EdgeTest.class; |
|||
JsonNode configuration = mapper.readTree(edgeTestClass.getClassLoader().getResourceAsStream("RootRuleChain.json")); |
|||
RuleChain ruleChain = mapper.treeToValue(configuration.get("ruleChain"), RuleChain.class); |
|||
RuleChainMetaData ruleChainMetaData = mapper.treeToValue(configuration.get("metadata"), RuleChainMetaData.class); |
|||
RuleChain savedRuleChain = restClient.saveRuleChain(ruleChain); |
|||
ruleChainMetaData.setRuleChainId(savedRuleChain.getId()); |
|||
restClient.saveRuleChainMetaData(ruleChainMetaData); |
|||
restClient.setRootRuleChain(savedRuleChain.getId()); |
|||
} |
|||
|
|||
private static void uninstallation() { |
|||
Device device = restClient.getTenantDevice("Edge Device 1").get(); |
|||
restClient.deleteDevice(device.getId()); |
|||
|
|||
Asset asset = restClient.getTenantAsset("Edge Asset 1").get(); |
|||
restClient.deleteAsset(asset.getId()); |
|||
|
|||
Edge edge = restClient.getTenantEdge("Edge1").get(); |
|||
restClient.deleteEdge(edge.getId()); |
|||
|
|||
List<RuleChain> ruleChains = restClient.getRuleChains(new TextPageLink(3)).getData(); |
|||
RuleChain oldRoot = ruleChains.stream().filter(ruleChain -> ruleChain.getName().equals("Root Rule Chain")).findAny().get(); |
|||
RuleChain newRoot = ruleChains.stream().filter(ruleChain -> ruleChain.getName().equals("Test Root Rule Chain")).findAny().get(); |
|||
restClient.setRootRuleChain(oldRoot.getId()); |
|||
restClient.deleteRuleChain(newRoot.getId()); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,133 @@ |
|||
{ |
|||
"ruleChain": { |
|||
"additionalInfo": null, |
|||
"name": "Test Root Rule Chain", |
|||
"type": "CORE", |
|||
"firstRuleNodeId": null, |
|||
"root": false, |
|||
"debugMode": false, |
|||
"configuration": null |
|||
}, |
|||
"metadata": { |
|||
"firstNodeIndex": 4, |
|||
"nodes": [ |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 1117, |
|||
"layoutY": 156 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.edge.TbMsgPushToEdgeNode", |
|||
"name": "Push to edge", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"version": 0 |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 825, |
|||
"layoutY": 407 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", |
|||
"name": "RPC Call Request", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"timeoutInSeconds": 60 |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 826, |
|||
"layoutY": 327 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.action.TbLogNode", |
|||
"name": "Log Other", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 827, |
|||
"layoutY": 244 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.action.TbLogNode", |
|||
"name": "Log RPC from Device", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 347, |
|||
"layoutY": 149 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", |
|||
"name": "Message Type Switch", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"version": 0 |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 821, |
|||
"layoutY": 72 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", |
|||
"name": "Save Client Attributes", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"scope": "CLIENT_SCOPE" |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 824, |
|||
"layoutY": 156 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", |
|||
"name": "Save Timeseries", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"defaultTTL": 0 |
|||
} |
|||
} |
|||
], |
|||
"connections": [ |
|||
{ |
|||
"fromIndex": 4, |
|||
"toIndex": 1, |
|||
"type": "RPC Request to Device" |
|||
}, |
|||
{ |
|||
"fromIndex": 4, |
|||
"toIndex": 3, |
|||
"type": "RPC Request from Device" |
|||
}, |
|||
{ |
|||
"fromIndex": 4, |
|||
"toIndex": 6, |
|||
"type": "Post telemetry" |
|||
}, |
|||
{ |
|||
"fromIndex": 4, |
|||
"toIndex": 5, |
|||
"type": "Post attributes" |
|||
}, |
|||
{ |
|||
"fromIndex": 4, |
|||
"toIndex": 2, |
|||
"type": "Other" |
|||
}, |
|||
{ |
|||
"fromIndex": 6, |
|||
"toIndex": 0, |
|||
"type": "Success" |
|||
} |
|||
], |
|||
"ruleChainConnections": null |
|||
} |
|||
} |
|||
Loading…
Reference in new issue