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 | } |
ui-ngx/src/app/core/http/alarm.service.ts
0 → 100644
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', |