Browse Source

Merge pull request #7914 from volodymyr-babak/edge/rule-chain-in-profiles

[3.5] Added default edge rule chain to asset/device profiles
pull/7952/head
Andrew Shvayka 3 years ago
committed by GitHub
parent
commit
5d08ef9289
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
  2. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  3. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java
  4. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java
  5. 12
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java
  6. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
  7. 3
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java
  8. 16
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationEdgeProcessor.java
  9. 3
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java
  10. 81
      application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
  11. 28
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  12. 55
      application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java
  13. 8
      application/src/test/java/org/thingsboard/server/edge/BaseAssetProfileEdgeTest.java
  14. 34
      application/src/test/java/org/thingsboard/server/edge/BaseDeviceEdgeTest.java
  15. 25
      application/src/test/java/org/thingsboard/server/edge/BaseDeviceProfileEdgeTest.java
  16. 8
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java
  17. 6
      common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfile.java
  18. 6
      common/edge-api/src/main/proto/edge.proto
  19. 2
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  20. 9
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetProfileEntity.java
  21. 9
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java
  22. 8
      dao/src/main/resources/sql/schema-entities.sql
  23. 6
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html
  24. 7
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts
  25. 6
      ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html
  26. 8
      ui-ngx/src/app/modules/home/components/profile/asset-profile.component.ts
  27. 6
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.html
  28. 8
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts
  29. 5
      ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.html
  30. 10
      ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.ts
  31. 1
      ui-ngx/src/app/shared/models/asset.models.ts
  32. 1
      ui-ngx/src/app/shared/models/device.models.ts
  33. 4
      ui-ngx/src/assets/locale/locale.constant-en_US.json

3
application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java

