Commit e91fd302d6c6a9b598f17b18f1d470f172099758

Authored by Igor Kulikov
1 parent 52ab3d6d

Implement alarms table

Showing 31 changed files with 1141 additions and 151 deletions
@@ -150,9 +150,11 @@ public class AlarmController extends BaseController { @@ -150,9 +150,11 @@ public class AlarmController extends BaseController {
150 @RequestParam(required = false) String status, 150 @RequestParam(required = false) String status,
151 @RequestParam int pageSize, 151 @RequestParam int pageSize,
152 @RequestParam int page, 152 @RequestParam int page,
  153 + @RequestParam(required = false) String textSearch,
  154 + @RequestParam(required = false) String sortProperty,
  155 + @RequestParam(required = false) String sortOrder,
153 @RequestParam(required = false) Long startTime, 156 @RequestParam(required = false) Long startTime,
154 @RequestParam(required = false) Long endTime, 157 @RequestParam(required = false) Long endTime,
155 - @RequestParam(required = false, defaultValue = "false") boolean ascOrder,  
156 @RequestParam(required = false) Boolean fetchOriginator 158 @RequestParam(required = false) Boolean fetchOriginator
157 ) throws ThingsboardException { 159 ) throws ThingsboardException {
158 checkParameter("EntityId", strEntityId); 160 checkParameter("EntityId", strEntityId);
@@ -165,9 +167,8 @@ public class AlarmController extends BaseController { @@ -165,9 +167,8 @@ public class AlarmController extends BaseController {
165 "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); 167 "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
166 } 168 }
167 checkEntityId(entityId, Operation.READ); 169 checkEntityId(entityId, Operation.READ);
  170 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
168 try { 171 try {
169 - TimePageLink pageLink = createTimePageLink(pageSize, page, "",  
170 - "id", ascOrder ? "asc" : "desc", startTime, endTime);  
171 return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); 172 return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get());
172 } catch (Exception e) { 173 } catch (Exception e) {
173 throw handleException(e); 174 throw handleException(e);
@@ -29,6 +29,11 @@ public class AlarmInfo extends Alarm { @@ -29,6 +29,11 @@ public class AlarmInfo extends Alarm {
29 super(alarm); 29 super(alarm);
30 } 30 }
31 31
  32 + public AlarmInfo(Alarm alarm, String originatorName) {
  33 + super(alarm);
  34 + this.originatorName = originatorName;
  35 + }
  36 +
32 public String getOriginatorName() { 37 public String getOriginatorName() {
33 return originatorName; 38 return originatorName;
34 } 39 }
@@ -40,5 +40,5 @@ public interface AlarmDao extends Dao<Alarm> { @@ -40,5 +40,5 @@ public interface AlarmDao extends Dao<Alarm> {
40 40
41 Alarm save(TenantId tenantId, Alarm alarm); 41 Alarm save(TenantId tenantId, Alarm alarm);
42 42
43 - ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query); 43 + PageData<AlarmInfo> findAlarms(TenantId tenantId, AlarmQuery query);
44 } 44 }
@@ -261,27 +261,25 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -261,27 +261,25 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
261 261
262 @Override 262 @Override
263 public ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query) { 263 public ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query) {
264 - ListenableFuture<PageData<AlarmInfo>> alarms = alarmDao.findAlarms(tenantId, query); 264 + PageData<AlarmInfo> alarms = alarmDao.findAlarms(tenantId, query);
265 if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) { 265 if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
266 - alarms = Futures.transformAsync(alarms, input -> {  
267 - List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.getData().size());  
268 - for (AlarmInfo alarmInfo : input.getData()) {  
269 - alarmFutures.add(Futures.transform(  
270 - entityService.fetchEntityNameAsync(tenantId, alarmInfo.getOriginator()), originatorName -> {  
271 - if (originatorName == null) {  
272 - originatorName = "Deleted";  
273 - }  
274 - alarmInfo.setOriginatorName(originatorName);  
275 - return alarmInfo; 266 + List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(alarms.getData().size());
  267 + for (AlarmInfo alarmInfo : alarms.getData()) {
  268 + alarmFutures.add(Futures.transform(
  269 + entityService.fetchEntityNameAsync(tenantId, alarmInfo.getOriginator()), originatorName -> {
  270 + if (originatorName == null) {
  271 + originatorName = "Deleted";
276 } 272 }
277 - ));  
278 - }  
279 - return Futures.transform(Futures.successfulAsList(alarmFutures), alarmInfos -> {  
280 - return new PageData(alarmInfos, input.getTotalPages(), input.getTotalElements(), input.hasNext());  
281 - }); 273 + alarmInfo.setOriginatorName(originatorName);
  274 + return alarmInfo;
  275 + }
  276 + ));
  277 + }
  278 + return Futures.transform(Futures.successfulAsList(alarmFutures), alarmInfos -> {
  279 + return new PageData(alarmInfos, alarms.getTotalPages(), alarms.getTotalElements(), alarms.hasNext());
282 }); 280 });
283 } 281 }
284 - return alarms; 282 + return Futures.immediateFuture(alarms);
285 } 283 }
286 284
287 @Override 285 @Override
@@ -293,14 +291,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -293,14 +291,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
293 AlarmQuery query; 291 AlarmQuery query;
294 while (hasNext && AlarmSeverity.CRITICAL != highestSeverity) { 292 while (hasNext && AlarmSeverity.CRITICAL != highestSeverity) {
295 query = new AlarmQuery(entityId, nextPageLink, alarmSearchStatus, alarmStatus, false); 293 query = new AlarmQuery(entityId, nextPageLink, alarmSearchStatus, alarmStatus, false);
296 - PageData<AlarmInfo> alarms;  
297 - try {  
298 - alarms = alarmDao.findAlarms(tenantId, query).get();  
299 - } catch (ExecutionException | InterruptedException e) {  
300 - log.warn("Failed to find highest alarm severity. EntityId: [{}], AlarmSearchStatus: [{}], AlarmStatus: [{}]",  
301 - entityId, alarmSearchStatus, alarmStatus);  
302 - throw new RuntimeException(e);  
303 - } 294 + PageData<AlarmInfo> alarms = alarmDao.findAlarms(tenantId, query);
304 if (alarms.hasNext()) { 295 if (alarms.hasNext()) {
305 nextPageLink = nextPageLink.nextPageLink(); 296 nextPageLink = nextPageLink.nextPageLink();
306 } 297 }
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.sql;
  17 +
  18 +import com.datastax.driver.core.utils.UUIDs;
  19 +import com.fasterxml.jackson.databind.JsonNode;
  20 +import lombok.Data;
  21 +import lombok.EqualsAndHashCode;
  22 +import org.hibernate.annotations.Type;
  23 +import org.hibernate.annotations.TypeDef;
  24 +import org.thingsboard.server.common.data.EntityType;
  25 +import org.thingsboard.server.common.data.UUIDConverter;
  26 +import org.thingsboard.server.common.data.alarm.Alarm;
  27 +import org.thingsboard.server.common.data.alarm.AlarmId;
  28 +import org.thingsboard.server.common.data.alarm.AlarmSeverity;
  29 +import org.thingsboard.server.common.data.alarm.AlarmStatus;
  30 +import org.thingsboard.server.common.data.id.EntityIdFactory;
  31 +import org.thingsboard.server.common.data.id.TenantId;
  32 +import org.thingsboard.server.dao.model.BaseEntity;
  33 +import org.thingsboard.server.dao.model.BaseSqlEntity;
  34 +import org.thingsboard.server.dao.model.ModelConstants;
  35 +import org.thingsboard.server.dao.util.mapping.JsonStringType;
  36 +
  37 +import javax.persistence.*;
  38 +
  39 +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ACK_TS_PROPERTY;
  40 +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CLEAR_TS_PROPERTY;
  41 +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COLUMN_FAMILY_NAME;
  42 +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_END_TS_PROPERTY;
  43 +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY;
  44 +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY;
  45 +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_PROPERTY;
  46 +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_SEVERITY_PROPERTY;
  47 +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_START_TS_PROPERTY;
  48 +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_STATUS_PROPERTY;
  49 +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TENANT_ID_PROPERTY;
  50 +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TYPE_PROPERTY;
  51 +
  52 +@Data
  53 +@EqualsAndHashCode(callSuper = true)
  54 +@TypeDef(name = "json", typeClass = JsonStringType.class)
  55 +@MappedSuperclass
  56 +public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity<T> implements BaseEntity<T> {
  57 +
  58 + @Column(name = ALARM_TENANT_ID_PROPERTY)
  59 + private String tenantId;
  60 +
  61 + @Column(name = ALARM_ORIGINATOR_ID_PROPERTY)
  62 + private String originatorId;
  63 +
  64 + @Column(name = ALARM_ORIGINATOR_TYPE_PROPERTY)
  65 + private EntityType originatorType;
  66 +
  67 + @Column(name = ALARM_TYPE_PROPERTY)
  68 + private String type;
  69 +
  70 + @Enumerated(EnumType.STRING)
  71 + @Column(name = ALARM_SEVERITY_PROPERTY)
  72 + private AlarmSeverity severity;
  73 +
  74 + @Enumerated(EnumType.STRING)
  75 + @Column(name = ALARM_STATUS_PROPERTY)
  76 + private AlarmStatus status;
  77 +
  78 + @Column(name = ALARM_START_TS_PROPERTY)
  79 + private Long startTs;
  80 +
  81 + @Column(name = ALARM_END_TS_PROPERTY)
  82 + private Long endTs;
  83 +
  84 + @Column(name = ALARM_ACK_TS_PROPERTY)
  85 + private Long ackTs;
  86 +
  87 + @Column(name = ALARM_CLEAR_TS_PROPERTY)
  88 + private Long clearTs;
  89 +
  90 + @Type(type = "json")
  91 + @Column(name = ModelConstants.ASSET_ADDITIONAL_INFO_PROPERTY)
  92 + private JsonNode details;
  93 +
  94 + @Column(name = ALARM_PROPAGATE_PROPERTY)
  95 + private Boolean propagate;
  96 +
  97 + public AbstractAlarmEntity() {
  98 + super();
  99 + }
  100 +
  101 + public AbstractAlarmEntity(Alarm alarm) {
  102 + if (alarm.getId() != null) {
  103 + this.setId(alarm.getId().getId());
  104 + }
  105 + if (alarm.getTenantId() != null) {
  106 + this.tenantId = UUIDConverter.fromTimeUUID(alarm.getTenantId().getId());
  107 + }
  108 + this.type = alarm.getType();
  109 + this.originatorId = UUIDConverter.fromTimeUUID(alarm.getOriginator().getId());
  110 + this.originatorType = alarm.getOriginator().getEntityType();
  111 + this.type = alarm.getType();
  112 + this.severity = alarm.getSeverity();
  113 + this.status = alarm.getStatus();
  114 + this.propagate = alarm.isPropagate();
  115 + this.startTs = alarm.getStartTs();
  116 + this.endTs = alarm.getEndTs();
  117 + this.ackTs = alarm.getAckTs();
  118 + this.clearTs = alarm.getClearTs();
  119 + this.details = alarm.getDetails();
  120 + }
  121 +
  122 + public AbstractAlarmEntity(AlarmEntity alarmEntity) {
  123 + this.setId(alarmEntity.getId());
  124 + this.tenantId = alarmEntity.getTenantId();
  125 + this.type = alarmEntity.getType();
  126 + this.originatorId = alarmEntity.getOriginatorId();
  127 + this.originatorType = alarmEntity.getOriginatorType();
  128 + this.type = alarmEntity.getType();
  129 + this.severity = alarmEntity.getSeverity();
  130 + this.status = alarmEntity.getStatus();
  131 + this.propagate = alarmEntity.getPropagate();
  132 + this.startTs = alarmEntity.getStartTs();
  133 + this.endTs = alarmEntity.getEndTs();
  134 + this.ackTs = alarmEntity.getAckTs();
  135 + this.clearTs = alarmEntity.getClearTs();
  136 + this.details = alarmEntity.getDetails();
  137 + }
  138 +
  139 + protected Alarm toAlarm() {
  140 + Alarm alarm = new Alarm(new AlarmId(UUIDConverter.fromString(id)));
  141 + alarm.setCreatedTime(UUIDs.unixTimestamp(UUIDConverter.fromString(id)));
  142 + if (tenantId != null) {
  143 + alarm.setTenantId(new TenantId(UUIDConverter.fromString(tenantId)));
  144 + }
  145 + alarm.setOriginator(EntityIdFactory.getByTypeAndUuid(originatorType, UUIDConverter.fromString(originatorId)));
  146 + alarm.setType(type);
  147 + alarm.setSeverity(severity);
  148 + alarm.setStatus(status);
  149 + alarm.setPropagate(propagate);
  150 + alarm.setStartTs(startTs);
  151 + alarm.setEndTs(endTs);
  152 + alarm.setAckTs(ackTs);
  153 + alarm.setClearTs(clearTs);
  154 + alarm.setDetails(details);
  155 + return alarm;
  156 + }
  157 +}
@@ -83,7 +83,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti @@ -83,7 +83,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
83 } 83 }
84 84
85 public AbstractDeviceEntity(DeviceEntity deviceEntity) { 85 public AbstractDeviceEntity(DeviceEntity deviceEntity) {
86 - this.setId(deviceEntity.getId());; 86 + this.setId(deviceEntity.getId());
87 this.tenantId = deviceEntity.getTenantId(); 87 this.tenantId = deviceEntity.getTenantId();
88 this.customerId = deviceEntity.getCustomerId(); 88 this.customerId = deviceEntity.getCustomerId();
89 this.type = deviceEntity.getType(); 89 this.type = deviceEntity.getType();
@@ -15,133 +15,34 @@ @@ -15,133 +15,34 @@
15 */ 15 */
16 package org.thingsboard.server.dao.model.sql; 16 package org.thingsboard.server.dao.model.sql;
17 17
18 -import com.datastax.driver.core.utils.UUIDs;  
19 -import com.fasterxml.jackson.databind.JsonNode;  
20 import lombok.Data; 18 import lombok.Data;
21 import lombok.EqualsAndHashCode; 19 import lombok.EqualsAndHashCode;
22 -import org.hibernate.annotations.Type;  
23 import org.hibernate.annotations.TypeDef; 20 import org.hibernate.annotations.TypeDef;
24 -import org.thingsboard.server.common.data.EntityType;  
25 -import org.thingsboard.server.common.data.UUIDConverter;  
26 import org.thingsboard.server.common.data.alarm.Alarm; 21 import org.thingsboard.server.common.data.alarm.Alarm;
27 -import org.thingsboard.server.common.data.alarm.AlarmId;  
28 -import org.thingsboard.server.common.data.alarm.AlarmSeverity;  
29 -import org.thingsboard.server.common.data.alarm.AlarmStatus;  
30 -import org.thingsboard.server.common.data.id.EntityIdFactory;  
31 -import org.thingsboard.server.common.data.id.TenantId;  
32 -import org.thingsboard.server.dao.model.BaseEntity;  
33 -import org.thingsboard.server.dao.model.BaseSqlEntity;  
34 -import org.thingsboard.server.dao.model.ModelConstants;  
35 import org.thingsboard.server.dao.util.mapping.JsonStringType; 22 import org.thingsboard.server.dao.util.mapping.JsonStringType;
36 23
37 -import javax.persistence.Column;  
38 import javax.persistence.Entity; 24 import javax.persistence.Entity;
39 -import javax.persistence.EnumType;  
40 -import javax.persistence.Enumerated;  
41 import javax.persistence.Table; 25 import javax.persistence.Table;
42 26
43 -import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ACK_TS_PROPERTY;  
44 -import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CLEAR_TS_PROPERTY;  
45 import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COLUMN_FAMILY_NAME; 27 import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COLUMN_FAMILY_NAME;
46 -import static org.thingsboard.server.dao.model.ModelConstants.ALARM_END_TS_PROPERTY;  
47 -import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY;  
48 -import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY;  
49 -import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_PROPERTY;  
50 -import static org.thingsboard.server.dao.model.ModelConstants.ALARM_SEVERITY_PROPERTY;  
51 -import static org.thingsboard.server.dao.model.ModelConstants.ALARM_START_TS_PROPERTY;  
52 -import static org.thingsboard.server.dao.model.ModelConstants.ALARM_STATUS_PROPERTY;  
53 -import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TENANT_ID_PROPERTY;  
54 -import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TYPE_PROPERTY;  
55 28
56 @Data 29 @Data
57 @EqualsAndHashCode(callSuper = true) 30 @EqualsAndHashCode(callSuper = true)
58 @Entity 31 @Entity
59 @TypeDef(name = "json", typeClass = JsonStringType.class) 32 @TypeDef(name = "json", typeClass = JsonStringType.class)
60 @Table(name = ALARM_COLUMN_FAMILY_NAME) 33 @Table(name = ALARM_COLUMN_FAMILY_NAME)
61 -public final class AlarmEntity extends BaseSqlEntity<Alarm> implements BaseEntity<Alarm> {  
62 -  
63 - @Column(name = ALARM_TENANT_ID_PROPERTY)  
64 - private String tenantId;  
65 -  
66 - @Column(name = ALARM_ORIGINATOR_ID_PROPERTY)  
67 - private String originatorId;  
68 -  
69 - @Column(name = ALARM_ORIGINATOR_TYPE_PROPERTY)  
70 - private EntityType originatorType;  
71 -  
72 - @Column(name = ALARM_TYPE_PROPERTY)  
73 - private String type;  
74 -  
75 - @Enumerated(EnumType.STRING)  
76 - @Column(name = ALARM_SEVERITY_PROPERTY)  
77 - private AlarmSeverity severity;  
78 -  
79 - @Enumerated(EnumType.STRING)  
80 - @Column(name = ALARM_STATUS_PROPERTY)  
81 - private AlarmStatus status;  
82 -  
83 - @Column(name = ALARM_START_TS_PROPERTY)  
84 - private Long startTs;  
85 -  
86 - @Column(name = ALARM_END_TS_PROPERTY)  
87 - private Long endTs;  
88 -  
89 - @Column(name = ALARM_ACK_TS_PROPERTY)  
90 - private Long ackTs;  
91 -  
92 - @Column(name = ALARM_CLEAR_TS_PROPERTY)  
93 - private Long clearTs;  
94 -  
95 - @Type(type = "json")  
96 - @Column(name = ModelConstants.ASSET_ADDITIONAL_INFO_PROPERTY)  
97 - private JsonNode details;  
98 -  
99 - @Column(name = ALARM_PROPAGATE_PROPERTY)  
100 - private Boolean propagate; 34 +public final class AlarmEntity extends AbstractAlarmEntity<Alarm> {
101 35
102 public AlarmEntity() { 36 public AlarmEntity() {
103 super(); 37 super();
104 } 38 }
105 39
106 public AlarmEntity(Alarm alarm) { 40 public AlarmEntity(Alarm alarm) {
107 - if (alarm.getId() != null) {  
108 - this.setId(alarm.getId().getId());  
109 - }  
110 - if (alarm.getTenantId() != null) {  
111 - this.tenantId = UUIDConverter.fromTimeUUID(alarm.getTenantId().getId());  
112 - }  
113 - this.type = alarm.getType();  
114 - this.originatorId = UUIDConverter.fromTimeUUID(alarm.getOriginator().getId());  
115 - this.originatorType = alarm.getOriginator().getEntityType();  
116 - this.type = alarm.getType();  
117 - this.severity = alarm.getSeverity();  
118 - this.status = alarm.getStatus();  
119 - this.propagate = alarm.isPropagate();  
120 - this.startTs = alarm.getStartTs();  
121 - this.endTs = alarm.getEndTs();  
122 - this.ackTs = alarm.getAckTs();  
123 - this.clearTs = alarm.getClearTs();  
124 - this.details = alarm.getDetails(); 41 + super(alarm);
125 } 42 }
126 43
127 @Override 44 @Override
128 public Alarm toData() { 45 public Alarm toData() {
129 - Alarm alarm = new Alarm(new AlarmId(UUIDConverter.fromString(id)));  
130 - alarm.setCreatedTime(UUIDs.unixTimestamp(UUIDConverter.fromString(id)));  
131 - if (tenantId != null) {  
132 - alarm.setTenantId(new TenantId(UUIDConverter.fromString(tenantId)));  
133 - }  
134 - alarm.setOriginator(EntityIdFactory.getByTypeAndUuid(originatorType, UUIDConverter.fromString(originatorId)));  
135 - alarm.setType(type);  
136 - alarm.setSeverity(severity);  
137 - alarm.setStatus(status);  
138 - alarm.setPropagate(propagate);  
139 - alarm.setStartTs(startTs);  
140 - alarm.setEndTs(endTs);  
141 - alarm.setAckTs(ackTs);  
142 - alarm.setClearTs(clearTs);  
143 - alarm.setDetails(details);  
144 - return alarm; 46 + return super.toAlarm();
145 } 47 }
146 -  
147 -}  
  48 +}
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.sql;
  17 +
  18 +import lombok.Data;
  19 +import lombok.EqualsAndHashCode;
  20 +import org.thingsboard.server.common.data.alarm.AlarmInfo;
  21 +
  22 +@Data
  23 +@EqualsAndHashCode(callSuper = true)
  24 +public class AlarmInfoEntity extends AbstractAlarmEntity<AlarmInfo> {
  25 +
  26 + private String originatorName;
  27 +
  28 + public AlarmInfoEntity() {
  29 + super();
  30 + }
  31 +
  32 + public AlarmInfoEntity(AlarmEntity alarmEntity) {
  33 + super(alarmEntity);
  34 + }
  35 +
  36 + @Override
  37 + public AlarmInfo toData() {
  38 + return new AlarmInfo(super.toAlarm(), this.originatorName);
  39 + }
  40 +}
@@ -15,12 +15,14 @@ @@ -15,12 +15,14 @@
15 */ 15 */
16 package org.thingsboard.server.dao.sql.alarm; 16 package org.thingsboard.server.dao.sql.alarm;
17 17
  18 +import org.springframework.data.domain.Page;
18 import org.springframework.data.domain.Pageable; 19 import org.springframework.data.domain.Pageable;
19 import org.springframework.data.jpa.repository.Query; 20 import org.springframework.data.jpa.repository.Query;
20 import org.springframework.data.repository.CrudRepository; 21 import org.springframework.data.repository.CrudRepository;
21 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
22 import org.thingsboard.server.common.data.EntityType; 23 import org.thingsboard.server.common.data.EntityType;
23 import org.thingsboard.server.dao.model.sql.AlarmEntity; 24 import org.thingsboard.server.dao.model.sql.AlarmEntity;
  25 +import org.thingsboard.server.dao.model.sql.AlarmInfoEntity;
24 import org.thingsboard.server.dao.util.SqlDao; 26 import org.thingsboard.server.dao.util.SqlDao;
25 27
26 import java.util.List; 28 import java.util.List;
@@ -38,4 +40,26 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, String> { @@ -38,4 +40,26 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, String> {
38 @Param("entityType") EntityType entityType, 40 @Param("entityType") EntityType entityType,
39 @Param("alarmType") String alarmType, 41 @Param("alarmType") String alarmType,
40 Pageable pageable); 42 Pageable pageable);
  43 +
  44 + @Query("SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a) FROM AlarmEntity a, " +
  45 + "RelationEntity re " +
  46 + "WHERE a.tenantId = :tenantId " +
  47 + "AND a.id = re.toId AND re.toType = 'ALARM' " +
  48 + "AND re.relationTypeGroup = 'ALARM' " +
  49 + "AND re.relationType = :relationType " +
  50 + "AND re.fromId = :affectedEntityId " +
  51 + "AND re.fromType = :affectedEntityType " +
  52 + "AND (:startId IS NULL OR a.id >= :startId) " +
  53 + "AND (:endId IS NULL OR a.id <= :endId) " +
  54 + "AND (LOWER(a.type) LIKE LOWER(CONCAT(:searchText, '%'))" +
  55 + "OR LOWER(a.severity) LIKE LOWER(CONCAT(:searchText, '%'))" +
  56 + "OR LOWER(a.status) LIKE LOWER(CONCAT(:searchText, '%')))")
  57 + Page<AlarmInfoEntity> findAlarms(@Param("tenantId") String tenantId,
  58 + @Param("affectedEntityId") String affectedEntityId,
  59 + @Param("affectedEntityType") String affectedEntityType,
  60 + @Param("relationType") String relationType,
  61 + @Param("startId") String startId,
  62 + @Param("endId") String endId,
  63 + @Param("searchText") String searchText,
  64 + Pageable pageable);
41 } 65 }
@@ -43,8 +43,13 @@ import org.thingsboard.server.dao.util.SqlDao; @@ -43,8 +43,13 @@ import org.thingsboard.server.dao.util.SqlDao;
43 43
44 import java.util.ArrayList; 44 import java.util.ArrayList;
45 import java.util.List; 45 import java.util.List;
  46 +import java.util.Objects;
