Browse Source

Merge branch 'develop/3.5' of github.com:thingsboard/thingsboard into feature/home-page

pull/8359/head
Igor Kulikov 4 years ago
parent
commit
f10113f20c
  1. 77
      application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json
  2. 23
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
  3. 70
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java
  4. 28
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java
  5. 66
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java
  6. 23
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  7. 23
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  8. 2
      application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java
  9. 16
      application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java
  10. 23
      application/src/test/java/org/thingsboard/server/edge/BaseAlarmEdgeTest.java
  11. 12
      common/data/src/main/java/org/thingsboard/server/common/data/UpdateMessage.java
  12. 8
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCreateOrUpdateActiveRequest.java
  13. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java
  14. 2
      pom.xml
  15. 17
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/AbstractTbMsgPushNode.java
  16. 4
      ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts
  17. 8
      ui-ngx/src/assets/locale/locale.constant-en_US.json

77
application/src/main/data/json/tenant/edge_management/rule_chains/edge_root_rule_chain.json → application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json

@ -6,7 +6,8 @@
"firstRuleNodeId": null,
"root": true,
"debugMode": false,
"configuration": null
"configuration": null,
"externalId": null
},
"metadata": {
"firstNodeIndex": 0,
@ -23,7 +24,8 @@
"configuration": {
"persistAlarmRulesState": false,
"fetchAlarmRulesStateOnStart": false
}
},
"externalId": null
},
{
"additionalInfo": {
@ -35,7 +37,8 @@
"debugMode": false,
"configuration": {
"defaultTTL": 0
}
},
"externalId": null
},
{
"additionalInfo": {
@ -46,8 +49,10 @@
"name": "Save Client Attributes",
"debugMode": false,
"configuration": {
"scope": "CLIENT_SCOPE"
}
"scope": "CLIENT_SCOPE",
"notifyDevice": "false"
},
"externalId": null
},
{
"additionalInfo": {
@ -59,7 +64,8 @@
"debugMode": false,
"configuration": {
"version": 0
}
},
"externalId": null
},
{
"additionalInfo": {
@ -73,7 +79,8 @@
"scriptLang": "TBEL",
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
"tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
}
},
"externalId": null
},
{
"additionalInfo": {
@ -87,7 +94,8 @@
"scriptLang": "TBEL",
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
"tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
}
},
"externalId": null
},
{
"additionalInfo": {
@ -99,19 +107,34 @@
"debugMode": false,
"configuration": {
"timeoutInSeconds": 60
}
},
"externalId": null
},
{
"additionalInfo": {
"layoutX": 1129,
"layoutY": 52
"layoutX": 1126,
"layoutY": 104
},
"type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
"name": "Push to cloud",
"debugMode": false,
"configuration": {
"scope": "SERVER_SCOPE"
}
},
"externalId": null
},
{
"additionalInfo": {
"layoutX": 826,
"layoutY": 601
},
"type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
"name": "Push to cloud",
"debugMode": false,
"configuration": {
"scope": "SERVER_SCOPE"
},
"externalId": null
}
],
"connections": [
@ -132,24 +155,14 @@
},
{
"fromIndex": 3,
"toIndex": 6,
"type": "RPC Request to Device"
},
{
"fromIndex": 3,
"toIndex": 5,
"type": "Other"
"toIndex": 1,
"type": "Post telemetry"
},
{
"fromIndex": 3,
"toIndex": 2,
"type": "Post attributes"
},
{
"fromIndex": 3,
"toIndex": 1,
"type": "Post telemetry"
},
{
"fromIndex": 3,
"toIndex": 4,
@ -157,23 +170,23 @@
},
{
"fromIndex": 3,
"toIndex": 7,
"type": "Attributes Updated"
"toIndex": 5,
"type": "Other"
},
{
"fromIndex": 3,
"toIndex": 7,
"type": "Attributes Deleted"
"toIndex": 6,
"type": "RPC Request to Device"
},
{
"fromIndex": 3,
"toIndex": 7,
"type": "Timeseries Deleted"
"toIndex": 8,
"type": "Attributes Deleted"
},
{
"fromIndex": 3,
"toIndex": 7,
"type": "Timeseries Updated"
"toIndex": 8,
"type": "Attributes Updated"
}
],
"ruleChainConnections": null

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