@ -233,9 +233,8 @@ public class ThingsboardInstallService {
log.info("Upgrading ThingsBoard from version 3.4.1 to 3.4.2 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.4.1");
dataUpdateService.updateData("3.4.1");
break;
case "3.4.3":
log.info("Upgrading ThingsBoard from version 3.4.3 to 3.5 ...");
log.info("Upgrading ThingsBoard from version 3.4.3 to 3.5.0 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.4.3");
log.info("Updating system data...");
systemDataLoaderService.updateSystemWidgets();

6
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

@ -193,8 +193,10 @@ public final class EdgeGrpcSession implements Closeable {
private void doSync(EdgeSyncCursor cursor) {
if (cursor.hasNext()) {
log.info("[{}][{}] starting sync process, cursor current idx = {}", edge.getTenantId(), edge.getId(), cursor.getCurrentIdx());
ListenableFuture<UUID> uuidListenableFuture = startProcessingEdgeEvents(cursor.getNext());
EdgeEventFetcher next = cursor.getNext();
log.info("[{}][{}] starting sync process, cursor current idx = {}, class = {}",
edge.getTenantId(), edge.getId(), cursor.getCurrentIdx(), next.getClass().getSimpleName());
ListenableFuture<UUID> uuidListenableFuture = startProcessingEdgeEvents(next);
Futures.addCallback(uuidListenableFuture, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable UUID result) {

4
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java

@ -49,6 +49,10 @@ public class AssetProfileMsgConstructor {
if (assetProfile.getImage() != null) {
builder.setImage(ByteString.copyFrom(assetProfile.getImage().getBytes(StandardCharsets.UTF_8)));
}
if (assetProfile.getDefaultEdgeRuleChainId() != null) {
builder.setDefaultRuleChainIdMSB(assetProfile.getDefaultEdgeRuleChainId().getId().getMostSignificantBits())
.setDefaultRuleChainIdLSB(assetProfile.getDefaultEdgeRuleChainId().getId().getLeastSignificantBits());
}
return builder.build();
}

4
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java

@ -66,6 +66,10 @@ public class DeviceMsgConstructor {
builder.setFirmwareIdMSB(device.getFirmwareId().getId().getMostSignificantBits())
.setFirmwareIdLSB(device.getFirmwareId().getId().getLeastSignificantBits());
}
if (device.getSoftwareId() != null) {
builder.setSoftwareIdMSB(device.getSoftwareId().getId().getMostSignificantBits())
.setSoftwareIdLSB(device.getSoftwareId().getId().getLeastSignificantBits());
}
if (conflictName != null) {
builder.setConflictName(conflictName);
}

12
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java

@ -65,6 +65,18 @@ public class DeviceProfileMsgConstructor {
builder.setFirmwareIdMSB(deviceProfile.getFirmwareId().getId().getMostSignificantBits())
.setFirmwareIdLSB(deviceProfile.getFirmwareId().getId().getLeastSignificantBits());
}
if (deviceProfile.getSoftwareId() != null) {
builder.setSoftwareIdMSB(deviceProfile.getSoftwareId().getId().getMostSignificantBits())
.setSoftwareIdLSB(deviceProfile.getSoftwareId().getId().getLeastSignificantBits());
}
if (deviceProfile.getDefaultEdgeRuleChainId() != null) {
builder.setDefaultRuleChainIdMSB(deviceProfile.getDefaultEdgeRuleChainId().getId().getMostSignificantBits())
.setDefaultRuleChainIdLSB(deviceProfile.getDefaultEdgeRuleChainId().getId().getLeastSignificantBits());
}
if (deviceProfile.getDefaultDashboardId() != null) {
builder.setDefaultDashboardIdMSB(deviceProfile.getDefaultDashboardId().getId().getMostSignificantBits())
.setDefaultDashboardIdLSB(deviceProfile.getDefaultDashboardId().getId().getLeastSignificantBits());
}
return builder.build();
}

6
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java

@ -306,6 +306,12 @@ public abstract class BaseEdgeProcessor {
return futures;
}
protected ListenableFuture<Void> handleUnsupportedMsgType(UpdateMsgType msgType) {
String errMsg = String.format("Unsupported msg type %s", msgType);
log.error(errMsg);
return Futures.immediateFailedFuture(new RuntimeException(errMsg));
}
protected UpdateMsgType getUpdateMsgType(EdgeEventActionType actionType) {
switch (actionType) {
case UPDATED:

3
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java

@ -132,8 +132,7 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
return Futures.immediateFuture(null);
case UNRECOGNIZED:
default:
log.error("Unsupported msg type {}", deviceUpdateMsg.getMsgType());
return Futures.immediateFailedFuture(new RuntimeException("Unsupported msg type " + deviceUpdateMsg.getMsgType()));
return handleUnsupportedMsgType(deviceUpdateMsg.getMsgType());
}
}

16
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationEdgeProcessor.java

@ -80,23 +80,25 @@ public class RelationEdgeProcessor extends BaseEdgeProcessor {
case ENTITY_UPDATED_RPC_MESSAGE:
if (isEntityExists(tenantId, entityRelation.getTo())
&& isEntityExists(tenantId, entityRelation.getFrom())) {
relationService.saveRelationAsync(tenantId, entityRelation);
return Futures.transform(relationService.saveRelationAsync(tenantId, entityRelation),
(result) -> null, dbCallbackExecutorService);
} else {
log.warn("Skipping relating update msg because from/to entity doesn't exists on edge, {}", relationUpdateMsg);
return Futures.immediateFuture(null);
}
break;
case ENTITY_DELETED_RPC_MESSAGE:
relationService.deleteRelation(tenantId, entityRelation);
break;
return Futures.transform(relationService.deleteRelationAsync(tenantId, entityRelation),
(result) -> null, dbCallbackExecutorService);
case UNRECOGNIZED:
log.error("Unsupported msg type");
default:
return handleUnsupportedMsgType(relationUpdateMsg.getMsgType());
}
return Futures.immediateFuture(null);
} catch (Exception e) {
log.error("Failed to process relation update msg [{}]", relationUpdateMsg, e);
return Futures.immediateFailedFuture(new RuntimeException("Failed to process relation update msg", e));
}
}
private boolean isEntityExists(TenantId tenantId, EntityId entityId) throws ThingsboardException {
switch (entityId.getEntityType()) {
case DEVICE:

3
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryEdgeProcessor.java

@ -22,6 +22,7 @@ import com.google.common.util.concurrent.SettableFuture;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
@ -334,7 +335,7 @@ public class TelemetryEdgeProcessor extends BaseEdgeProcessor {
return null;
}
return constructEntityDataProtoMsg(entityId, edgeEvent.getAction(),
JsonUtils.parse(JacksonUtil.OBJECT_MAPPER.writeValueAsString(edgeEvent.getBody())));
JsonParser.parseString(JacksonUtil.OBJECT_MAPPER.writeValueAsString(edgeEvent.getBody())));
}
private DownlinkMsg constructEntityDataProtoMsg(EntityId entityId, EdgeEventActionType actionType, JsonElement entityData) {

81
application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java

@ -137,49 +137,50 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
}
String scope = attributesRequestMsg.getScope();
ListenableFuture<List<AttributeKvEntry>> findAttrFuture = attributesService.findAll(tenantId, entityId, scope);
return Futures.transformAsync(findAttrFuture, ssAttributes -> {
if (ssAttributes == null || ssAttributes.isEmpty()) {
log.trace("[{}][{}] No attributes found for entity {} [{}]", tenantId,
edge.getName(),
entityId.getEntityType(),
entityId.getId());
return Futures.immediateFuture(null);
}
return processEntityAttributesAndAddToEdgeQueue(tenantId, entityId, edge, entityType, scope, ssAttributes, attributesRequestMsg);
}, dbCallbackExecutorService);
return Futures.transformAsync(findAttrFuture, ssAttributes
-> processEntityAttributesAndAddToEdgeQueue(tenantId, entityId, edge, entityType, scope, ssAttributes, attributesRequestMsg),
dbCallbackExecutorService);
}
private ListenableFuture<Void> processEntityAttributesAndAddToEdgeQueue(TenantId tenantId, EntityId entityId, Edge edge,
EdgeEventType entityType, String scope, List<AttributeKvEntry> ssAttributes,
AttributesRequestMsg attributesRequestMsg) {
try {
Map<String, Object> entityData = new HashMap<>();
ObjectNode attributes = JacksonUtil.OBJECT_MAPPER.createObjectNode();
for (AttributeKvEntry attr : ssAttributes) {
if (DefaultDeviceStateService.PERSISTENT_ATTRIBUTES.contains(attr.getKey())
&& !DefaultDeviceStateService.INACTIVITY_TIMEOUT.equals(attr.getKey())) {
continue;
ListenableFuture<Void> future;
if (ssAttributes == null || ssAttributes.isEmpty()) {
log.trace("[{}][{}] No attributes found for entity {} [{}]", tenantId,
edge.getName(),
entityId.getEntityType(),
entityId.getId());
future = Futures.immediateFuture(null);
} else {
Map<String, Object> entityData = new HashMap<>();
ObjectNode attributes = JacksonUtil.OBJECT_MAPPER.createObjectNode();
for (AttributeKvEntry attr : ssAttributes) {
if (DefaultDeviceStateService.PERSISTENT_ATTRIBUTES.contains(attr.getKey())
&& !DefaultDeviceStateService.INACTIVITY_TIMEOUT.equals(attr.getKey())) {
continue;
}
if (attr.getDataType() == DataType.BOOLEAN && attr.getBooleanValue().isPresent()) {
attributes.put(attr.getKey(), attr.getBooleanValue().get());
} else if (attr.getDataType() == DataType.DOUBLE && attr.getDoubleValue().isPresent()) {
attributes.put(attr.getKey(), attr.getDoubleValue().get());
} else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) {
attributes.put(attr.getKey(), attr.getLongValue().get());
} else {
attributes.put(attr.getKey(), attr.getValueAsString());
}
}
if (attr.getDataType() == DataType.BOOLEAN && attr.getBooleanValue().isPresent()) {
attributes.put(attr.getKey(), attr.getBooleanValue().get());
} else if (attr.getDataType() == DataType.DOUBLE && attr.getDoubleValue().isPresent()) {
attributes.put(attr.getKey(), attr.getDoubleValue().get());
} else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) {
attributes.put(attr.getKey(), attr.getLongValue().get());
if (attributes.size() > 0) {
entityData.put("kv", attributes);
entityData.put("scope", scope);
JsonNode body = JacksonUtil.OBJECT_MAPPER.valueToTree(entityData);
log.debug("Sending attributes data msg, entityId [{}], attributes [{}]", entityId, body);
future = saveEdgeEvent(tenantId, edge.getId(), entityType, EdgeEventActionType.ATTRIBUTES_UPDATED, entityId, body);
} else {
attributes.put(attr.getKey(), attr.getValueAsString());
future = Futures.immediateFuture(null);
}
}
ListenableFuture<Void> future;
if (attributes.size() > 0) {
entityData.put("kv", attributes);
entityData.put("scope", scope);
JsonNode body = JacksonUtil.OBJECT_MAPPER.valueToTree(entityData);
log.debug("Sending attributes data msg, entityId [{}], attributes [{}]", entityId, body);
future = saveEdgeEvent(tenantId, edge.getId(), entityType, EdgeEventActionType.ATTRIBUTES_UPDATED, entityId, body);
} else {
future = Futures.immediateFuture(null);
}
return Futures.transformAsync(future, v -> processLatestTimeseriesAndAddToEdgeQueue(tenantId, entityId, edge, entityType), dbCallbackExecutorService);
} catch (Exception e) {
String errMsg = String.format("[%s] Failed to save attribute updates to the edge [%s]", edge.getId(), attributesRequestMsg);
@ -199,16 +200,18 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
entityId.getId());
return Futures.immediateFuture(null);
}
List<ListenableFuture<Void>> futures = new ArrayList<>();
Map<Long, Map<String, Object>> tsData = new HashMap<>();
for (TsKvEntry tsKvEntry : tsKvEntries) {
if (DefaultDeviceStateService.PERSISTENT_ATTRIBUTES.contains(tsKvEntry.getKey())) {
continue;
}
ObjectNode entityBody = JacksonUtil.OBJECT_MAPPER.createObjectNode();
ObjectNode ts = JacksonUtil.OBJECT_MAPPER.createObjectNode();
ts.put(tsKvEntry.getKey(), tsKvEntry.getValueAsString());
entityBody.set("data", ts);
entityBody.put("ts", tsKvEntry.getTs());
tsData.computeIfAbsent(tsKvEntry.getTs(), k -> new HashMap<>()).put(tsKvEntry.getKey(), tsKvEntry.getValue());
}
List<ListenableFuture<Void>> futures = new ArrayList<>();
for (Map.Entry<Long, Map<String, Object>> entry : tsData.entrySet()) {
Map<String, Object> entityBody = new HashMap<>();
entityBody.put("data", entry.getValue());
entityBody.put("ts", entry.getKey());
futures.add(saveEdgeEvent(tenantId, edge.getId(), entityType, EdgeEventActionType.TIMESERIES_UPDATED, entityId, JacksonUtil.valueToTree(entityBody)));
}
return Futures.transform(Futures.allAsList(futures), v -> null, dbCallbackExecutorService);

28
application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java

@ -677,6 +677,34 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
log.error("Failed updating schema!!!", e);
}
break;
case "3.4.3":
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
log.info("Updating schema ...");
if (isOldSchema(conn, 3004002)) {
try {
conn.createStatement().execute("ALTER TABLE asset_profile ADD COLUMN default_edge_rule_chain_id uuid"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
} catch (Exception e) {
}
try {
conn.createStatement().execute("ALTER TABLE device_profile ADD COLUMN default_edge_rule_chain_id uuid"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
} catch (Exception e) {
}
try {
conn.createStatement().execute("ALTER TABLE asset_profile ADD CONSTRAINT fk_default_edge_rule_chain_asset_profile FOREIGN KEY (default_edge_rule_chain_id) REFERENCES rule_chain(id)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
} catch (Exception e) {
}
try {
conn.createStatement().execute("ALTER TABLE device_profile ADD CONSTRAINT fk_default_edge_rule_chain_device_profile FOREIGN KEY (default_edge_rule_chain_id) REFERENCES rule_chain(id)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
} catch (Exception e) {
}
conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3005000;");
}
log.info("Schema updated.");
} catch (Exception e) {
log.error("Failed updating schema!!!", e);
}
break;
default:
throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
}

55
application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java

@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.OtaPackageInfo;
@ -51,11 +52,13 @@ import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.query.EntityKeyValueType;
@ -63,6 +66,7 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue;
import org.thingsboard.server.common.data.query.NumericFilterPredicate;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.dao.edge.EdgeEventService;
@ -500,13 +504,13 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
return doPost("/api/asset", asset, Asset.class);
}
protected OtaPackageInfo saveOtaPackageInfo(DeviceProfileId deviceProfileId) {
protected OtaPackageInfo saveOtaPackageInfo(DeviceProfileId deviceProfileId, OtaPackageType type) {
SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle("Firmware Edge " + StringUtils.randomAlphanumeric(3));
firmwareInfo.setType(type);
firmwareInfo.setTitle(type.name() + " Edge " + StringUtils.randomAlphanumeric(3));
firmwareInfo.setVersion("v1.0");
firmwareInfo.setTag("My firmware #1 v1.0");
firmwareInfo.setTag("My " + type.name() + " #1 v1.0");
firmwareInfo.setUsesUrl(true);
firmwareInfo.setUrl("http://localhost:8080/v1/package");
firmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode());
@ -541,6 +545,49 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
Assert.assertEquals(source.hashCode(), target.hashCode());
}
protected RuleChainId createEdgeRuleChainAndAssignToEdge(String ruleChainName) throws Exception {
edgeImitator.expectMessageAmount(1);
RuleChain ruleChain = new RuleChain();
ruleChain.setName(ruleChainName);
ruleChain.setType(RuleChainType.EDGE);
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
doPost("/api/edge/" + edge.getUuidId()
+ "/ruleChain/" + savedRuleChain.getUuidId(), RuleChain.class);
Assert.assertTrue(edgeImitator.waitForMessages());
return savedRuleChain.getId();
}
protected void unAssignFromEdgeAndDeleteRuleChain(RuleChainId ruleChainId) throws Exception {
edgeImitator.expectMessageAmount(1);
doDelete("/api/edge/" + edge.getUuidId()
+ "/ruleChain/" + ruleChainId.getId(), RuleChain.class);
Assert.assertTrue(edgeImitator.waitForMessages());
// delete rule chain
doDelete("/api/ruleChain/" + ruleChainId.getId())
.andExpect(status().isOk());
}
protected DashboardId createDashboardAndAssignToEdge(String dashboardName) throws Exception {
edgeImitator.expectMessageAmount(1);
Dashboard dashboard = new Dashboard();
dashboard.setTitle(dashboardName);
Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class);
doPost("/api/edge/" + edge.getUuidId()
+ "/dashboard/" + savedDashboard.getUuidId(), Dashboard.class);
Assert.assertTrue(edgeImitator.waitForMessages());
return savedDashboard.getId();
}
protected void unAssignFromEdgeAndDeleteDashboard(DashboardId dashboardId) throws Exception {
edgeImitator.expectMessageAmount(1);
doDelete("/api/edge/" + edge.getUuidId()
+ "/dashboard/" + dashboardId.getId(), RuleChain.class);
Assert.assertTrue(edgeImitator.waitForMessages());
// delete dashboard
doDelete("/api/dashboard/" + dashboardId.getId())
.andExpect(status().isOk());
}
}