46 import java.util.UUID; 47 import java.util.UUID;
47 48
  49 +import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID;
  50 +import static org.thingsboard.server.dao.DaoUtil.endTimeToId;
  51 +import static org.thingsboard.server.dao.DaoUtil.startTimeToId;
  52 +
48 /** 53 /**
49 * Created by Valerii Sosliuk on 5/19/2017. 54 * Created by Valerii Sosliuk on 5/19/2017.
50 */ 55 */
@@ -93,7 +98,7 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A @@ -93,7 +98,7 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
93 } 98 }
94 99
95 @Override 100 @Override
96 - public ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query) { 101 + public PageData<AlarmInfo> findAlarms(TenantId tenantId, AlarmQuery query) {
97 log.trace("Try to find alarms by entity [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getStatus(), query.getPageLink()); 102 log.trace("Try to find alarms by entity [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getStatus(), query.getPageLink());
98 EntityId affectedEntity = query.getAffectedEntityId(); 103 EntityId affectedEntity = query.getAffectedEntityId();
99 String searchStatusName; 104 String searchStatusName;
@@ -105,17 +110,18 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A @@ -105,17 +110,18 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
105 searchStatusName = query.getStatus().name(); 110 searchStatusName = query.getStatus().name();
106 } 111 }
107 String relationType = BaseAlarmService.ALARM_RELATION_PREFIX + searchStatusName; 112 String relationType = BaseAlarmService.ALARM_RELATION_PREFIX + searchStatusName;
108 - ListenableFuture<PageData<EntityRelation>> relations = relationDao.findRelations(tenantId, affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink());  
109 - return Futures.transformAsync(relations, input -> {  
110 - List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.getData().size());  
111 - for (EntityRelation relation : input.getData()) {  
112 - alarmFutures.add(Futures.transform(  
113 - findAlarmByIdAsync(tenantId, relation.getTo().getId()),  
114 - AlarmInfo::new));  
115 - }  
116 - return Futures.transform(Futures.successfulAsList(alarmFutures), alarmInfos -> {  
117 - return new PageData(alarmInfos, input.getTotalPages(), input.getTotalElements(), input.hasNext());  
118 - });  
119 - }); 113 +
  114 + return DaoUtil.toPageData(
  115 + alarmRepository.findAlarms(
  116 + fromTimeUUID(tenantId.getId()),
  117 + fromTimeUUID(affectedEntity.getId()),
  118 + affectedEntity.getEntityType().name(),
  119 + relationType,
  120 + startTimeToId(query.getPageLink().getStartTime()),
  121 + endTimeToId(query.getPageLink().getEndTime()),
  122 + Objects.toString(query.getPageLink().getTextSearch(), ""),
  123 + DaoUtil.toPageable(query.getPageLink())
  124 + )
  125 + );