@ -485,4 +485,27 @@ public abstract class BaseEdgeProcessor {
}
return customerId;
}
protected boolean isEntityExists(TenantId tenantId, EntityId entityId) {
switch (entityId.getEntityType()) {
case TENANT:
return tenantService.findTenantById(tenantId) != null;
case DEVICE:
return deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())) != null;
case ASSET:
return assetService.findAssetById(tenantId, new AssetId(entityId.getId())) != null;
case ENTITY_VIEW:
return entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())) != null;
case CUSTOMER:
return customerService.findCustomerById(tenantId, new CustomerId(entityId.getId())) != null;
case USER:
return userService.findUserById(tenantId, new UserId(entityId.getId())) != null;
case DASHBOARD:
return dashboardService.findDashboardById(tenantId, new DashboardId(entityId.getId())) != null;
case EDGE:
return edgeService.findEdgeById(tenantId, new EdgeId(entityId.getId())) != null;
default:
return false;
}
}
}

70
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java

@ -19,19 +19,19 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.util.UUID;
@ -43,51 +43,59 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor {
log.trace("[{}] processAlarmMsg [{}]", tenantId, alarmUpdateMsg);
EntityId originatorId = getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(),
EntityType.valueOf(alarmUpdateMsg.getOriginatorType()));
AlarmId alarmId = new AlarmId(new UUID(alarmUpdateMsg.getIdMSB(), alarmUpdateMsg.getIdLSB()));
if (originatorId == null) {
log.warn("Originator not found for the alarm msg {}", alarmUpdateMsg);
return Futures.immediateFuture(null);
}
try {
Alarm existentAlarm = alarmService.findLatestActiveByOriginatorAndType(tenantId, originatorId, alarmUpdateMsg.getType());
switch (alarmUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE:
case ENTITY_UPDATED_RPC_MESSAGE:
if (existentAlarm == null || existentAlarm.getStatus().isCleared()) {
existentAlarm = new Alarm();
existentAlarm.setTenantId(tenantId);
existentAlarm.setType(alarmUpdateMsg.getName());
existentAlarm.setOriginator(originatorId);
existentAlarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity()));
existentAlarm.setStartTs(alarmUpdateMsg.getStartTs());
existentAlarm.setClearTs(alarmUpdateMsg.getClearTs());
existentAlarm.setPropagate(alarmUpdateMsg.getPropagate());
}
Alarm alarm = new Alarm();
alarm.setId(alarmId);
alarm.setTenantId(tenantId);
alarm.setType(alarmUpdateMsg.getName());
alarm.setOriginator(originatorId);
alarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity()));
alarm.setStartTs(alarmUpdateMsg.getStartTs());
var alarmStatus = AlarmStatus.valueOf(alarmUpdateMsg.getStatus());
existentAlarm.setCleared(alarmStatus.isCleared());
existentAlarm.setAcknowledged(alarmStatus.isAck());
existentAlarm.setAckTs(alarmUpdateMsg.getAckTs());
existentAlarm.setEndTs(alarmUpdateMsg.getEndTs());
existentAlarm.setDetails(JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails()));
alarmService.createOrUpdateAlarm(existentAlarm);
break;
alarm.setClearTs(alarmUpdateMsg.getClearTs());
alarm.setPropagate(alarmUpdateMsg.getPropagate());
alarm.setCleared(alarmStatus.isCleared());
alarm.setAcknowledged(alarmStatus.isAck());
alarm.setAckTs(alarmUpdateMsg.getAckTs());
alarm.setEndTs(alarmUpdateMsg.getEndTs());
alarm.setDetails(JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails()));
if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(alarmUpdateMsg.getMsgType())) {
alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.fromAlarm(alarm, null, alarmId));
} else {
alarmService.updateAlarm(AlarmUpdateRequest.fromAlarm(alarm));
}
return Futures.immediateFuture(null);
case ALARM_ACK_RPC_MESSAGE:
if (existentAlarm != null) {
alarmService.acknowledgeAlarm(tenantId, existentAlarm.getId(), alarmUpdateMsg.getAckTs());
Alarm alarmToAck = alarmService.findAlarmById(tenantId, alarmId);
if (alarmToAck != null) {
alarmService.acknowledgeAlarm(tenantId, alarmId, alarmUpdateMsg.getAckTs());
}
break;
return Futures.immediateFuture(null);
case ALARM_CLEAR_RPC_MESSAGE:
if (existentAlarm != null) {
alarmService.clearAlarm(tenantId, existentAlarm.getId(),
alarmUpdateMsg.getAckTs(), JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails()));
Alarm alarmToClear = alarmService.findAlarmById(tenantId, alarmId);
if (alarmToClear != null) {
alarmService.clearAlarm(tenantId, alarmId, alarmUpdateMsg.getClearTs(),
JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails()));
}
break;
return Futures.immediateFuture(null);
case ENTITY_DELETED_RPC_MESSAGE:
if (existentAlarm != null) {
alarmService.delAlarm(tenantId, existentAlarm.getId());
Alarm alarmToDelete = alarmService.findAlarmById(tenantId, alarmId);
if (alarmToDelete != null) {
alarmService.delAlarm(tenantId, alarmId);
}
break;
return Futures.immediateFuture(null);
case UNRECOGNIZED:
default:
return handleUnsupportedMsgType(alarmUpdateMsg.getMsgType());
}
return Futures.immediateFuture(null);
} catch (Exception e) {
log.error("[{}] Failed to process alarm update msg [{}]", tenantId, alarmUpdateMsg, e);
return Futures.immediateFailedFuture(e);

28
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java

@ -20,16 +20,9 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
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.EntityIdFactory;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
@ -80,25 +73,4 @@ public abstract class BaseRelationProcessor extends BaseEdgeProcessor {
return Futures.immediateFailedFuture(e);
}
}
private boolean isEntityExists(TenantId tenantId, EntityId entityId) {
switch (entityId.getEntityType()) {
case DEVICE:
return deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())) != null;
case ASSET:
return assetService.findAssetById(tenantId, new AssetId(entityId.getId())) != null;
case ENTITY_VIEW:
return entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())) != null;
case CUSTOMER:
return customerService.findCustomerById(tenantId, new CustomerId(entityId.getId())) != null;
case USER:
return userService.findUserById(tenantId, new UserId(entityId.getId())) != null;
case DASHBOARD:
return dashboardService.findDashboardById(tenantId, new DashboardId(entityId.getId())) != null;
case EDGE:
return edgeService.findEdgeById(tenantId, new EdgeId(entityId.getId())) != null;
default:
return false;
}
}
}