8
application/src/test/java/org/thingsboard/server/edge/BaseAssetProfileEdgeTest.java

@ -20,6 +20,7 @@ import com.google.protobuf.ByteString;
import org.junit.Assert;
import org.junit.Test;
import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
@ -31,8 +32,11 @@ abstract public class BaseAssetProfileEdgeTest extends AbstractEdgeTest {
@Test
public void testAssetProfiles() throws Exception {
RuleChainId buildingsRuleChainId = createEdgeRuleChainAndAssignToEdge("Buildings Rule Chain");
// create asset profile
AssetProfile assetProfile = this.createAssetProfile("Building");
assetProfile.setDefaultEdgeRuleChainId(buildingsRuleChainId);
edgeImitator.expectMessageAmount(1);
assetProfile = doPost("/api/assetProfile", assetProfile, AssetProfile.class);
Assert.assertTrue(edgeImitator.waitForMessages());
@ -43,6 +47,8 @@ abstract public class BaseAssetProfileEdgeTest extends AbstractEdgeTest {
Assert.assertEquals(assetProfile.getUuidId().getMostSignificantBits(), assetProfileUpdateMsg.getIdMSB());
Assert.assertEquals(assetProfile.getUuidId().getLeastSignificantBits(), assetProfileUpdateMsg.getIdLSB());
Assert.assertEquals("Building", assetProfileUpdateMsg.getName());
Assert.assertEquals(buildingsRuleChainId.getId().getMostSignificantBits(), assetProfileUpdateMsg.getDefaultRuleChainIdMSB());
Assert.assertEquals(buildingsRuleChainId.getId().getLeastSignificantBits(), assetProfileUpdateMsg.getDefaultRuleChainIdLSB());
// update asset profile
assetProfile.setImage("IMAGE");
@ -66,5 +72,7 @@ abstract public class BaseAssetProfileEdgeTest extends AbstractEdgeTest {
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType());
Assert.assertEquals(assetProfile.getUuidId().getMostSignificantBits(), assetProfileUpdateMsg.getIdMSB());
Assert.assertEquals(assetProfile.getUuidId().getLeastSignificantBits(), assetProfileUpdateMsg.getIdLSB());
unAssignFromEdgeAndDeleteRuleChain(buildingsRuleChainId);
}
}

34
application/src/test/java/org/thingsboard/server/edge/BaseDeviceEdgeTest.java

@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials;
@ -174,7 +175,7 @@ abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest {
// create device and assign to edge; update device
Device savedDevice = saveDeviceOnCloudAndVerifyDeliveryToEdge();
verifyUpdateFirmwareIdAndDeviceData(savedDevice);
verifyUpdateFirmwareIdSoftwareIdAndDeviceData(savedDevice);
// update device credentials - ACCESS_TOKEN
edgeImitator.expectMessageAmount(1);
@ -210,15 +211,20 @@ abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest {
Assert.assertEquals(deviceCredentials.getCredentialsValue(), deviceCredentialsUpdateMsg.getCredentialsValue());
}
private void verifyUpdateFirmwareIdAndDeviceData(Device savedDevice) throws InterruptedException {
// create ota package
private void verifyUpdateFirmwareIdSoftwareIdAndDeviceData(Device savedDevice) throws InterruptedException {
// create ota packages
edgeImitator.expectMessageAmount(1);
OtaPackageInfo firmwareOtaPackageInfo = saveOtaPackageInfo(thermostatDeviceProfile.getId());
OtaPackageInfo firmwareOtaPackageInfo = saveOtaPackageInfo(thermostatDeviceProfile.getId(), OtaPackageType.FIRMWARE);
Assert.assertTrue(edgeImitator.waitForMessages());
edgeImitator.expectMessageAmount(1);
OtaPackageInfo softwareOtaPackageInfo = saveOtaPackageInfo(thermostatDeviceProfile.getId(), OtaPackageType.SOFTWARE);
Assert.assertTrue(edgeImitator.waitForMessages());
// update device
edgeImitator.expectMessageAmount(1);
savedDevice.setFirmwareId(firmwareOtaPackageInfo.getId());
savedDevice.setSoftwareId(softwareOtaPackageInfo.getId());
DeviceData deviceData = new DeviceData();
deviceData.setConfiguration(new DefaultDeviceConfiguration());
@ -239,6 +245,8 @@ abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest {
Assert.assertEquals(savedDevice.getType(), deviceUpdateMsg.getType());
Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getFirmwareIdMSB());
Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getFirmwareIdLSB());
Assert.assertEquals(softwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getSoftwareIdMSB());
Assert.assertEquals(softwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getSoftwareIdLSB());
Optional<DeviceData> deviceDataOpt =
dataDecodingEncodingService.decode(deviceUpdateMsg.getDeviceDataBytes().toByteArray());
Assert.assertTrue(deviceDataOpt.isPresent());
@ -693,7 +701,7 @@ abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest {
public void testVerifyDeliveryOfLatestTimeseriesOnAttributesRequest() throws Exception {
Device device = findDeviceByName("Edge Device 1");
JsonNode timeseriesData = mapper.readTree("{\"temperature\":25}");
JsonNode timeseriesData = mapper.readTree("{\"temperature\":25, \"isEnabled\": true}");
doPost("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE,
timeseriesData);
@ -732,9 +740,17 @@ abstract public class BaseDeviceEdgeTest extends AbstractEdgeTest {
TransportProtos.PostTelemetryMsg timeseriesUpdatedMsg = latestEntityDataMsg.getPostTelemetryMsg();
Assert.assertEquals(1, timeseriesUpdatedMsg.getTsKvListList().size());
TransportProtos.TsKvListProto tsKvListProto = timeseriesUpdatedMsg.getTsKvListList().get(0);
Assert.assertEquals(1, tsKvListProto.getKvList().size());
TransportProtos.KeyValueProto keyValueProto = tsKvListProto.getKvList().get(0);
Assert.assertEquals(25, keyValueProto.getLongV());
Assert.assertEquals("temperature", keyValueProto.getKey());
Assert.assertEquals(2, tsKvListProto.getKvList().size());
for (TransportProtos.KeyValueProto keyValueProto : tsKvListProto.getKvList()) {
if ("temperature".equals(keyValueProto.getKey())) {
Assert.assertEquals(TransportProtos.KeyValueType.LONG_V, keyValueProto.getType());
Assert.assertEquals(25, keyValueProto.getLongV());
} else if ("isEnabled".equals(keyValueProto.getKey())) {
Assert.assertEquals(TransportProtos.KeyValueType.BOOLEAN_V, keyValueProto.getType());
Assert.assertTrue(keyValueProto.getBoolV());
} else {
Assert.fail("Unexpected key: " + keyValueProto.getKey());
}
}
}
}

25
application/src/test/java/org/thingsboard/server/edge/BaseDeviceProfileEdgeTest.java

@ -36,7 +36,10 @@ import org.thingsboard.server.common.data.device.profile.lwm2m.TelemetryMappingC
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.AbstractLwM2MBootstrapServerCredential;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.NoSecLwM2MBootstrapServerCredential;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping;
import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
import org.thingsboard.server.common.data.transport.snmp.config.impl.TelemetryQueryingSnmpCommunicationConfig;
@ -55,8 +58,11 @@ abstract public class BaseDeviceProfileEdgeTest extends AbstractEdgeTest {
@Test
public void testDeviceProfiles() throws Exception {
RuleChainId thermostatsRuleChainId = createEdgeRuleChainAndAssignToEdge("Thermostats Rule Chain");
// create device profile
DeviceProfile deviceProfile = this.createDeviceProfile("ONE_MORE_DEVICE_PROFILE", null);
deviceProfile.setDefaultEdgeRuleChainId(thermostatsRuleChainId);
extendDeviceProfileData(deviceProfile);
edgeImitator.expectMessageAmount(1);
deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
@ -67,13 +73,23 @@ abstract public class BaseDeviceProfileEdgeTest extends AbstractEdgeTest {
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType());
Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB());
Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB());
Assert.assertEquals(thermostatsRuleChainId.getId().getMostSignificantBits(), deviceProfileUpdateMsg.getDefaultRuleChainIdMSB());
Assert.assertEquals(thermostatsRuleChainId.getId().getLeastSignificantBits(), deviceProfileUpdateMsg.getDefaultRuleChainIdLSB());
// update device profile
OtaPackageInfo firmwareOtaPackageInfo = saveOtaPackageInfo(deviceProfile.getId());
edgeImitator.expectMessageAmount(1);
OtaPackageInfo firmwareOtaPackageInfo = saveOtaPackageInfo(deviceProfile.getId(), OtaPackageType.FIRMWARE);
Assert.assertTrue(edgeImitator.waitForMessages());
edgeImitator.expectMessageAmount(1);
OtaPackageInfo softwareOtaPackageInfo = saveOtaPackageInfo(deviceProfile.getId(), OtaPackageType.SOFTWARE);
Assert.assertTrue(edgeImitator.waitForMessages());
DashboardId thermostatsDashboardId = createDashboardAndAssignToEdge("Thermostats Dashboard");
deviceProfile.setFirmwareId(firmwareOtaPackageInfo.getId());
deviceProfile.setSoftwareId(softwareOtaPackageInfo.getId());
deviceProfile.setDefaultDashboardId(thermostatsDashboardId);
edgeImitator.expectMessageAmount(1);
deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
Assert.assertTrue(edgeImitator.waitForMessages());
@ -82,6 +98,10 @@ abstract public class BaseDeviceProfileEdgeTest extends AbstractEdgeTest {
deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage;
Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getFirmwareIdMSB());
Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getFirmwareIdLSB());
Assert.assertEquals(softwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getSoftwareIdMSB());
Assert.assertEquals(softwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getSoftwareIdLSB());
Assert.assertEquals(thermostatsDashboardId.getId().getMostSignificantBits(), deviceProfileUpdateMsg.getDefaultDashboardIdMSB());
Assert.assertEquals(thermostatsDashboardId.getId().getLeastSignificantBits(), deviceProfileUpdateMsg.getDefaultDashboardIdLSB());
// delete profile
edgeImitator.expectMessageAmount(1);
@ -94,6 +114,9 @@ abstract public class BaseDeviceProfileEdgeTest extends AbstractEdgeTest {
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType());
Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB());
Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB());
unAssignFromEdgeAndDeleteRuleChain(thermostatsRuleChainId);
unAssignFromEdgeAndDeleteDashboard(thermostatsDashboardId);
}
@Test