120 } 126 }
121 } 127 }
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { Injectable } from '@angular/core';
  18 +import { defaultHttpOptions } from './http-utils';
  19 +import { Observable } from 'rxjs/index';
  20 +import { HttpClient } from '@angular/common/http';
  21 +import { PageData } from '@shared/models/page/page-data';
  22 +import { EntityId } from '@shared/models/id/entity-id';
  23 +import {
  24 + Alarm,
  25 + AlarmInfo,
  26 + AlarmQuery,
  27 + AlarmSearchStatus,
  28 + AlarmSeverity,
  29 + AlarmStatus
  30 +} from '@shared/models/alarm.models';
  31 +
  32 +@Injectable({
  33 + providedIn: 'root'
  34 +})
  35 +export class AlarmService {
  36 +
  37 + constructor(
  38 + private http: HttpClient
  39 + ) { }
  40 +
  41 + public getAlarm(alarmId: string, ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<Alarm> {
  42 + return this.http.get<Alarm>(`/api/alarm/${alarmId}`, defaultHttpOptions(ignoreLoading, ignoreErrors));
  43 + }
  44 +
  45 + public getAlarmInfo(alarmId: string, ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<AlarmInfo> {
  46 + return this.http.get<AlarmInfo>(`/api/alarm/info/${alarmId}`, defaultHttpOptions(ignoreLoading, ignoreErrors));
  47 + }
  48 +
  49 + public saveAlarm(alarm: Alarm, ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<Alarm> {
  50 + return this.http.post<Alarm>('/api/alarm', alarm, defaultHttpOptions(ignoreLoading, ignoreErrors));
  51 + }
  52 +
  53 + public ackAlarm(alarmId: string, ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<void> {
  54 + return this.http.post<void>(`/api/alarm/${alarmId}/ack`, null, defaultHttpOptions(ignoreLoading, ignoreErrors));
  55 + }
  56 +
  57 + public clearAlarm(alarmId: string, ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<void> {
  58 + return this.http.post<void>(`/api/alarm/${alarmId}/clear`, null, defaultHttpOptions(ignoreLoading, ignoreErrors));
  59 + }
  60 +
  61 + public getAlarms(query: AlarmQuery,
  62 + ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<PageData<AlarmInfo>> {
  63 + return this.http.get<PageData<AlarmInfo>>(`/api/alarm${query.toQuery()}`,
  64 + defaultHttpOptions(ignoreLoading, ignoreErrors));
  65 + }
  66 +
  67 + public getHighestAlarmSeverity(entityId: EntityId, alarmSearchStatus: AlarmSearchStatus, alarmStatus: AlarmStatus,
  68 + ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<AlarmSeverity> {
  69 + let url = `/api/alarm/highestSeverity/${entityId.entityType}/${entityId.entityType}`;
  70 + if (alarmSearchStatus) {
  71 + url += `?searchStatus=${alarmSearchStatus}`;
  72 + } else if (alarmStatus) {
  73 + url += `?status=${alarmStatus}`;
  74 + }
  75 + return this.http.get<AlarmSeverity>(url,
  76 + defaultHttpOptions(ignoreLoading, ignoreErrors));
  77 + }
  78 +}
  1 +<!--
  2 +
  3 + Copyright © 2016-2019 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<form [formGroup]="alarmFormGroup" style="width: 600px;">
  19 + <mat-toolbar fxLayout="row" color="primary">
  20 + <h2>{{ 'alarm.alarm-details' | translate }}</h2>
  21 + <span fxFlex></span>
  22 + <button mat-button mat-icon-button
  23 + (click)="close()"
  24 + type="button">
  25 + <mat-icon class="material-icons">close</mat-icon>
  26 + </button>
  27 + </mat-toolbar>
  28 + <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
  29 + </mat-progress-bar>
  30 + <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
  31 + <div mat-dialog-content>
  32 + <fieldset [disabled]="isLoading$ | async">
  33 + <div fxLayout="row" fxLayoutGap="6px">
  34 + <mat-form-field class="mat-block">
  35 + <mat-label translate>alarm.created-time</mat-label>
  36 + <input matInput formControlName="createdTime" readonly>
  37 + </mat-form-field>
  38 + <mat-form-field fxFlex class="mat-block">
  39 + <mat-label translate>alarm.originator</mat-label>
  40 + <input matInput formControlName="originatorName" readonly>
  41 + </mat-form-field>
  42 + </div>
  43 + <div fxLayout="row" fxLayoutGap="6px" *ngIf="alarmFormGroup.get('startTime').value ||
  44 + alarmFormGroup.get('endTime').value">
  45 + <mat-form-field *ngIf="alarmFormGroup.get('startTime').value" fxFlex class="mat-block">
  46 + <mat-label translate>alarm.start-time</mat-label>
  47 + <input matInput formControlName="startTime" readonly>
  48 + </mat-form-field>
  49 + <mat-form-field *ngIf="alarmFormGroup.get('endTime').value" fxFlex class="mat-block">
  50 + <mat-label translate>alarm.end-time</mat-label>
  51 + <input matInput formControlName="endTime" readonly>
  52 + </mat-form-field>
  53 + <span fxFlex *ngIf="!alarmFormGroup.get('startTime').value || !alarmFormGroup.get('endTime').value"></span>
  54 + </div>
  55 + <div fxLayout="row" fxLayoutGap="6px" *ngIf="alarmFormGroup.get('ackTime').value ||
  56 + alarmFormGroup.get('clearTime').value">
  57 + <mat-form-field *ngIf="alarmFormGroup.get('ackTime').value" fxFlex class="mat-block">
  58 + <mat-label translate>alarm.ack-time</mat-label>
  59 + <input matInput formControlName="ackTime" readonly>
  60 + </mat-form-field>
  61 + <mat-form-field *ngIf="alarmFormGroup.get('clearTime').value" fxFlex class="mat-block">
  62 + <mat-label translate>alarm.clear-time</mat-label>
  63 + <input matInput formControlName="clearTime" readonly>
  64 + </mat-form-field>
  65 + <span fxFlex *ngIf="!alarmFormGroup.get('ackTime').value || !alarmFormGroup.get('clearTime').value"></span>
  66 + </div>
  67 + <div fxLayout="row" fxLayoutGap="6px">
  68 + <mat-form-field fxFlex class="mat-block">
  69 + <mat-label translate>alarm.type</mat-label>
  70 + <input matInput formControlName="type" readonly>
  71 + </mat-form-field>
  72 + <mat-form-field fxFlex class="mat-block">
  73 + <mat-label translate>alarm.severity</mat-label>
  74 + <input matInput formControlName="alarmSeverity" readonly
  75 + [ngStyle]="{fontWeight: 'bold', color: alarmSeverityColorsMap.get((alarm$ | async)?.severity)}">
  76 + </mat-form-field>
  77 + <mat-form-field fxFlex class="mat-block">
  78 + <mat-label translate>alarm.status</mat-label>
  79 + <input matInput formControlName="alarmStatus" readonly>
  80 + </mat-form-field>
  81 + </div>
  82 + <tb-json-object-edit
  83 + *ngIf="displayDetails"
  84 + formControlName="alarmDetails"
  85 + readonly
  86 + label="{{ 'alarm.details' | translate }}">
  87 + </tb-json-object-edit>
  88 + </fieldset>
  89 + </div>
  90 + <div mat-dialog-actions fxLayout="row">
  91 + <div fxLayout="row" *ngIf="alarm$ | async; let alarm;">
  92 + <button *ngIf="allowAcknowledgment && (alarm.status === alarmStatuses.ACTIVE_UNACK ||
  93 + alarm.status === alarmStatuses.CLEARED_UNACK)"
  94 + mat-button
  95 + mat-raised-button
  96 + color="primary"
  97 + type="button"
  98 + (click)="acknowledge()"
  99 + style="margin-right: 20px;"
  100 + [disabled]="(isLoading$ | async)">
  101 + {{ 'alarm.acknowledge' | translate }}
  102 + </button>
  103 + <button *ngIf="allowClear && (alarm.status === alarmStatuses.ACTIVE_ACK ||
  104 + alarm.status === alarmStatuses.ACTIVE_UNACK)"
  105 + mat-button
  106 + mat-raised-button
  107 + color="primary"
  108 + type="button"
  109 + (click)="clear()"
  110 + [disabled]="(isLoading$ | async)">
  111 + {{ 'alarm.clear' | translate }}
  112 + </button>
  113 + </div>
  114 + <span fxFlex></span>
  115 + <button mat-button color="primary"
  116 + style="margin-right: 20px;"
  117 + type="button"
  118 + [disabled]="(isLoading$ | async)"
  119 + (click)="close()" cdkFocusInitial>
  120 + {{ 'action.close' | translate }}
  121 + </button>
  122 + </div>
  123 +</form>
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { Component, Inject, OnInit } from '@angular/core';
  18 +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
  19 +import { Store } from '@ngrx/store';
  20 +import { AppState } from '@core/core.state';
  21 +import { FormBuilder, FormGroup } from '@angular/forms';
  22 +import { Observable, Subject } from 'rxjs';
  23 +import { Router } from '@angular/router';
  24 +import { DialogComponent } from '@app/shared/components/dialog.component';
  25 +import {
  26 + AlarmInfo,
  27 + alarmSeverityColors,
  28 + alarmSeverityTranslations,
  29 + AlarmStatus,
  30 + alarmStatusTranslations
  31 +} from '@app/shared/models/alarm.models';
  32 +import { AlarmService } from '@core/http/alarm.service';
  33 +import { share, tap } from 'rxjs/operators';
  34 +import { DatePipe } from '@angular/common';
  35 +import { TranslateService } from '@ngx-translate/core';
  36 +
  37 +export interface AlarmDetailsDialogData {
  38 + alarmId: string;
  39 + allowAcknowledgment: boolean;
  40 + allowClear: boolean;
  41 + displayDetails: boolean;
  42 +}
  43 +
  44 +@Component({
  45 + selector: 'tb-alarm-details-dialog',
  46 + templateUrl: './alarm-details-dialog.component.html',
  47 + styleUrls: []
  48 +})
  49 +export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDialogComponent, boolean> implements OnInit {
  50 +
  51 + alarmFormGroup: FormGroup;
  52 +
  53 + allowAcknowledgment: boolean;
  54 + allowClear: boolean;
  55 + displayDetails: boolean;
  56 +
  57 + loadAlarmSubject = new Subject<AlarmInfo>();
  58 + alarm$: Observable<AlarmInfo> = this.loadAlarmSubject.asObservable().pipe(
  59 + tap(alarm => this.loadAlarmFields(alarm)),
  60 + share()
  61 + );
  62 +
  63 + alarmSeverityColorsMap = alarmSeverityColors;
  64 + alarmStatuses = AlarmStatus;
  65 +
  66 + alarmUpdated = false;
  67 +
  68 + constructor(protected store: Store<AppState>,
  69 + protected router: Router,
  70 + private datePipe: DatePipe,
  71 + private translate: TranslateService,
  72 + @Inject(MAT_DIALOG_DATA) public data: AlarmDetailsDialogData,
  73 + private alarmService: AlarmService,
  74 + public dialogRef: MatDialogRef<AlarmDetailsDialogComponent, boolean>,
  75 + public fb: FormBuilder) {
  76 + super(store, router, dialogRef);
  77 +
  78 + this.allowAcknowledgment = data.allowAcknowledgment;
  79 + this.allowClear = data.allowClear;
  80 + this.displayDetails = data.displayDetails;
  81 +
  82 + this.alarmFormGroup = this.fb.group(
  83 + {
  84 + createdTime: [''],
  85 + originatorName: [''],
  86 + startTime: [''],
  87 + endTime: [''],
  88 + ackTime: [''],
  89 + clearTime: [''],
  90 + type: [''],
  91 + alarmSeverity: [''],
  92 + alarmStatus: [''],
  93 + alarmDetails: [null]
  94 + }
  95 + );
  96 +
  97 + this.loadAlarm();
  98 +
  99 + }
  100 +
  101 + loadAlarm() {
  102 + this.alarmService.getAlarmInfo(this.data.alarmId).subscribe(
  103 + alarm => this.loadAlarmSubject.next(alarm)
  104 + );
  105 + }
  106 +
  107 + loadAlarmFields(alarm: AlarmInfo) {
  108 + this.alarmFormGroup.get('createdTime')
  109 + .patchValue(this.datePipe.transform(alarm.createdTime, 'yyyy-MM-dd HH:mm:ss'));
  110 + this.alarmFormGroup.get('originatorName')
  111 + .patchValue(alarm.originatorName);
  112 + if (alarm.startTs) {
  113 + this.alarmFormGroup.get('startTime')
  114 + .patchValue(this.datePipe.transform(alarm.startTs, 'yyyy-MM-dd HH:mm:ss'));
  115 + }
  116 + if (alarm.endTs) {
  117 + this.alarmFormGroup.get('endTime')
  118 + .patchValue(this.datePipe.transform(alarm.endTs, 'yyyy-MM-dd HH:mm:ss'));
  119 + }
  120 + if (alarm.ackTs) {
  121 + this.alarmFormGroup.get('ackTime')
  122 + .patchValue(this.datePipe.transform(alarm.ackTs, 'yyyy-MM-dd HH:mm:ss'));
  123 + }
  124 + if (alarm.clearTs) {
  125 + this.alarmFormGroup.get('clearTime')
  126 + .patchValue(this.datePipe.transform(alarm.clearTs, 'yyyy-MM-dd HH:mm:ss'));
  127 + }
  128 + this.alarmFormGroup.get('type').patchValue(alarm.type);
  129 + this.alarmFormGroup.get('alarmSeverity')
  130 + .patchValue(this.translate.instant(alarmSeverityTranslations.get(alarm.severity)));
  131 + this.alarmFormGroup.get('alarmStatus')
  132 + .patchValue(this.translate.instant(alarmStatusTranslations.get(alarm.status)));
  133 + this.alarmFormGroup.get('alarmDetails').patchValue(alarm.details);
  134 + }
  135 +
  136 + ngOnInit(): void {
  137 + }
  138 +
  139 + close(): void {
  140 + this.dialogRef.close(this.alarmUpdated);
  141 + }
  142 +
  143 + acknowledge(): void {
  144 + this.alarmService.ackAlarm(this.data.alarmId).subscribe(
  145 + () => { this.alarmUpdated = true; this.loadAlarm(); }
  146 + );
  147 + }
  148 +
  149 + clear(): void {
  150 + this.alarmService.clearAlarm(this.data.alarmId).subscribe(
  151 + () => { this.alarmUpdated = true; this.loadAlarm(); }
  152 + );
  153 + }
  154 +
  155 +}
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import {
  18 + DateEntityTableColumn,
  19 + EntityTableColumn,
  20 + EntityTableConfig
  21 +} from '@home/models/entity/entities-table-config.models';
  22 +import { EntityType, EntityTypeResource, entityTypeTranslations } from '@shared/models/entity-type.models';
  23 +import { TranslateService } from '@ngx-translate/core';
  24 +import { DatePipe } from '@angular/common';
  25 +import { Direction } from '@shared/models/page/sort-order';
  26 +import { MatDialog } from '@angular/material';
  27 +import { TimePageLink } from '@shared/models/page/page-link';
  28 +import { Observable } from 'rxjs';
  29 +import { PageData } from '@shared/models/page/page-data';
  30 +import { EntityId } from '@shared/models/id/entity-id';
  31 +import {
  32 + AlarmInfo,
  33 + AlarmQuery,
  34 + AlarmSearchStatus,
  35 + alarmSeverityColors,
  36 + alarmSeverityTranslations,
  37 + alarmStatusTranslations
  38 +} from '@app/shared/models/alarm.models';
  39 +import { AlarmService } from '@app/core/http/alarm.service';
  40 +import { DialogService } from '@core/services/dialog.service';
  41 +import { AlarmTableHeaderComponent } from '@home/components/alarm/alarm-table-header.component';
  42 +import {
  43 + AuditLogDetailsDialogComponent,
  44 + AuditLogDetailsDialogData
  45 +} from '@home/components/audit-log/audit-log-details-dialog.component';
  46 +import {
  47 + AlarmDetailsDialogComponent,
  48 + AlarmDetailsDialogData
  49 +} from '@home/components/alarm/alarm-details-dialog.component';
  50 +
  51 +export class AlarmTableConfig extends EntityTableConfig<AlarmInfo, TimePageLink> {
  52 +
  53 + searchStatus: AlarmSearchStatus;
  54 +
  55 + constructor(private alarmService: AlarmService,
  56 + private dialogService: DialogService,
  57 + private translate: TranslateService,
  58 + private datePipe: DatePipe,
  59 + private dialog: MatDialog,
  60 + public entityId: EntityId = null,
  61 + private defaultSearchStatus: AlarmSearchStatus = AlarmSearchStatus.ANY) {
  62 + super();
  63 + this.loadDataOnInit = false;
  64 + this.tableTitle = '';
  65 + this.useTimePageLink = true;
  66 + this.detailsPanelEnabled = false;
  67 + this.selectionEnabled = false;
  68 + this.searchEnabled = true;
  69 + this.addEnabled = false;
  70 + this.entitiesDeleteEnabled = false;
  71 + this.actionsColumnTitle = 'alarm.details';
  72 + this.entityType = EntityType.ALARM;
  73 + this.entityTranslations = entityTypeTranslations.get(EntityType.ALARM);
  74 + this.entityResources = {
  75 + } as EntityTypeResource;
  76 + this.searchStatus = defaultSearchStatus;
  77 +
  78 + this.headerComponent = AlarmTableHeaderComponent;
  79 +
  80 + this.entitiesFetchFunction = pageLink => this.fetchAlarms(pageLink);
  81 +
  82 + this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC};
  83 +
  84 + this.columns.push(
  85 + new DateEntityTableColumn<AlarmInfo>('createdTime', 'alarm.created-time', this.datePipe, '150px'));
  86 + this.columns.push(
  87 + new EntityTableColumn<AlarmInfo>('originatorName', 'alarm.originator', '100%',
  88 + (entity) => entity.originatorName, entity => ({}), false));
  89 + this.columns.push(
  90 + new EntityTableColumn<AlarmInfo>('type', 'alarm.type', '100%'));
  91 + this.columns.push(
  92 + new EntityTableColumn<AlarmInfo>('severity', 'alarm.severity', '100%',
  93 + (entity) => this.translate.instant(alarmSeverityTranslations.get(entity.severity)),
  94 + entity => ({
  95 + fontWeight: 'bold',
  96 + color: alarmSeverityColors.get(entity.severity)
  97 + })));
  98 + this.columns.push(
  99 + new EntityTableColumn<AlarmInfo>('status', 'alarm.status', '100%',
  100 + (entity) => this.translate.instant(alarmStatusTranslations.get(entity.status))));
  101 +
  102 + this.cellActionDescriptors.push(
  103 + {
  104 + name: this.translate.instant('alarm.details'),
  105 + icon: 'more_horiz',
  106 + isEnabled: () => true,
  107 + onAction: ($event, entity) => this.showAlarmDetails(entity)
  108 + }
  109 + );
  110 + }
  111 +
  112 + fetchAlarms(pageLink: TimePageLink): Observable<PageData<AlarmInfo>> {
  113 + const query = new AlarmQuery(this.entityId, pageLink, this.searchStatus, null, true);
  114 + return this.alarmService.getAlarms(query);
  115 + }
  116 +
  117 + showAlarmDetails(entity: AlarmInfo) {
  118 + this.dialog.open<AlarmDetailsDialogComponent, AlarmDetailsDialogData, boolean>
  119 + (AlarmDetailsDialogComponent,
  120 + {
  121 + disableClose: true,
  122 + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
  123 + data: {
  124 + alarmId: entity.id.id,
  125 + allowAcknowledgment: true,
  126 + allowClear: true,
  127 + displayDetails: true
  128 + }
  129 + }).afterClosed().subscribe(
  130 + (res) => {
  131 + if (res) {
  132 + this.table.updateData();
  133 + }
  134 + }
  135 + );
  136 + }
  137 +}
  1 +<!--
  2 +
  3 + Copyright © 2016-2019 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +
  19 +<mat-form-field class="mat-block" style="width: 200px;">
  20 + <mat-label translate>alarm.alarm-status</mat-label>
  21 + <mat-select matInput [ngModel]="alarmTableConfig.searchStatus"
  22 + (ngModelChange)="searchStatusChanged($event)">
  23 + <mat-option *ngFor="let status of alarmSearchStatusTypes" [value]="status">
  24 + {{ alarmSearchStatusTranslationsMap.get(status) | translate }}
  25 + </mat-option>
  26 + </mat-select>
  27 +</mat-form-field>
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +:host {
  17 + /* flex: 1;
  18 + display: flex;
  19 + justify-content: flex-start; */
  20 + padding-right: 8px;
  21 +}
  22 +
  23 +:host ::ng-deep {
  24 + mat-form-field {
  25 + font-size: 16px;
  26 +
  27 + .mat-form-field-wrapper {
  28 + padding-bottom: 0;
  29 + }
  30 +
  31 + .mat-form-field-underline {
  32 + bottom: 0;
  33 + }
  34 + }
  35 +}
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { Component } from '@angular/core';
  18 +import { Store } from '@ngrx/store';
  19 +import { AppState } from '@core/core.state';
  20 +import { EntityTableHeaderComponent } from '../../components/entity/entity-table-header.component';
  21 +import { AlarmInfo, AlarmSearchStatus, alarmSearchStatusTranslations } from '@shared/models/alarm.models';
  22 +import { AlarmTableConfig } from './alarm-table-config';
  23 +
  24 +@Component({
  25 + selector: 'tb-alarm-table-header',
  26 + templateUrl: './alarm-table-header.component.html',
  27 + styleUrls: ['./alarm-table-header.component.scss']
  28 +})
  29 +export class AlarmTableHeaderComponent extends EntityTableHeaderComponent<AlarmInfo> {
  30 +
  31 + alarmSearchStatusTranslationsMap = alarmSearchStatusTranslations;
  32 +
  33 + alarmSearchStatusTypes = Object.keys(AlarmSearchStatus);
  34 +
  35 + get alarmTableConfig(): AlarmTableConfig {
  36 + return this.entitiesTableConfig as AlarmTableConfig;
  37 + }
  38 +
  39 + constructor(protected store: Store<AppState>) {
  40 + super(store);
  41 + }
  42 +
  43 + searchStatusChanged(searchStatus: AlarmSearchStatus) {
  44 + this.alarmTableConfig.searchStatus = searchStatus;
  45 + this.alarmTableConfig.table.updateData();
  46 + }
  47 +}
  1 +<!--
  2 +
  3 + Copyright © 2016-2019 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<tb-entities-table [entitiesTableConfig]="alarmTableConfig"></tb-entities-table>
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +:host ::ng-deep {
  17 + tb-entities-table {
  18 + .mat-drawer-container {
  19 + background-color: white;
  20 + }
  21 + }
  22 +}
  1 +///
  2 +/// Copyright © 2016-2019 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { Component, Input, OnInit, ViewChild } from '@angular/core';
  18 +import { TranslateService } from '@ngx-translate/core';
  19 +import { DatePipe } from '@angular/common';
  20 +import { MatDialog } from '@angular/material';
  21 +import { EntityId } from '@shared/models/id/entity-id';
  22 +import { EntitiesTableComponent } from '@home/components/entity/entities-table.component';
  23 +import { Store } from '@ngrx/store';
  24 +import { AppState } from '@core/core.state';
  25 +import { DialogService } from '@core/services/dialog.service';
  26 +import { AlarmTableConfig } from './alarm-table-config';
  27 +import { AlarmSearchStatus } from '@shared/models/alarm.models';
  28 +import { AlarmService } from '@app/core/http/alarm.service';
  29 +
  30 +@Component({
  31 + selector: 'tb-alarm-table',
  32 + templateUrl: './alarm-table.component.html',
  33 + styleUrls: ['./alarm-table.component.scss']
  34 +})
  35 +export class AlarmTableComponent implements OnInit {
  36 +
  37 + activeValue = false;
  38 + dirtyValue = false;
  39 + entityIdValue: EntityId;
  40 +
  41 + @Input()
  42 + set active(active: boolean) {
  43 + if (this.activeValue !== active) {
  44 + this.activeValue = active;
  45 + if (this.activeValue && this.dirtyValue) {
  46 + this.dirtyValue = false;
  47 + this.entitiesTable.updateData();
  48 + }
  49 + }
  50 + }
  51 +
  52 + @Input()
  53 + set entityId(entityId: EntityId) {
  54 + this.entityIdValue = entityId;
  55 + if (this.alarmTableConfig && this.alarmTableConfig.entityId !== entityId) {
  56 + this.alarmTableConfig.searchStatus = AlarmSearchStatus.ANY;
  57 + this.alarmTableConfig.entityId = entityId;
  58 + this.entitiesTable.resetSortAndFilter(this.activeValue);
  59 + if (!this.activeValue) {
  60 + this.dirtyValue = true;
  61 + }
  62 + }
  63 + }
  64 +
  65 + @ViewChild(EntitiesTableComponent, {static: true}) entitiesTable: EntitiesTableComponent;
  66 +
  67 + alarmTableConfig: AlarmTableConfig;
  68 +
  69 + constructor(private alarmService: AlarmService,
  70 + private dialogService: DialogService,
  71 + private translate: TranslateService,
  72 + private datePipe: DatePipe,
  73 + private dialog: MatDialog,
  74 + private store: Store<AppState>) {
  75 + }
  76 +
  77 + ngOnInit() {
  78 + this.dirtyValue = !this.activeValue;
  79 + this.alarmTableConfig = new AlarmTableConfig(
  80 + this.alarmService,
  81 + this.dialogService,
  82 + this.translate,
  83 + this.datePipe,
  84 + this.dialog,
  85 + this.entityIdValue,
  86 + AlarmSearchStatus.ANY
  87 + );
  88 + }
  89 +
  90 +}
@@ -28,13 +28,18 @@ import { EventTableHeaderComponent } from '@home/components/event/event-table-he @@ -28,13 +28,18 @@ import { EventTableHeaderComponent } from '@home/components/event/event-table-he
28 import { EventTableComponent } from '@home/components/event/event-table.component'; 28 import { EventTableComponent } from '@home/components/event/event-table.component';
29 import { RelationTableComponent } from '@home/components/relation/relation-table.component'; 29 import { RelationTableComponent } from '@home/components/relation/relation-table.component';
30 import { RelationDialogComponent } from './relation/relation-dialog.component'; 30 import { RelationDialogComponent } from './relation/relation-dialog.component';
  31 +import { AlarmTableHeaderComponent } from '@home/components/alarm/alarm-table-header.component';
  32 +import { AlarmTableComponent } from '@home/components/alarm/alarm-table.component';
  33 +import { AlarmDetailsDialogComponent } from '@home/components/alarm/alarm-details-dialog.component';
31 34
32 @NgModule({ 35 @NgModule({
33 entryComponents: [ 36 entryComponents: [
34 AddEntityDialogComponent, 37 AddEntityDialogComponent,
35 AuditLogDetailsDialogComponent, 38 AuditLogDetailsDialogComponent,
36 EventTableHeaderComponent, 39 EventTableHeaderComponent,
37 - RelationDialogComponent 40 + RelationDialogComponent,
  41 + AlarmTableHeaderComponent,
  42 + AlarmDetailsDialogComponent
38 ], 43 ],
39 declarations: 44 declarations:
40 [ 45 [
@@ -48,7 +53,10 @@ import { RelationDialogComponent } from './relation/relation-dialog.component'; @@ -48,7 +53,10 @@ import { RelationDialogComponent } from './relation/relation-dialog.component';
48 EventTableHeaderComponent, 53 EventTableHeaderComponent,
49 EventTableComponent, 54 EventTableComponent,
50 RelationTableComponent, 55 RelationTableComponent,
51 - RelationDialogComponent 56 + RelationDialogComponent,
  57 + AlarmTableHeaderComponent,
  58 + AlarmTableComponent,
  59 + AlarmDetailsDialogComponent
52 ], 60 ],
53 imports: [ 61 imports: [
54 CommonModule, 62 CommonModule,
@@ -62,7 +70,9 @@ import { RelationDialogComponent } from './relation/relation-dialog.component'; @@ -62,7 +70,9 @@ import { RelationDialogComponent } from './relation/relation-dialog.component';
62 ContactComponent, 70 ContactComponent,
63 AuditLogTableComponent, 71 AuditLogTableComponent,
64 EventTableComponent, 72 EventTableComponent,
65 - RelationTableComponent 73 + RelationTableComponent,
  74 + AlarmTableComponent,
  75 + AlarmDetailsDialogComponent
66 ] 76 ]
67 }) 77 })
68 export class HomeComponentsModule { } 78 export class HomeComponentsModule { }
@@ -16,6 +16,10 @@ @@ -16,6 +16,10 @@
16 16
17 --> 17 -->
18 <mat-tab *ngIf="entity" 18 <mat-tab *ngIf="entity"
  19 + label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab">
  20 + <tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table>
  21 +</mat-tab>
  22 +<mat-tab *ngIf="entity"
19 label="{{ 'asset.events' | translate }}" #eventsTab="matTab"> 23 label="{{ 'asset.events' | translate }}" #eventsTab="matTab">
20 <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id" 24 <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id"
21 [entityId]="entity.id"></tb-event-table> 25 [entityId]="entity.id"></tb-event-table>
@@ -16,6 +16,10 @@ @@ -16,6 +16,10 @@
16 16
17 --> 17 -->
18 <mat-tab *ngIf="entity" 18 <mat-tab *ngIf="entity"
  19 + label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab">
  20 + <tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table>
  21 +</mat-tab>
  22 +<mat-tab *ngIf="entity"
19 label="{{ 'customer.events' | translate }}" #eventsTab="matTab"> 23 label="{{ 'customer.events' | translate }}" #eventsTab="matTab">
20 <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id" 24 <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id"
21 [entityId]="entity.id"></tb-event-table> 25 [entityId]="entity.id"></tb-event-table>
@@ -62,7 +62,7 @@ @@ -62,7 +62,7 @@
62 </div> 62 </div>
63 <div class="mat-padding" fxLayout="column"> 63 <div class="mat-padding" fxLayout="column">
64 <mat-form-field class="mat-block" 64 <mat-form-field class="mat-block"
65 - [fxShow]="!isEdit && assignedCustomersText 65 + [fxShow]="!isEdit && assignedCustomersText?.length
66 && dashboardScope === 'tenant'"> 66 && dashboardScope === 'tenant'">
67 <mat-label translate>dashboard.assignedToCustomers</mat-label> 67 <mat-label translate>dashboard.assignedToCustomers</mat-label>
68 <input matInput disabled [ngModel]="assignedCustomersText"> 68 <input matInput disabled [ngModel]="assignedCustomersText">
@@ -16,6 +16,10 @@ @@ -16,6 +16,10 @@
16 16
17 --> 17 -->
18 <mat-tab *ngIf="entity" 18 <mat-tab *ngIf="entity"
  19 + label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab">
  20 + <tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table>
  21 +</mat-tab>
  22 +<mat-tab *ngIf="entity"
19 label="{{ 'device.events' | translate }}" #eventsTab="matTab"> 23 label="{{ 'device.events' | translate }}" #eventsTab="matTab">
20 <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id" 24 <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id"
21 [entityId]="entity.id"></tb-event-table> 25 [entityId]="entity.id"></tb-event-table>
@@ -16,6 +16,10 @@ @@ -16,6 +16,10 @@
16 16
17 --> 17 -->
18 <mat-tab *ngIf="entity" 18 <mat-tab *ngIf="entity"
  19 + label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab">
  20 + <tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table>
  21 +</mat-tab>
  22 +<mat-tab *ngIf="entity"
19 label="{{ 'entity-view.events' | translate }}" #eventsTab="matTab"> 23 label="{{ 'entity-view.events' | translate }}" #eventsTab="matTab">
20 <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id" 24 <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id"
21 [entityId]="entity.id"></tb-event-table> 25 [entityId]="entity.id"></tb-event-table>
@@ -16,6 +16,10 @@ @@ -16,6 +16,10 @@
16 16
17 --> 17 -->
18 <mat-tab *ngIf="entity" 18 <mat-tab *ngIf="entity"
  19 + label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab">
  20 + <tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table>
  21 +</mat-tab>
  22 +<mat-tab *ngIf="entity"
19 label="{{ 'rulechain.events' | translate }}" #eventsTab="matTab"> 23 label="{{ 'rulechain.events' | translate }}" #eventsTab="matTab">
20 <tb-event-table [active]="eventsTab.isActive" 24 <tb-event-table [active]="eventsTab.isActive"
21 [debugEventTypes]="[debugEventTypes.DEBUG_RULE_CHAIN]" 25 [debugEventTypes]="[debugEventTypes.DEBUG_RULE_CHAIN]"
@@ -16,6 +16,10 @@ @@ -16,6 +16,10 @@
16 16
17 --> 17 -->
18 <mat-tab *ngIf="entity" 18 <mat-tab *ngIf="entity"
  19 + label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab">
  20 + <tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table>
  21 +</mat-tab>
  22 +<mat-tab *ngIf="entity"
19 label="{{ 'tenant.events' | translate }}" #eventsTab="matTab"> 23 label="{{ 'tenant.events' | translate }}" #eventsTab="matTab">
20 <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="nullUid" 24 <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="nullUid"
21 [entityId]="entity.id"></tb-event-table> 25 [entityId]="entity.id"></tb-event-table>
@@ -42,7 +42,6 @@ export abstract class DialogComponent<T, R = any> extends PageComponent implemen @@ -42,7 +42,6 @@ export abstract class DialogComponent<T, R = any> extends PageComponent implemen
42 } 42 }
43 43
44 ngOnDestroy(): void { 44 ngOnDestroy(): void {
45 - console.log('Dialog destroy called');  
46 super.ngOnDestroy(); 45 super.ngOnDestroy();
47 if (this.routerSubscription) { 46 if (this.routerSubscription) {
48 this.routerSubscription.unsubscribe(); 47 this.routerSubscription.unsubscribe();
@@ -20,6 +20,8 @@ import {TenantId} from '@shared/models/id/tenant-id'; @@ -20,6 +20,8 @@ import {TenantId} from '@shared/models/id/tenant-id';
20 import {CustomerId} from '@shared/models/id/customer-id'; 20 import {CustomerId} from '@shared/models/id/customer-id';
21 import {AlarmId} from '@shared/models/id/alarm-id'; 21 import {AlarmId} from '@shared/models/id/alarm-id';
22 import {EntityId} from '@shared/models/id/entity-id'; 22 import {EntityId} from '@shared/models/id/entity-id';
  23 +import { ActionStatus } from '@shared/models/audit-log.models';
  24 +import { TimePageLink } from '@shared/models/page/page-link';
23 25
24 export enum AlarmSeverity { 26 export enum AlarmSeverity {
25 CRITICAL = 'CRITICAL', 27 CRITICAL = 'CRITICAL',
@@ -36,6 +38,53 @@ export enum AlarmStatus { @@ -36,6 +38,53 @@ export enum AlarmStatus {
36 CLEARED_ACK = 'CLEARED_ACK' 38 CLEARED_ACK = 'CLEARED_ACK'
37 } 39 }
38 40
  41 +export enum AlarmSearchStatus {
  42 + ANY = 'ANY',
  43 + ACTIVE = 'ACTIVE',
  44 + CLEARED = 'CLEARED',
  45 + ACK = 'ACK',
  46 + UNACK = 'UNACK'
  47 +}
  48 +
  49 +export const alarmSeverityTranslations = new Map<AlarmSeverity, string>(
  50 + [
  51 + [AlarmSeverity.CRITICAL, 'alarm.severity-critical'],
  52 + [AlarmSeverity.MAJOR, 'alarm.severity-major'],
  53 + [AlarmSeverity.MINOR, 'alarm.severity-minor'],
  54 + [AlarmSeverity.WARNING, 'alarm.severity-warning'],
  55 + [AlarmSeverity.INDETERMINATE, 'alarm.severity-indeterminate']
  56 + ]
  57 +);
  58 +
  59 +export const alarmStatusTranslations = new Map<AlarmStatus, string>(
  60 + [
  61 + [AlarmStatus.ACTIVE_UNACK, 'alarm.display-status.ACTIVE_UNACK'],
  62 + [AlarmStatus.ACTIVE_ACK, 'alarm.display-status.ACTIVE_ACK'],
  63 + [AlarmStatus.CLEARED_UNACK, 'alarm.display-status.CLEARED_UNACK'],
  64 + [AlarmStatus.CLEARED_ACK, 'alarm.display-status.CLEARED_ACK'],
  65 + ]
  66 +);
  67 +
  68 +export const alarmSearchStatusTranslations = new Map<AlarmSearchStatus, string>(
  69 + [
  70 + [AlarmSearchStatus.ANY, 'alarm.search-status.ANY'],
  71 + [AlarmSearchStatus.ACTIVE, 'alarm.search-status.ACTIVE'],
  72 + [AlarmSearchStatus.CLEARED, 'alarm.search-status.CLEARED'],
  73 + [AlarmSearchStatus.ACK, 'alarm.search-status.ACK'],
  74 + [AlarmSearchStatus.UNACK, 'alarm.search-status.UNACK']
  75 + ]
  76 +);
  77 +
  78 +export const alarmSeverityColors = new Map<AlarmSeverity, string>(
  79 + [
  80 + [AlarmSeverity.CRITICAL, 'red'],
  81 + [AlarmSeverity.MAJOR, 'orange'],
  82 + [AlarmSeverity.MINOR, '#ffca3d'],
  83 + [AlarmSeverity.WARNING, '#abab00'],
  84 + [AlarmSeverity.INDETERMINATE, 'green']
  85 + ]
  86 +);
  87 +
39 export interface Alarm extends BaseData<AlarmId> { 88 export interface Alarm extends BaseData<AlarmId> {
40 tenantId: TenantId; 89 tenantId: TenantId;
41 type: string; 90 type: string;
@@ -49,3 +98,41 @@ export interface Alarm extends BaseData<AlarmId> { @@ -49,3 +98,41 @@ export interface Alarm extends BaseData<AlarmId> {
49 propagate: boolean; 98 propagate: boolean;
50 details?: any; 99 details?: any;
51 } 100 }
  101 +
  102 +export interface AlarmInfo extends Alarm {
  103 + originatorName: string;
  104 +}
  105 +
  106 +export class AlarmQuery {
  107 +
  108 + affectedEntityId: EntityId;
  109 + pageLink: TimePageLink;
  110 + searchStatus: AlarmSearchStatus;
  111 + status: AlarmStatus;
  112 + fetchOriginator: boolean;
  113 +
  114 + constructor(entityId: EntityId, pageLink: TimePageLink,
  115 + searchStatus: AlarmSearchStatus, status: AlarmStatus,
  116 + fetchOriginator: boolean) {
  117 + this.affectedEntityId = entityId;
  118 + this.pageLink = pageLink;
  119 + this.searchStatus = searchStatus;
  120 + this.status = status;
  121 + this.fetchOriginator = fetchOriginator;
  122 + }
  123 +
  124 + public toQuery(): string {
  125 + let query = `/${this.affectedEntityId.entityType}/${this.affectedEntityId.id}`;
  126 + query += this.pageLink.toQuery();
  127 + if (this.searchStatus) {
  128 + query += `&searchStatus=${this.searchStatus}`;
  129 + } else if (this.status) {
  130 + query += `&status=${this.status}`;
  131 + }
  132 + if (typeof this.fetchOriginator !== 'undefined' && this.fetchOriginator !== null) {
  133 + query += `&fetchOriginator=${this.fetchOriginator}`;
  134 + }
  135 + return query;
  136 + }
  137 +
  138 +}
@@ -166,6 +166,19 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti @@ -166,6 +166,19 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti
166 } 166 }
167 ], 167 ],
168 [ 168 [
  169 + EntityType.ALARM,
  170 + {
  171 + type: 'entity.type-alarm',
  172 + typePlural: 'entity.type-alarms',
  173 + list: 'entity.list-of-alarms',
  174 + nameStartsWith: 'entity.alarm-name-starts-with',
  175 + details: 'dashboard.dashboard-details',
  176 + noEntities: 'alarm.no-alarms-prompt',
  177 + search: 'alarm.search',
  178 + selectedEntities: 'alarm.selected-alarms'
  179 + }
  180 + ],
  181 + [
169 EntityType.WIDGETS_BUNDLE, 182 EntityType.WIDGETS_BUNDLE,
170 { 183 {
171 details: 'widgets-bundle.widgets-bundle-details', 184 details: 'widgets-bundle.widgets-bundle-details',