66
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java

@ -90,42 +90,46 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
log.trace("[{}] processTelemetryMsg [{}]", tenantId, entityData);
List<ListenableFuture<Void>> result = new ArrayList<>();
EntityId entityId = constructEntityId(entityData.getEntityType(), entityData.getEntityIdMSB(), entityData.getEntityIdLSB());
if ((entityData.hasPostAttributesMsg() || entityData.hasPostTelemetryMsg() || entityData.hasAttributesUpdatedMsg()) && entityId != null) {
Pair<TbMsgMetaData, CustomerId> pair = getBaseMsgMetadataAndCustomerId(tenantId, entityId);
TbMsgMetaData metaData = pair.getKey();
CustomerId customerId = pair.getValue();
metaData.putValue(DataConstants.MSG_SOURCE_KEY, getMsgSourceKey());
if (entityData.hasPostAttributesMsg()) {
result.add(processPostAttributes(tenantId, customerId, entityId, entityData.getPostAttributesMsg(), metaData));
}
if (entityData.hasAttributesUpdatedMsg()) {
metaData.putValue("scope", entityData.getPostAttributeScope());
result.add(processAttributesUpdate(tenantId, customerId, entityId, entityData.getAttributesUpdatedMsg(), metaData));
}
if (entityData.hasPostTelemetryMsg()) {
result.add(processPostTelemetry(tenantId, customerId, entityId, entityData.getPostTelemetryMsg(), metaData));
}
if (EntityType.DEVICE.equals(entityId.getEntityType())) {
DeviceId deviceId = new DeviceId(entityId.getId());
if (entityId != null && isEntityExists(tenantId, entityId)) {
if ((entityData.hasPostAttributesMsg() || entityData.hasPostTelemetryMsg() || entityData.hasAttributesUpdatedMsg())) {
Pair<TbMsgMetaData, CustomerId> pair = getBaseMsgMetadataAndCustomerId(tenantId, entityId);
TbMsgMetaData metaData = pair.getKey();
CustomerId customerId = pair.getValue();
metaData.putValue(DataConstants.MSG_SOURCE_KEY, getMsgSourceKey());
if (entityData.hasPostAttributesMsg()) {
result.add(processPostAttributes(tenantId, customerId, entityId, entityData.getPostAttributesMsg(), metaData));
}
if (entityData.hasAttributesUpdatedMsg()) {
metaData.putValue("scope", entityData.getPostAttributeScope());
result.add(processAttributesUpdate(tenantId, customerId, entityId, entityData.getAttributesUpdatedMsg(), metaData));
}
if (entityData.hasPostTelemetryMsg()) {
result.add(processPostTelemetry(tenantId, customerId, entityId, entityData.getPostTelemetryMsg(), metaData));
}
if (EntityType.DEVICE.equals(entityId.getEntityType())) {
DeviceId deviceId = new DeviceId(entityId.getId());
long currentTs = System.currentTimeMillis();
long currentTs = System.currentTimeMillis();
TransportProtos.DeviceActivityProto deviceActivityMsg = TransportProtos.DeviceActivityProto.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
.setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
.setLastActivityTime(currentTs).build();
TransportProtos.DeviceActivityProto deviceActivityMsg = TransportProtos.DeviceActivityProto.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
.setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
.setLastActivityTime(currentTs).build();
log.trace("[{}][{}] device activity time is going to be updated, ts {}", tenantId, deviceId, currentTs);
log.trace("[{}][{}] device activity time is going to be updated, ts {}", tenantId, deviceId, currentTs);
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId);
tbCoreMsgProducer.send(tpi, new TbProtoQueueMsg<>(deviceId.getId(),
TransportProtos.ToCoreMsg.newBuilder().setDeviceActivityMsg(deviceActivityMsg).build()), null);
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId);
tbCoreMsgProducer.send(tpi, new TbProtoQueueMsg<>(deviceId.getId(),
TransportProtos.ToCoreMsg.newBuilder().setDeviceActivityMsg(deviceActivityMsg).build()), null);
}
}
}
if (entityData.hasAttributeDeleteMsg()) {
result.add(processAttributeDeleteMsg(tenantId, entityId, entityData.getAttributeDeleteMsg(), entityData.getEntityType()));
if (entityData.hasAttributeDeleteMsg()) {
result.add(processAttributeDeleteMsg(tenantId, entityId, entityData.getAttributeDeleteMsg(), entityData.getEntityType()));
}
} else {
log.warn("Skipping telemetry update msg because entity doesn't exists on edge, {}", entityData);
}
return result;
}