8
common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java

@ -16,7 +16,6 @@
package org.thingsboard.server.common.data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ -28,7 +27,6 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
@ -93,6 +91,11 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
@ApiModelProperty(position = 10, value = "Reference to the software OTA package. If present, the specified package will be used as default device software. ")
private OtaPackageId softwareId;
@ApiModelProperty(position = 17, value = "Reference to the edge rule chain. " +
"If present, the specified edge rule chain will be used on the edge to process all messages related to device, including telemetry, attribute updates, etc. " +
"Otherwise, the edge root rule chain will be used to process those messages.")
private RuleChainId defaultEdgeRuleChainId;
private DeviceProfileId externalId;
public DeviceProfile() {
@ -117,6 +120,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
this.firmwareId = deviceProfile.getFirmwareId();
this.softwareId = deviceProfile.getSoftwareId();
this.defaultEdgeRuleChainId = deviceProfile.getDefaultEdgeRuleChainId();
this.externalId = deviceProfile.getExternalId();
}

6
common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfile.java

@ -68,6 +68,11 @@ public class AssetProfile extends SearchTextBased<AssetProfileId> implements Has
"Otherwise, the 'Main' queue will be used to store those messages.")
private String defaultQueueName;
@ApiModelProperty(position = 13, value = "Reference to the edge rule chain. " +
"If present, the specified edge rule chain will be used on the edge to process all messages related to asset, including asset updates, telemetry, attribute updates, etc. " +
"Otherwise, the edge root rule chain will be used to process those messages.")
private RuleChainId defaultEdgeRuleChainId;
private AssetProfileId externalId;
public AssetProfile() {
@ -88,6 +93,7 @@ public class AssetProfile extends SearchTextBased<AssetProfileId> implements Has
this.defaultRuleChainId = assetProfile.getDefaultRuleChainId();
this.defaultDashboardId = assetProfile.getDefaultDashboardId();
this.defaultQueueName = assetProfile.getDefaultQueueName();
this.defaultEdgeRuleChainId = assetProfile.getDefaultEdgeRuleChainId();
this.externalId = assetProfile.getExternalId();
}

