Showing
31 changed files
with
1141 additions
and
151 deletions
... | ... | @@ -150,9 +150,11 @@ public class AlarmController extends BaseController { |
150 | 150 | @RequestParam(required = false) String status, |
151 | 151 | @RequestParam int pageSize, |
152 | 152 | @RequestParam int page, |
153 | + @RequestParam(required = false) String textSearch, | |
154 | + @RequestParam(required = false) String sortProperty, | |
155 | + @RequestParam(required = false) String sortOrder, | |
153 | 156 | @RequestParam(required = false) Long startTime, |
154 | 157 | @RequestParam(required = false) Long endTime, |
155 | - @RequestParam(required = false, defaultValue = "false") boolean ascOrder, | |
156 | 158 | @RequestParam(required = false) Boolean fetchOriginator |
157 | 159 | ) throws ThingsboardException { |
158 | 160 | checkParameter("EntityId", strEntityId); |
... | ... | @@ -165,9 +167,8 @@ public class AlarmController extends BaseController { |
165 | 167 | "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); |
166 | 168 | } |
167 | 169 | checkEntityId(entityId, Operation.READ); |
170 | + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); | |
168 | 171 | try { |
169 | - TimePageLink pageLink = createTimePageLink(pageSize, page, "", | |
170 | - "id", ascOrder ? "asc" : "desc", startTime, endTime); | |
171 | 172 | return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); |
172 | 173 | } catch (Exception e) { |
173 | 174 | throw handleException(e); | ... | ... |
... | ... | @@ -29,6 +29,11 @@ public class AlarmInfo extends Alarm { |
29 | 29 | super(alarm); |
30 | 30 | } |
31 | 31 | |
32 | + public AlarmInfo(Alarm alarm, String originatorName) { | |
33 | + super(alarm); | |
34 | + this.originatorName = originatorName; | |
35 | + } | |
36 | + | |
32 | 37 | public String getOriginatorName() { |
33 | 38 | return originatorName; |
34 | 39 | } | ... | ... |
... | ... | @@ -40,5 +40,5 @@ public interface AlarmDao extends Dao<Alarm> { |
40 | 40 | |
41 | 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 | 261 | |
262 | 262 | @Override |
263 | 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 | 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 | 285 | @Override |
... | ... | @@ -293,14 +291,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ |
293 | 291 | AlarmQuery query; |
294 | 292 | while (hasNext && AlarmSeverity.CRITICAL != highestSeverity) { |
295 | 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 | 295 | if (alarms.hasNext()) { |
305 | 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 | 83 | } |
84 | 84 | |
85 | 85 | public AbstractDeviceEntity(DeviceEntity deviceEntity) { |
86 | - this.setId(deviceEntity.getId());; | |
86 | + this.setId(deviceEntity.getId()); | |
87 | 87 | this.tenantId = deviceEntity.getTenantId(); |
88 | 88 | this.customerId = deviceEntity.getCustomerId(); |
89 | 89 | this.type = deviceEntity.getType(); | ... | ... |
... | ... | @@ -15,133 +15,34 @@ |
15 | 15 | */ |
16 | 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 | 18 | import lombok.Data; |
21 | 19 | import lombok.EqualsAndHashCode; |
22 | -import org.hibernate.annotations.Type; | |
23 | 20 | import org.hibernate.annotations.TypeDef; |
24 | -import org.thingsboard.server.common.data.EntityType; | |
25 | -import org.thingsboard.server.common.data.UUIDConverter; | |
26 | 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 | 22 | import org.thingsboard.server.dao.util.mapping.JsonStringType; |
36 | 23 | |
37 | -import javax.persistence.Column; | |
38 | 24 | import javax.persistence.Entity; |
39 | -import javax.persistence.EnumType; | |
40 | -import javax.persistence.Enumerated; | |
41 | 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 | 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 | 29 | @Data |
57 | 30 | @EqualsAndHashCode(callSuper = true) |
58 | 31 | @Entity |
59 | 32 | @TypeDef(name = "json", typeClass = JsonStringType.class) |
60 | 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 | 36 | public AlarmEntity() { |
103 | 37 | super(); |
104 | 38 | } |
105 | 39 | |
106 | 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 | 44 | @Override |
128 | 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 | -} | |
\ No newline at end of file | ||
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 | 15 | */ |
16 | 16 | package org.thingsboard.server.dao.sql.alarm; |
17 | 17 | |
18 | +import org.springframework.data.domain.Page; | |
18 | 19 | import org.springframework.data.domain.Pageable; |
19 | 20 | import org.springframework.data.jpa.repository.Query; |
20 | 21 | import org.springframework.data.repository.CrudRepository; |
21 | 22 | import org.springframework.data.repository.query.Param; |
22 | 23 | import org.thingsboard.server.common.data.EntityType; |
23 | 24 | import org.thingsboard.server.dao.model.sql.AlarmEntity; |
25 | +import org.thingsboard.server.dao.model.sql.AlarmInfoEntity; | |
24 | 26 | import org.thingsboard.server.dao.util.SqlDao; |
25 | 27 | |
26 | 28 | import java.util.List; |
... | ... | @@ -38,4 +40,26 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, String> { |
38 | 40 | @Param("entityType") EntityType entityType, |
39 | 41 | @Param("alarmType") String alarmType, |
40 | 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 | 43 | |
44 | 44 | import java.util.ArrayList; |
45 | 45 | import java.util.List; |
46 | +import java.util.Objects; | |
46 | 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 | 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 | 98 | } |
94 | 99 | |
95 | 100 | @Override |
96 | - public ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query) { | |
101 | + public PageData<AlarmInfo> findAlarms(TenantId tenantId, AlarmQuery query) { | |
97 | 102 | log.trace("Try to find alarms by entity [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getStatus(), query.getPageLink()); |
98 | 103 | EntityId affectedEntity = query.getAffectedEntityId(); |
99 | 104 | String searchStatusName; |
... | ... | @@ -105,17 +110,18 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A |
105 | 110 | searchStatusName = query.getStatus().name(); |
106 | 111 | } |
107 | 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 | 28 | import { EventTableComponent } from '@home/components/event/event-table.component'; |
29 | 29 | import { RelationTableComponent } from '@home/components/relation/relation-table.component'; |
30 | 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 | 35 | @NgModule({ |
33 | 36 | entryComponents: [ |
34 | 37 | AddEntityDialogComponent, |
35 | 38 | AuditLogDetailsDialogComponent, |
36 | 39 | EventTableHeaderComponent, |
37 | - RelationDialogComponent | |
40 | + RelationDialogComponent, | |
41 | + AlarmTableHeaderComponent, | |
42 | + AlarmDetailsDialogComponent | |
38 | 43 | ], |
39 | 44 | declarations: |
40 | 45 | [ |
... | ... | @@ -48,7 +53,10 @@ import { RelationDialogComponent } from './relation/relation-dialog.component'; |
48 | 53 | EventTableHeaderComponent, |
49 | 54 | EventTableComponent, |
50 | 55 | RelationTableComponent, |
51 | - RelationDialogComponent | |
56 | + RelationDialogComponent, | |
57 | + AlarmTableHeaderComponent, | |
58 | + AlarmTableComponent, | |
59 | + AlarmDetailsDialogComponent | |
52 | 60 | ], |
53 | 61 | imports: [ |
54 | 62 | CommonModule, |
... | ... | @@ -62,7 +70,9 @@ import { RelationDialogComponent } from './relation/relation-dialog.component'; |
62 | 70 | ContactComponent, |
63 | 71 | AuditLogTableComponent, |
64 | 72 | EventTableComponent, |
65 | - RelationTableComponent | |
73 | + RelationTableComponent, | |
74 | + AlarmTableComponent, | |
75 | + AlarmDetailsDialogComponent | |
66 | 76 | ] |
67 | 77 | }) |
68 | 78 | export class HomeComponentsModule { } | ... | ... |
... | ... | @@ -16,6 +16,10 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 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 | 23 | label="{{ 'asset.events' | translate }}" #eventsTab="matTab"> |
20 | 24 | <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id" |
21 | 25 | [entityId]="entity.id"></tb-event-table> | ... | ... |
... | ... | @@ -16,6 +16,10 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 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 | 23 | label="{{ 'customer.events' | translate }}" #eventsTab="matTab"> |
20 | 24 | <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id" |
21 | 25 | [entityId]="entity.id"></tb-event-table> | ... | ... |
... | ... | @@ -62,7 +62,7 @@ |
62 | 62 | </div> |
63 | 63 | <div class="mat-padding" fxLayout="column"> |
64 | 64 | <mat-form-field class="mat-block" |
65 | - [fxShow]="!isEdit && assignedCustomersText | |
65 | + [fxShow]="!isEdit && assignedCustomersText?.length | |
66 | 66 | && dashboardScope === 'tenant'"> |
67 | 67 | <mat-label translate>dashboard.assignedToCustomers</mat-label> |
68 | 68 | <input matInput disabled [ngModel]="assignedCustomersText"> | ... | ... |
... | ... | @@ -16,6 +16,10 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 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 | 23 | label="{{ 'device.events' | translate }}" #eventsTab="matTab"> |
20 | 24 | <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id" |
21 | 25 | [entityId]="entity.id"></tb-event-table> | ... | ... |
... | ... | @@ -16,6 +16,10 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 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 | 23 | label="{{ 'entity-view.events' | translate }}" #eventsTab="matTab"> |
20 | 24 | <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id" |
21 | 25 | [entityId]="entity.id"></tb-event-table> | ... | ... |
... | ... | @@ -16,6 +16,10 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 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 | 23 | label="{{ 'rulechain.events' | translate }}" #eventsTab="matTab"> |
20 | 24 | <tb-event-table [active]="eventsTab.isActive" |
21 | 25 | [debugEventTypes]="[debugEventTypes.DEBUG_RULE_CHAIN]" | ... | ... |
... | ... | @@ -16,6 +16,10 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 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 | 23 | label="{{ 'tenant.events' | translate }}" #eventsTab="matTab"> |
20 | 24 | <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="nullUid" |
21 | 25 | [entityId]="entity.id"></tb-event-table> | ... | ... |
... | ... | @@ -42,7 +42,6 @@ export abstract class DialogComponent<T, R = any> extends PageComponent implemen |
42 | 42 | } |
43 | 43 | |
44 | 44 | ngOnDestroy(): void { |
45 | - console.log('Dialog destroy called'); | |
46 | 45 | super.ngOnDestroy(); |
47 | 46 | if (this.routerSubscription) { |
48 | 47 | this.routerSubscription.unsubscribe(); | ... | ... |
... | ... | @@ -20,6 +20,8 @@ import {TenantId} from '@shared/models/id/tenant-id'; |
20 | 20 | import {CustomerId} from '@shared/models/id/customer-id'; |
21 | 21 | import {AlarmId} from '@shared/models/id/alarm-id'; |
22 | 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 | 26 | export enum AlarmSeverity { |
25 | 27 | CRITICAL = 'CRITICAL', |
... | ... | @@ -36,6 +38,53 @@ export enum AlarmStatus { |
36 | 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 | 88 | export interface Alarm extends BaseData<AlarmId> { |
40 | 89 | tenantId: TenantId; |
41 | 90 | type: string; |
... | ... | @@ -49,3 +98,41 @@ export interface Alarm extends BaseData<AlarmId> { |
49 | 98 | propagate: boolean; |
50 | 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 | 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 | 182 | EntityType.WIDGETS_BUNDLE, |
170 | 183 | { |
171 | 184 | details: 'widgets-bundle.widgets-bundle-details', | ... | ... |