23
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java

@ -22,7 +22,6 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@ -91,6 +90,7 @@ import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.notification.NotificationSettingsService;
import org.thingsboard.server.dao.notification.NotificationTargetService;
import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.settings.AdminSettingsService;
@ -177,6 +177,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
@Autowired
private NotificationSettingsService notificationSettingsService;
@Autowired
private NotificationTargetService notificationTargetService;
@Bean
protected BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
@ -679,27 +682,15 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
@Override
public void createDefaultNotificationConfigs() {
try {
log.info("Creating default notification configs for system admin");
log.info("Creating default notification configs for system admin");
if (notificationTargetService.findNotificationTargetsByTenantId(TenantId.SYS_TENANT_ID, new PageLink(1)).getTotalElements() == 0) {
notificationSettingsService.createDefaultNotificationConfigs(TenantId.SYS_TENANT_ID);
} catch (Exception e) {
if (StringUtils.contains(e.getMessage(), "already exists")) {
log.info("Default notification configs are already present for system admin, skipping");
} else {
throw e;
}
}
PageDataIterable<TenantId> tenants = new PageDataIterable<>(tenantService::findTenantsIds, 500);
log.info("Creating default notification configs for all tenants");
for (TenantId tenantId : tenants) {
try {
if (notificationTargetService.findNotificationTargetsByTenantId(tenantId, new PageLink(1)).getTotalElements() == 0) {
notificationSettingsService.createDefaultNotificationConfigs(tenantId);
} catch (Exception e) {
if (StringUtils.contains(e.getMessage(), "already exists")) {
log.info("Default notification configs are already present for tenant {}, skipping", tenantId);
} else {
throw e;
}
}
}
}

23
application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java

@ -65,6 +65,7 @@ public class InstallScripts {
public static final String JSON_DIR = "json";
public static final String SYSTEM_DIR = "system";
public static final String TENANT_DIR = "tenant";
public static final String EDGE_DIR = "edge";
public static final String DEVICE_PROFILE_DIR = "device_profile";
public static final String DEMO_DIR = "demo";
public static final String RULE_CHAINS_DIR = "rule_chains";
@ -74,8 +75,6 @@ public class InstallScripts {
public static final String MODELS_LWM2M_DIR = "lwm2m-registry";
public static final String CREDENTIALS_DIR = "credentials";
public static final String EDGE_MANAGEMENT = "edge_management";
public static final String JSON_EXT = ".json";
public static final String XML_EXT = ".xml";
@ -109,7 +108,7 @@ public class InstallScripts {
}
private Path getEdgeRuleChainsDir() {
return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, EDGE_MANAGEMENT, RULE_CHAINS_DIR);
return Paths.get(getDataDir(), JSON_DIR, EDGE_DIR, RULE_CHAINS_DIR);
}
public String getDataDir() {
@ -293,17 +292,15 @@ public class InstallScripts {
}
private void doSaveLwm2mResource(TbResource resource) throws ThingsboardException {
try {
log.trace("Executing saveResource [{}]", resource);
if (StringUtils.isEmpty(resource.getData())) {
throw new DataValidationException("Resource data should be specified!");
}
toLwm2mResource(resource);
log.trace("Executing saveResource [{}]", resource);
if (StringUtils.isEmpty(resource.getData())) {
throw new DataValidationException("Resource data should be specified!");
}
toLwm2mResource(resource);
TbResource foundResource =
resourceService.getResource(TenantId.SYS_TENANT_ID, ResourceType.LWM2M_MODEL, resource.getResourceKey());
if (foundResource == null) {
resourceService.saveResource(resource);
} catch (DataValidationException e) {
log.debug("[{}] {}", resource.getFileName(), e.getMessage());
} catch (Exception ex) {
throw ex;
}
}
}

2
application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java

@ -44,7 +44,7 @@ public class NewPlatformVersionTriggerProcessor implements NotificationRuleTrigg
@Override
public NotificationInfo constructNotificationInfo(NewPlatformVersionTrigger trigger, NewPlatformVersionNotificationRuleTriggerConfig triggerConfig) {
return NewPlatformVersionNotificationInfo.builder()
.message(trigger.getMessage().getMessage())
.message("New version available - " + trigger.getMessage().getLatestVersion())
.build();
}

16
application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java

@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.info.BuildProperties;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
@ -57,6 +58,9 @@ public class DefaultUpdateService implements UpdateService {
@Value("${updates.enabled}")
private boolean updatesEnabled;
@Autowired(required = false)
private BuildProperties buildProperties;
@Autowired
private NotificationRuleProcessingService notificationRuleProcessingService;
@ -73,14 +77,11 @@ public class DefaultUpdateService implements UpdateService {
@PostConstruct
private void init() {
updateMessage = new UpdateMessage("", false, "");
version = buildProperties != null ? buildProperties.getVersion() : "unknown";
updateMessage = new UpdateMessage(false, version, "", "");
if (updatesEnabled) {
try {
platform = System.getProperty("platform", "unknown");
version = getClass().getPackage().getImplementationVersion();
if (version == null) {
version = "unknown";
}
instanceId = parseInstanceId();
checkUpdatesFuture = scheduler.scheduleAtFixedRate(checkUpdatesRunnable, 0, 1, TimeUnit.HOURS);
} catch (Exception e) {
@ -131,9 +132,10 @@ public class DefaultUpdateService implements UpdateService {
JsonNode response = restClient.postForObject(UPDATE_SERVER_BASE_URL + "/api/thingsboard/updates", request, JsonNode.class);
UpdateMessage prevUpdateMessage = updateMessage;
updateMessage = new UpdateMessage(
response.get("message").asText(),
response.get("updateAvailable").asBoolean(),
version
version,
"3.5.0",
"https://thingsboard.io/docs/user-guide/install/pe/upgrade-instructions"
);
if (updateMessage.isUpdateAvailable() && !updateMessage.equals(prevUpdateMessage)) {
notificationRuleProcessingService.process(TenantId.SYS_TENANT_ID, NewPlatformVersionTrigger.builder()

23
application/src/test/java/org/thingsboard/server/edge/BaseAlarmEdgeTest.java

@ -19,12 +19,14 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.google.protobuf.AbstractMessage;
import org.junit.Assert;
import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType;
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.id.AlarmId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
@ -33,6 +35,7 @@ import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -42,8 +45,11 @@ abstract public class BaseAlarmEdgeTest extends AbstractEdgeTest {
public void testSendAlarmToCloud() throws Exception {
Device device = saveDeviceOnCloudAndVerifyDeliveryToEdge();
UUID alarmUUID = UUID.randomUUID();
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
AlarmUpdateMsg.Builder alarmUpdateMgBuilder = AlarmUpdateMsg.newBuilder();
alarmUpdateMgBuilder.setIdMSB(alarmUUID.getMostSignificantBits());
alarmUpdateMgBuilder.setIdLSB(alarmUUID.getLeastSignificantBits());
alarmUpdateMgBuilder.setName("alarm from edge");
alarmUpdateMgBuilder.setStatus(AlarmStatus.ACTIVE_UNACK.name());
alarmUpdateMgBuilder.setSeverity(AlarmSeverity.CRITICAL.name());
@ -65,6 +71,7 @@ abstract public class BaseAlarmEdgeTest extends AbstractEdgeTest {
Optional<AlarmInfo> foundAlarm = alarms.stream().filter(alarm -> alarm.getType().equals("alarm from edge")).findAny();
Assert.assertTrue(foundAlarm.isPresent());
AlarmInfo alarmInfo = foundAlarm.get();
Assert.assertEquals(new AlarmId(alarmUUID), alarmInfo.getId());
Assert.assertEquals(device.getId(), alarmInfo.getOriginator());
Assert.assertEquals(AlarmStatus.ACTIVE_UNACK, alarmInfo.getStatus());
Assert.assertEquals(AlarmSeverity.CRITICAL, alarmInfo.getSeverity());
@ -85,12 +92,28 @@ abstract public class BaseAlarmEdgeTest extends AbstractEdgeTest {
Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg);
AlarmUpdateMsg alarmUpdateMsg = (AlarmUpdateMsg) latestMessage;
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, alarmUpdateMsg.getMsgType());
Assert.assertEquals(savedAlarm.getUuidId().getMostSignificantBits(), alarmUpdateMsg.getIdMSB());
Assert.assertEquals(savedAlarm.getUuidId().getLeastSignificantBits(), alarmUpdateMsg.getIdLSB());
Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType());
Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName());
Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName());
Assert.assertEquals(savedAlarm.getStatus().name(), alarmUpdateMsg.getStatus());
Assert.assertEquals(savedAlarm.getSeverity().name(), alarmUpdateMsg.getSeverity());
// update alarm
String updatedDetails = "{\"testKey\":\"testValue\"}";
savedAlarm.setDetails(JacksonUtil.OBJECT_MAPPER.readTree(updatedDetails));
edgeImitator.expectMessageAmount(1);
savedAlarm = doPost("/api/alarm", savedAlarm, Alarm.class);
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg);
alarmUpdateMsg = (AlarmUpdateMsg) latestMessage;
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, alarmUpdateMsg.getMsgType());
Assert.assertEquals(savedAlarm.getUuidId().getMostSignificantBits(), alarmUpdateMsg.getIdMSB());
Assert.assertEquals(savedAlarm.getUuidId().getLeastSignificantBits(), alarmUpdateMsg.getIdLSB());
Assert.assertEquals(updatedDetails, alarmUpdateMsg.getDetails());
// ack alarm
edgeImitator.expectMessageAmount(1);
doPost("/api/alarm/" + savedAlarm.getUuidId() + "/ack");

12
common/data/src/main/java/org/thingsboard/server/common/data/UpdateMessage.java

@ -23,10 +23,14 @@ import lombok.Data;
@Data
public class UpdateMessage {
@ApiModelProperty(position = 1, value = "The message about new platform update available.")
private final String message;
@ApiModelProperty(position = 2, value = "'True' if new platform update is available.")
@ApiModelProperty(position = 1, value = "'True' if new platform update is available.")
private final boolean isUpdateAvailable;
@ApiModelProperty(position = 3, value = "Current ThingsBoard version.")
@ApiModelProperty(position = 2, value = "Current ThingsBoard version.")
private final String currentVersion;
@ApiModelProperty(position = 3, value = "Latest ThingsBoard version.")
private final String latestVersion;
@ApiModelProperty(position = 4, value = "Upgrade instructions URL.")
private final String upgradeInstructionsUrl;
}

8
common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCreateOrUpdateActiveRequest.java

@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
@ -61,11 +62,17 @@ public class AlarmCreateOrUpdateActiveRequest implements AlarmModificationReques
private UserId userId;
private AlarmId edgeAlarmId;
public static AlarmCreateOrUpdateActiveRequest fromAlarm(Alarm a) {
return fromAlarm(a, null);
}
public static AlarmCreateOrUpdateActiveRequest fromAlarm(Alarm a, UserId userId) {
return fromAlarm(a, userId, null);
}
public static AlarmCreateOrUpdateActiveRequest fromAlarm(Alarm a, UserId userId, AlarmId edgeAlarmId) {
return AlarmCreateOrUpdateActiveRequest.builder()
.tenantId(a.getTenantId())
.customerId(a.getCustomerId())
@ -81,6 +88,7 @@ public class AlarmCreateOrUpdateActiveRequest implements AlarmModificationReques
.propagateToTenant(a.isPropagateToTenant())
.propagateRelationTypes(a.getPropagateRelationTypes()).build())
.userId(userId)
.edgeAlarmId(edgeAlarmId)
.build();
}

2
dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java

@ -245,7 +245,7 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
return toAlarmApiResult(alarmRepository.createOrUpdateActiveAlarm(
request.getTenantId().getId(),
request.getCustomerId() != null ? request.getCustomerId().getId() : CustomerId.NULL_UUID,
UUID.randomUUID(),
request.getEdgeAlarmId() != null ? request.getEdgeAlarmId().getId() : UUID.randomUUID(),
System.currentTimeMillis(),
request.getOriginator().getId(),
request.getOriginator().getEntityType().ordinal(),

2
pom.xml

@ -78,7 +78,7 @@
<zookeeper.version>3.5.5</zookeeper.version>
<protobuf.version>3.21.9</protobuf.version>
<grpc.version>1.42.1</grpc.version>
<tbel.version>1.0.5</tbel.version>
<tbel.version>1.0.6</tbel.version>
<lombok.version>1.18.18</lombok.version>
<paho.client.version>1.2.4</paho.client.version>
<paho.mqttv5.client.version>1.2.5</paho.mqttv5.client.version>

17
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/AbstractTbMsgPushNode.java

@ -74,7 +74,8 @@ public abstract class AbstractTbMsgPushNode<T extends BaseTbMsgPushNodeConfigura
protected S buildEvent(TbMsg msg, TbContext ctx) {
String msgType = msg.getType();
if (DataConstants.ALARM.equals(msgType)) {
return buildEvent(ctx.getTenantId(), EdgeEventActionType.ADDED, getUUIDFromMsgData(msg), getAlarmEventType(), null);
EdgeEventActionType actionType = getAlarmActionType(msg);
return buildEvent(ctx.getTenantId(), actionType, getUUIDFromMsgData(msg), getAlarmEventType(), null);
} else {
EdgeEventActionType actionType = getEdgeEventActionTypeByMsgType(msgType);
Map<String, Object> entityBody = new HashMap<>();
@ -107,6 +108,20 @@ public abstract class AbstractTbMsgPushNode<T extends BaseTbMsgPushNodeConfigura
}
}
private static EdgeEventActionType getAlarmActionType(TbMsg msg) {
boolean isNewAlarm = Boolean.parseBoolean(msg.getMetaData().getValue(DataConstants.IS_NEW_ALARM));
boolean isClearedAlarm = Boolean.parseBoolean(msg.getMetaData().getValue(DataConstants.IS_CLEARED_ALARM));
EdgeEventActionType eventAction;
if (isNewAlarm) {
eventAction = EdgeEventActionType.ADDED;
} else if (isClearedAlarm) {
eventAction = EdgeEventActionType.ALARM_CLEAR;
} else {
eventAction = EdgeEventActionType.UPDATED;
}
return eventAction;
}
abstract S buildEvent(TenantId tenantId, EdgeEventActionType eventAction, UUID entityId, U eventType, JsonNode entityBody);
abstract U getEventTypeByEntityType(EntityType entityType);

4
ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts

@ -142,11 +142,11 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
);
} else if (ruleChainScope === 'edges') {
columns.push(
new EntityTableColumn<RuleChain>('root', 'rulechain.edge-template-root', '60px',
new EntityTableColumn<RuleChain>('root', 'rulechain.edge-template-root', '70px',
entity => {
return checkBoxCell(entity.root);
}),
new EntityTableColumn<RuleChain>('assignToEdge', 'rulechain.assign-to-edge', '60px',
new EntityTableColumn<RuleChain>('assignToEdge', 'rulechain.assign-to-edge', '70px',
entity => {
return checkBoxCell(this.isAutoAssignToEdgeRuleChain(entity));
})

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

@ -1396,10 +1396,10 @@
"create-new-device-profile": "Create a new one!",
"mqtt-device-topic-filters": "MQTT device topic filters",
"mqtt-device-topic-filters-unique": "MQTT device topic filters need to be unique.",
"mqtt-device-topic-filters-spark-plug": "MQTT device topic filters SparkPlug.",
"mqtt-device-topic-filters-spark-plug-hint": "Default - telemetry. Example: namespace/group_id/message_type/edge_node_id/[device_id].",
"mqtt-device-topic-filters-spark-plug-attribute-metric-names": "SparkPlug attributes metric names",
"mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "Names of SparkPlug metrics that will be stored as device attributes. All other metrics will be stored as device telemetry",
"mqtt-device-topic-filters-spark-plug": "MQTT Sparkplug B Edge of Network (EoN) node.",
"mqtt-device-topic-filters-spark-plug-hint": "Allow connections from EoN nodes with Sparkplug B payload and topic format.",
"mqtt-device-topic-filters-spark-plug-attribute-metric-names": "SparkPlug metrics to store as attributes.",
"mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "Names of SparkPlug metrics that will be stored as device attributes. All other metrics will be stored as device telemetry.",
"mqtt-device-payload-type": "MQTT device payload",
"mqtt-device-payload-type-json": "JSON",
"mqtt-device-payload-type-proto": "Protobuf",

Loading…
Cancel
Save