6
common/edge-api/src/main/proto/edge.proto

@ -203,6 +203,8 @@ message DeviceUpdateMsg {
optional int64 firmwareIdMSB = 13;
optional int64 firmwareIdLSB = 14;
optional bytes deviceDataBytes = 15;
optional int64 softwareIdMSB = 16;
optional int64 softwareIdLSB = 17;
}
message DeviceProfileUpdateMsg {
@ -223,6 +225,10 @@ message DeviceProfileUpdateMsg {
optional bytes image = 15;
optional int64 firmwareIdMSB = 16;
optional int64 firmwareIdLSB = 17;
optional int64 softwareIdMSB = 18;
optional int64 softwareIdLSB = 19;
optional int64 defaultDashboardIdMSB = 20;
optional int64 defaultDashboardIdLSB = 21;
}
message AssetProfileUpdateMsg {

2
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -185,6 +185,7 @@ public class ModelConstants {
public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key";
public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id";
public static final String DEVICE_PROFILE_SOFTWARE_ID_PROPERTY = "software_id";
public static final String DEVICE_PROFILE_DEFAULT_EDGE_RULE_CHAIN_ID_PROPERTY = "default_edge_rule_chain_id";
/**
* Asset profile constants.
@ -198,6 +199,7 @@ public class ModelConstants {
public static final String ASSET_PROFILE_DEFAULT_RULE_CHAIN_ID_PROPERTY = "default_rule_chain_id";
public static final String ASSET_PROFILE_DEFAULT_DASHBOARD_ID_PROPERTY = "default_dashboard_id";
public static final String ASSET_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY = "default_queue_name";
public static final String ASSET_PROFILE_DEFAULT_EDGE_RULE_CHAIN_ID_PROPERTY = "default_edge_rule_chain_id";
/**
* Cassandra entityView constants.

9
dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetProfileEntity.java

@ -64,6 +64,9 @@ public final class AssetProfileEntity extends BaseSqlEntity<AssetProfile> implem
@Column(name = ModelConstants.ASSET_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY)
private String defaultQueueName;
@Column(name = ModelConstants.ASSET_PROFILE_DEFAULT_EDGE_RULE_CHAIN_ID_PROPERTY, columnDefinition = "uuid")
private UUID defaultEdgeRuleChainId;
@Column(name = ModelConstants.EXTERNAL_ID_PROPERTY)
private UUID externalId;
@ -90,6 +93,9 @@ public final class AssetProfileEntity extends BaseSqlEntity<AssetProfile> implem
this.defaultDashboardId = assetProfile.getDefaultDashboardId().getId();
}
this.defaultQueueName = assetProfile.getDefaultQueueName();
if (assetProfile.getDefaultEdgeRuleChainId() != null) {
this.defaultEdgeRuleChainId = assetProfile.getDefaultEdgeRuleChainId().getId();
}
if (assetProfile.getExternalId() != null) {
this.externalId = assetProfile.getExternalId().getId();
}
@ -127,6 +133,9 @@ public final class AssetProfileEntity extends BaseSqlEntity<AssetProfile> implem
if (defaultDashboardId != null) {
assetProfile.setDefaultDashboardId(new DashboardId(defaultDashboardId));
}
if (defaultEdgeRuleChainId != null) {
assetProfile.setDefaultEdgeRuleChainId(new RuleChainId(defaultEdgeRuleChainId));
}
if (externalId != null) {
assetProfile.setExternalId(new AssetProfileId(externalId));
}

9
dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java

@ -103,6 +103,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
@Column(name = ModelConstants.DEVICE_PROFILE_SOFTWARE_ID_PROPERTY)
private UUID softwareId;
@Column(name = ModelConstants.DEVICE_PROFILE_DEFAULT_EDGE_RULE_CHAIN_ID_PROPERTY, columnDefinition = "uuid")
private UUID defaultEdgeRuleChainId;
@Column(name = ModelConstants.EXTERNAL_ID_PROPERTY)
private UUID externalId;
@ -140,6 +143,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
if (deviceProfile.getSoftwareId() != null) {
this.softwareId = deviceProfile.getSoftwareId().getId();
}
if (deviceProfile.getDefaultEdgeRuleChainId() != null) {
this.defaultEdgeRuleChainId = deviceProfile.getDefaultEdgeRuleChainId().getId();
}
if (deviceProfile.getExternalId() != null) {
this.externalId = deviceProfile.getExternalId().getId();
}
@ -189,6 +195,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl
if (softwareId != null) {
deviceProfile.setSoftwareId(new OtaPackageId(softwareId));
}
if (defaultEdgeRuleChainId != null) {
deviceProfile.setDefaultEdgeRuleChainId(new RuleChainId(defaultEdgeRuleChainId));
}
if (externalId != null) {
deviceProfile.setExternalId(new DeviceProfileId(externalId));
}

8
dao/src/main/resources/sql/schema-entities.sql

@ -247,11 +247,13 @@ CREATE TABLE IF NOT EXISTS asset_profile (
default_rule_chain_id uuid,
default_dashboard_id uuid,
default_queue_name varchar(255),
default_edge_rule_chain_id uuid,
external_id uuid,
CONSTRAINT asset_profile_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT asset_profile_external_id_unq_key UNIQUE (tenant_id, external_id),
CONSTRAINT fk_default_rule_chain_asset_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
CONSTRAINT fk_default_dashboard_asset_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id)
CONSTRAINT fk_default_dashboard_asset_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id),
CONSTRAINT fk_default_edge_rule_chain_asset_profile FOREIGN KEY (default_edge_rule_chain_id) REFERENCES rule_chain(id)
);
CREATE TABLE IF NOT EXISTS asset (
@ -290,6 +292,7 @@ CREATE TABLE IF NOT EXISTS device_profile (
default_dashboard_id uuid,
default_queue_name varchar(255),
provision_device_key varchar,
default_edge_rule_chain_id uuid,
external_id uuid,
CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key),
@ -297,7 +300,8 @@ CREATE TABLE IF NOT EXISTS device_profile (
CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id),
CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id),
CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package(id),
CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package(id)
CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package(id),
CONSTRAINT fk_default_edge_rule_chain_device_profile FOREIGN KEY (default_edge_rule_chain_id) REFERENCES rule_chain(id)
);
DO

6
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html

@ -58,6 +58,12 @@
[queueType]="serviceType"
formControlName="defaultQueueName">
</tb-queue-autocomplete>
<tb-rule-chain-autocomplete
labelText="device-profile.default-edge-rule-chain"
formControlName="defaultEdgeRuleChainId"
[ruleChainType]="edgeRuleChainType">
<div tb-hint>{{'device-profile.default-edge-rule-chain-hint' | translate}}</div>
</tb-rule-chain-autocomplete>
<mat-form-field fxHide class="mat-block">
<mat-label translate>device-profile.type</mat-label>
<mat-select formControlName="type" required>

7
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts

@ -50,6 +50,7 @@ import { StepperSelectionEvent } from '@angular/cdk/stepper';
import { deepTrim } from '@core/utils';
import { ServiceType } from '@shared/models/queue.models';
import { DashboardId } from '@shared/models/id/dashboard-id';
import { RuleChainType } from '@shared/models/rule-chain.models';
export interface AddDeviceProfileDialogData {
deviceProfileName: string;
@ -93,6 +94,8 @@ export class AddDeviceProfileDialogComponent extends
serviceType = ServiceType.TB_RULE_ENGINE;
edgeRuleChainType = RuleChainType.EDGE;
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: AddDeviceProfileDialogData,
@ -111,6 +114,7 @@ export class AddDeviceProfileDialogComponent extends
defaultRuleChainId: [null, []],
defaultDashboardId: [null, []],
defaultQueueName: [null, []],
defaultEdgeRuleChainId: [null, []],
description: ['', []]
}
);
@ -205,6 +209,9 @@ export class AddDeviceProfileDialogComponent extends
if (this.deviceProfileDetailsFormGroup.get('defaultDashboardId').value) {
deviceProfile.defaultDashboardId = new DashboardId(this.deviceProfileDetailsFormGroup.get('defaultDashboardId').value);
}
if (this.deviceProfileDetailsFormGroup.get('defaultEdgeRuleChainId').value) {
deviceProfile.defaultEdgeRuleChainId = new RuleChainId(this.deviceProfileDetailsFormGroup.get('defaultEdgeRuleChainId').value);
}
this.deviceProfileService.saveDeviceProfile(deepTrim(deviceProfile)).subscribe(
(savedDeviceProfile) => {
this.dialogRef.close(savedDeviceProfile);

6
ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html

@ -77,6 +77,12 @@
[queueType]="serviceType"
formControlName="defaultQueueName">
</tb-queue-autocomplete>
<tb-rule-chain-autocomplete
labelText="asset-profile.default-edge-rule-chain"
formControlName="defaultEdgeRuleChainId"
[ruleChainType]="edgeRuleChainType">
<div tb-hint>{{'asset-profile.default-edge-rule-chain-hint' | translate}}</div>
</tb-rule-chain-autocomplete>
<tb-image-input fxFlex
label="{{'asset-profile.image' | translate}}"
maxSizeByte="524288"

8
ui-ngx/src/app/modules/home/components/profile/asset-profile.component.ts

@ -28,6 +28,7 @@ import { ServiceType } from '@shared/models/queue.models';
import { EntityId } from '@shared/models/id/entity-id';
import { DashboardId } from '@shared/models/id/dashboard-id';
import { AssetProfile, TB_SERVICE_QUEUE } from '@shared/models/asset.models';
import { RuleChainType } from '@shared/models/rule-chain.models';
@Component({
selector: 'tb-asset-profile',
@ -43,6 +44,8 @@ export class AssetProfileComponent extends EntityComponent<AssetProfile> {
serviceType = ServiceType.TB_RULE_ENGINE;
edgeRuleChainType = RuleChainType.EDGE;
TB_SERVICE_QUEUE = TB_SERVICE_QUEUE;
assetProfileId: EntityId;
@ -73,6 +76,7 @@ export class AssetProfileComponent extends EntityComponent<AssetProfile> {
defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []],
defaultDashboardId: [entity && entity.defaultDashboardId ? entity.defaultDashboardId.id : null, []],
defaultQueueName: [entity ? entity.defaultQueueName : null, []],
defaultEdgeRuleChainId: [entity && entity.defaultEdgeRuleChainId ? entity.defaultEdgeRuleChainId.id : null, []],
description: [entity ? entity.description : '', []],
}
);
@ -86,6 +90,7 @@ export class AssetProfileComponent extends EntityComponent<AssetProfile> {
this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false});
this.entityForm.patchValue({defaultDashboardId: entity.defaultDashboardId ? entity.defaultDashboardId.id : null}, {emitEvent: false});
this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName}, {emitEvent: false});
this.entityForm.patchValue({defaultEdgeRuleChainId: entity.defaultEdgeRuleChainId ? entity.defaultEdgeRuleChainId.id : null}, {emitEvent: false});
this.entityForm.patchValue({description: entity.description}, {emitEvent: false});
}
@ -96,6 +101,9 @@ export class AssetProfileComponent extends EntityComponent<AssetProfile> {
if (formValue.defaultDashboardId) {
formValue.defaultDashboardId = new DashboardId(formValue.defaultDashboardId);
}
if (formValue.defaultEdgeRuleChainId) {
formValue.defaultEdgeRuleChainId = new RuleChainId(formValue.defaultEdgeRuleChainId);
}
return super.prepareFormValue(formValue);
}

6
ui-ngx/src/app/modules/home/components/profile/device-profile.component.html

@ -77,6 +77,12 @@
[queueType]="serviceType"
formControlName="defaultQueueName">
</tb-queue-autocomplete>
<tb-rule-chain-autocomplete
labelText="device-profile.default-edge-rule-chain"
formControlName="defaultEdgeRuleChainId"
[ruleChainType]="edgeRuleChainType">
<div tb-hint>{{'device-profile.default-edge-rule-chain-hint' | translate}}</div>
</tb-rule-chain-autocomplete>
<tb-ota-package-autocomplete
[useFullEntityId]="true"
[showDetailsPageLink]="true"

8
ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts

@ -42,6 +42,7 @@ import { ServiceType } from '@shared/models/queue.models';
import { EntityId } from '@shared/models/id/entity-id';
import { OtaUpdateType } from '@shared/models/ota-package.models';
import { DashboardId } from '@shared/models/id/dashboard-id';
import { RuleChainType } from '@shared/models/rule-chain.models';
@Component({
selector: 'tb-device-profile',
@ -71,6 +72,8 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
serviceType = ServiceType.TB_RULE_ENGINE;
edgeRuleChainType = RuleChainType.EDGE;
deviceProfileId: EntityId;
otaUpdateType = OtaUpdateType;
@ -118,6 +121,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []],
defaultDashboardId: [entity && entity.defaultDashboardId ? entity.defaultDashboardId.id : null, []],
defaultQueueName: [entity ? entity.defaultQueueName : null, []],
defaultEdgeRuleChainId: [entity && entity.defaultEdgeRuleChainId ? entity.defaultEdgeRuleChainId.id : null, []],
firmwareId: [entity ? entity.firmwareId : null],
softwareId: [entity ? entity.softwareId : null],
description: [entity ? entity.description : '', []],
@ -198,6 +202,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false});
this.entityForm.patchValue({defaultDashboardId: entity.defaultDashboardId ? entity.defaultDashboardId.id : null}, {emitEvent: false});
this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName}, {emitEvent: false});
this.entityForm.patchValue({defaultEdgeRuleChainId: entity.defaultEdgeRuleChainId ? entity.defaultEdgeRuleChainId.id : null}, {emitEvent: false});
this.entityForm.patchValue({firmwareId: entity.firmwareId}, {emitEvent: false});
this.entityForm.patchValue({softwareId: entity.softwareId}, {emitEvent: false});
this.entityForm.patchValue({description: entity.description}, {emitEvent: false});
@ -210,6 +215,9 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
if (formValue.defaultDashboardId) {
formValue.defaultDashboardId = new DashboardId(formValue.defaultDashboardId);
}
if (formValue.defaultEdgeRuleChainId) {
formValue.defaultEdgeRuleChainId = new RuleChainId(formValue.defaultEdgeRuleChainId);
}
const deviceProvisionConfiguration: DeviceProvisionConfiguration = formValue.profileData.provisionConfiguration;
formValue.provisionType = deviceProvisionConfiguration.type;
formValue.provisionDeviceKey = deviceProvisionConfiguration.provisionDeviceKey;

5
ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.html

@ -16,7 +16,7 @@
-->
<mat-form-field [formGroup]="selectRuleChainFormGroup" class="mat-block">
<input matInput type="text" placeholder="{{ ruleChainLabel | translate }}"
<input matInput type="text" placeholder="{{ labelText | translate }}"
#ruleChainInput
formControlName="ruleChainId"
(focusin)="onFocus()"
@ -54,4 +54,7 @@
<mat-error *ngIf="selectRuleChainFormGroup.get('ruleChainId').hasError('required')">
{{ 'rulechain.rulechain-required' | translate }}
</mat-error>
<mat-hint>
<ng-content select="[tb-hint]"></ng-content>
</mat-hint>
</mat-form-field>

10
ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.ts

@ -45,16 +45,17 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI
selectRuleChainFormGroup: FormGroup;
ruleChainLabel = 'rulechain.rulechain';
modelValue: string | null;
@Input()
labelText: string;
labelText: string = 'rulechain.rulechain';
@Input()
requiredText: string;
@Input()
ruleChainType: RuleChainType = RuleChainType.CORE;
private requiredValue: boolean;
get required(): boolean {
return this.requiredValue;
@ -191,9 +192,8 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI
fetchRuleChain(searchText?: string): Observable<Array<BaseData<EntityId>>> {
this.searchText = searchText;
// @voba: at the moment device profiles are not supported by edge, so 'core' hardcoded
return this.entityService.getEntitiesByNameFilter(EntityType.RULE_CHAIN, searchText,
50, RuleChainType.CORE, {ignoreLoading: true}).pipe(
50, this.ruleChainType, {ignoreLoading: true}).pipe(
catchError(() => of([]))
);
}

1
ui-ngx/src/app/shared/models/asset.models.ts

@ -35,6 +35,7 @@ export interface AssetProfile extends BaseData<AssetProfileId>, ExportableEntity
defaultRuleChainId?: RuleChainId;
defaultDashboardId?: DashboardId;
defaultQueueName?: string;
defaultEdgeRuleChainId?: RuleChainId;
}
export interface AssetProfileInfo extends EntityInfoData {

1
ui-ngx/src/app/shared/models/device.models.ts

@ -579,6 +579,7 @@ export interface DeviceProfile extends BaseData<DeviceProfileId>, ExportableEnti
firmwareId?: OtaPackageId;
softwareId?: OtaPackageId;
profileData: DeviceProfileData;
defaultEdgeRuleChainId?: RuleChainId;
}
export interface DeviceProfileInfo extends EntityInfoData {

4
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1283,6 +1283,8 @@
"description": "Description",
"default": "Default",
"default-rule-chain": "Default rule chain",
"default-edge-rule-chain": "Default edge rule chain",
"default-edge-rule-chain-hint": "Used on edge as rule chain to process incoming data for assets of this asset profile",
"mobile-dashboard": "Mobile dashboard",
"mobile-dashboard-hint": "Used by mobile application as a asset details dashboard",
"select-queue-hint": "Select from a drop-down list.",
@ -1343,6 +1345,8 @@
"profile-configuration": "Profile configuration",
"transport-configuration": "Transport configuration",
"default-rule-chain": "Default rule chain",
"default-edge-rule-chain": "Default edge rule chain",
"default-edge-rule-chain-hint": "Used on edge as rule chain to process incoming data for devices of this device profile",
"mobile-dashboard": "Mobile dashboard",
"mobile-dashboard-hint": "Used by mobile application as a device details dashboard",
"select-queue-hint": "Select from a drop-down list.",

Loading…
Cancel
Save