Commit 44c28b5b0f64ff95e39783087775a5a2745bb9a2
Merge remote-tracking branch 'upstream/master' into dao-refactoring-vs
Showing
67 changed files
with
2757 additions
and
752 deletions
Too many changes to show.
To preserve performance only 67 of 120 files are displayed.
@@ -22,10 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize; | @@ -22,10 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize; | ||
22 | import org.springframework.web.bind.annotation.*; | 22 | import org.springframework.web.bind.annotation.*; |
23 | import org.thingsboard.server.common.data.Customer; | 23 | import org.thingsboard.server.common.data.Customer; |
24 | import org.thingsboard.server.common.data.Event; | 24 | import org.thingsboard.server.common.data.Event; |
25 | -import org.thingsboard.server.common.data.alarm.Alarm; | ||
26 | -import org.thingsboard.server.common.data.alarm.AlarmId; | ||
27 | -import org.thingsboard.server.common.data.alarm.AlarmQuery; | ||
28 | -import org.thingsboard.server.common.data.alarm.AlarmStatus; | 25 | +import org.thingsboard.server.common.data.alarm.*; |
29 | import org.thingsboard.server.common.data.asset.Asset; | 26 | import org.thingsboard.server.common.data.asset.Asset; |
30 | import org.thingsboard.server.common.data.id.*; | 27 | import org.thingsboard.server.common.data.id.*; |
31 | import org.thingsboard.server.common.data.page.TextPageData; | 28 | import org.thingsboard.server.common.data.page.TextPageData; |
@@ -61,6 +58,19 @@ public class AlarmController extends BaseController { | @@ -61,6 +58,19 @@ public class AlarmController extends BaseController { | ||
61 | } | 58 | } |
62 | 59 | ||
63 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 60 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
61 | + @RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET) | ||
62 | + @ResponseBody | ||
63 | + public AlarmInfo getAlarmInfoById(@PathVariable("alarmId") String strAlarmId) throws ThingsboardException { | ||
64 | + checkParameter("alarmId", strAlarmId); | ||
65 | + try { | ||
66 | + AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); | ||
67 | + return checkAlarmInfoId(alarmId); | ||
68 | + } catch (Exception e) { | ||
69 | + throw handleException(e); | ||
70 | + } | ||
71 | + } | ||
72 | + | ||
73 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | ||
64 | @RequestMapping(value = "/alarm", method = RequestMethod.POST) | 74 | @RequestMapping(value = "/alarm", method = RequestMethod.POST) |
65 | @ResponseBody | 75 | @ResponseBody |
66 | public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException { | 76 | public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException { |
@@ -103,24 +113,31 @@ public class AlarmController extends BaseController { | @@ -103,24 +113,31 @@ public class AlarmController extends BaseController { | ||
103 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 113 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
104 | @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET) | 114 | @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET) |
105 | @ResponseBody | 115 | @ResponseBody |
106 | - public TimePageData<Alarm> getAlarms( | 116 | + public TimePageData<AlarmInfo> getAlarms( |
107 | @PathVariable("entityType") String strEntityType, | 117 | @PathVariable("entityType") String strEntityType, |
108 | @PathVariable("entityId") String strEntityId, | 118 | @PathVariable("entityId") String strEntityId, |
119 | + @RequestParam(required = false) String searchStatus, | ||
109 | @RequestParam(required = false) String status, | 120 | @RequestParam(required = false) String status, |
110 | @RequestParam int limit, | 121 | @RequestParam int limit, |
111 | @RequestParam(required = false) Long startTime, | 122 | @RequestParam(required = false) Long startTime, |
112 | @RequestParam(required = false) Long endTime, | 123 | @RequestParam(required = false) Long endTime, |
113 | @RequestParam(required = false, defaultValue = "false") boolean ascOrder, | 124 | @RequestParam(required = false, defaultValue = "false") boolean ascOrder, |
114 | - @RequestParam(required = false) String offset | 125 | + @RequestParam(required = false) String offset, |
126 | + @RequestParam(required = false) Boolean fetchOriginator | ||
115 | ) throws ThingsboardException { | 127 | ) throws ThingsboardException { |
116 | checkParameter("EntityId", strEntityId); | 128 | checkParameter("EntityId", strEntityId); |
117 | checkParameter("EntityType", strEntityType); | 129 | checkParameter("EntityType", strEntityType); |
118 | EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); | 130 | EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); |
131 | + AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus); | ||
119 | AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status); | 132 | AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status); |
133 | + if (alarmSearchStatus != null && alarmStatus != null) { | ||
134 | + throw new ThingsboardException("Invalid alarms search query: Both parameters 'searchStatus' " + | ||
135 | + "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); | ||
136 | + } | ||
120 | checkEntityId(entityId); | 137 | checkEntityId(entityId); |
121 | try { | 138 | try { |
122 | TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); | 139 | TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); |
123 | - return checkNotNull(alarmService.findAlarms(new AlarmQuery(entityId, pageLink, alarmStatus)).get()); | 140 | + return checkNotNull(alarmService.findAlarms(new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); |
124 | } catch (Exception e) { | 141 | } catch (Exception e) { |
125 | throw handleException(e); | 142 | throw handleException(e); |
126 | } | 143 | } |
@@ -25,6 +25,7 @@ import org.thingsboard.server.actors.service.ActorService; | @@ -25,6 +25,7 @@ import org.thingsboard.server.actors.service.ActorService; | ||
25 | import org.thingsboard.server.common.data.*; | 25 | import org.thingsboard.server.common.data.*; |
26 | import org.thingsboard.server.common.data.alarm.Alarm; | 26 | import org.thingsboard.server.common.data.alarm.Alarm; |
27 | import org.thingsboard.server.common.data.alarm.AlarmId; | 27 | import org.thingsboard.server.common.data.alarm.AlarmId; |
28 | +import org.thingsboard.server.common.data.alarm.AlarmInfo; | ||
28 | import org.thingsboard.server.common.data.asset.Asset; | 29 | import org.thingsboard.server.common.data.asset.Asset; |
29 | import org.thingsboard.server.common.data.id.*; | 30 | import org.thingsboard.server.common.data.id.*; |
30 | import org.thingsboard.server.common.data.page.TextPageLink; | 31 | import org.thingsboard.server.common.data.page.TextPageLink; |
@@ -349,6 +350,17 @@ public abstract class BaseController { | @@ -349,6 +350,17 @@ public abstract class BaseController { | ||
349 | } | 350 | } |
350 | } | 351 | } |
351 | 352 | ||
353 | + AlarmInfo checkAlarmInfoId(AlarmId alarmId) throws ThingsboardException { | ||
354 | + try { | ||
355 | + validateId(alarmId, "Incorrect alarmId " + alarmId); | ||
356 | + AlarmInfo alarmInfo = alarmService.findAlarmInfoByIdAsync(alarmId).get(); | ||
357 | + checkAlarm(alarmInfo); | ||
358 | + return alarmInfo; | ||
359 | + } catch (Exception e) { | ||
360 | + throw handleException(e, false); | ||
361 | + } | ||
362 | + } | ||
363 | + | ||
352 | protected void checkAlarm(Alarm alarm) throws ThingsboardException { | 364 | protected void checkAlarm(Alarm alarm) throws ThingsboardException { |
353 | checkNotNull(alarm); | 365 | checkNotNull(alarm); |
354 | checkTenantId(alarm.getTenantId()); | 366 | checkTenantId(alarm.getTenantId()); |
@@ -250,6 +250,21 @@ public class EntityRelationController extends BaseController { | @@ -250,6 +250,21 @@ public class EntityRelationController extends BaseController { | ||
250 | } | 250 | } |
251 | } | 251 | } |
252 | 252 | ||
253 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | ||
254 | + @RequestMapping(value = "/relations/info", method = RequestMethod.POST) | ||
255 | + @ResponseBody | ||
256 | + public List<EntityRelationInfo> findInfoByQuery(@RequestBody EntityRelationsQuery query) throws ThingsboardException { | ||
257 | + checkNotNull(query); | ||
258 | + checkNotNull(query.getParameters()); | ||
259 | + checkNotNull(query.getFilters()); | ||
260 | + checkEntityId(query.getParameters().getEntityId()); | ||
261 | + try { | ||
262 | + return checkNotNull(relationService.findInfoByQuery(query).get()); | ||
263 | + } catch (Exception e) { | ||
264 | + throw handleException(e); | ||
265 | + } | ||
266 | + } | ||
267 | + | ||
253 | private RelationTypeGroup parseRelationTypeGroup(String strRelationTypeGroup, RelationTypeGroup defaultValue) { | 268 | private RelationTypeGroup parseRelationTypeGroup(String strRelationTypeGroup, RelationTypeGroup defaultValue) { |
254 | RelationTypeGroup result = defaultValue; | 269 | RelationTypeGroup result = defaultValue; |
255 | if (strRelationTypeGroup != null && strRelationTypeGroup.trim().length()>0) { | 270 | if (strRelationTypeGroup != null && strRelationTypeGroup.trim().length()>0) { |
@@ -53,6 +53,22 @@ public class Alarm extends BaseData<AlarmId> implements HasName { | @@ -53,6 +53,22 @@ public class Alarm extends BaseData<AlarmId> implements HasName { | ||
53 | super(id); | 53 | super(id); |
54 | } | 54 | } |
55 | 55 | ||
56 | + public Alarm(Alarm alarm) { | ||
57 | + super(alarm.getId()); | ||
58 | + this.createdTime = alarm.getCreatedTime(); | ||
59 | + this.tenantId = alarm.getTenantId(); | ||
60 | + this.type = alarm.getType(); | ||
61 | + this.originator = alarm.getOriginator(); | ||
62 | + this.severity = alarm.getSeverity(); | ||
63 | + this.status = alarm.getStatus(); | ||
64 | + this.startTs = alarm.getStartTs(); | ||
65 | + this.endTs = alarm.getEndTs(); | ||
66 | + this.ackTs = alarm.getAckTs(); | ||
67 | + this.clearTs = alarm.getClearTs(); | ||
68 | + this.details = alarm.getDetails(); | ||
69 | + this.propagate = alarm.isPropagate(); | ||
70 | + } | ||
71 | + | ||
56 | @Override | 72 | @Override |
57 | @JsonProperty(access = JsonProperty.Access.READ_ONLY) | 73 | @JsonProperty(access = JsonProperty.Access.READ_ONLY) |
58 | public String getName() { | 74 | public String getName() { |
1 | +/** | ||
2 | + * Copyright © 2016-2017 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.common.data.alarm; | ||
17 | + | ||
18 | +public class AlarmInfo extends Alarm { | ||
19 | + | ||
20 | + private static final long serialVersionUID = 2807343093519543363L; | ||
21 | + | ||
22 | + private String originatorName; | ||
23 | + | ||
24 | + public AlarmInfo() { | ||
25 | + super(); | ||
26 | + } | ||
27 | + | ||
28 | + public AlarmInfo(Alarm alarm) { | ||
29 | + super(alarm); | ||
30 | + } | ||
31 | + | ||
32 | + public String getOriginatorName() { | ||
33 | + return originatorName; | ||
34 | + } | ||
35 | + | ||
36 | + public void setOriginatorName(String originatorName) { | ||
37 | + this.originatorName = originatorName; | ||
38 | + } | ||
39 | + | ||
40 | + @Override | ||
41 | + public boolean equals(Object o) { | ||
42 | + if (this == o) return true; | ||
43 | + if (o == null || getClass() != o.getClass()) return false; | ||
44 | + if (!super.equals(o)) return false; | ||
45 | + | ||
46 | + AlarmInfo alarmInfo = (AlarmInfo) o; | ||
47 | + | ||
48 | + return originatorName != null ? originatorName.equals(alarmInfo.originatorName) : alarmInfo.originatorName == null; | ||
49 | + | ||
50 | + } | ||
51 | + | ||
52 | + @Override | ||
53 | + public int hashCode() { | ||
54 | + int result = super.hashCode(); | ||
55 | + result = 31 * result + (originatorName != null ? originatorName.hashCode() : 0); | ||
56 | + return result; | ||
57 | + } | ||
58 | +} |
@@ -32,6 +32,8 @@ public class AlarmQuery { | @@ -32,6 +32,8 @@ public class AlarmQuery { | ||
32 | 32 | ||
33 | private EntityId affectedEntityId; | 33 | private EntityId affectedEntityId; |
34 | private TimePageLink pageLink; | 34 | private TimePageLink pageLink; |
35 | + private AlarmSearchStatus searchStatus; | ||
35 | private AlarmStatus status; | 36 | private AlarmStatus status; |
37 | + private Boolean fetchOriginator; | ||
36 | 38 | ||
37 | } | 39 | } |
common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSearchStatus.java
renamed from
ui/src/app/entity/aliases-entity-select-panel.controller.js
1 | -/* | 1 | +/** |
2 | * Copyright © 2016-2017 The Thingsboard Authors | 2 | * Copyright © 2016-2017 The Thingsboard Authors |
3 | * | 3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
@@ -14,18 +14,10 @@ | @@ -14,18 +14,10 @@ | ||
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | 16 | ||
17 | -/*@ngInject*/ | ||
18 | -export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, types, entityAliases, entityAliasesInfo, onEntityAliasesUpdate) { | 17 | +package org.thingsboard.server.common.data.alarm; |
19 | 18 | ||
20 | - var vm = this; | ||
21 | - vm._mdPanelRef = mdPanelRef; | ||
22 | - vm.entityAliases = entityAliases; | ||
23 | - vm.entityAliasesInfo = entityAliasesInfo; | ||
24 | - vm.onEntityAliasesUpdate = onEntityAliasesUpdate; | 19 | +public enum AlarmSearchStatus { |
20 | + | ||
21 | + ANY, ACTIVE, CLEARED, ACK, UNACK | ||
25 | 22 | ||
26 | - $scope.$watch('vm.entityAliases', function () { | ||
27 | - if (onEntityAliasesUpdate) { | ||
28 | - onEntityAliasesUpdate(vm.entityAliases); | ||
29 | - } | ||
30 | - }, true); | ||
31 | } | 23 | } |
@@ -30,4 +30,13 @@ public enum AlarmStatus { | @@ -30,4 +30,13 @@ public enum AlarmStatus { | ||
30 | return this == CLEARED_ACK || this == CLEARED_UNACK; | 30 | return this == CLEARED_ACK || this == CLEARED_UNACK; |
31 | } | 31 | } |
32 | 32 | ||
33 | + public AlarmSearchStatus getClearSearchStatus() { | ||
34 | + return this.isCleared() ? AlarmSearchStatus.CLEARED : AlarmSearchStatus.ACTIVE; | ||
35 | + } | ||
36 | + | ||
37 | + public AlarmSearchStatus getAckSearchStatus() { | ||
38 | + return this.isAck() ? AlarmSearchStatus.ACK : AlarmSearchStatus.UNACK; | ||
39 | + } | ||
40 | + | ||
41 | + | ||
33 | } | 42 | } |
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm; | @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm; | ||
17 | 17 | ||
18 | import com.google.common.util.concurrent.ListenableFuture; | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | import org.thingsboard.server.common.data.alarm.Alarm; | 19 | import org.thingsboard.server.common.data.alarm.Alarm; |
20 | +import org.thingsboard.server.common.data.alarm.AlarmInfo; | ||
20 | import org.thingsboard.server.common.data.alarm.AlarmQuery; | 21 | import org.thingsboard.server.common.data.alarm.AlarmQuery; |
21 | import org.thingsboard.server.common.data.id.EntityId; | 22 | import org.thingsboard.server.common.data.id.EntityId; |
22 | import org.thingsboard.server.common.data.id.TenantId; | 23 | import org.thingsboard.server.common.data.id.TenantId; |
@@ -36,5 +37,5 @@ public interface AlarmDao extends Dao<Alarm> { | @@ -36,5 +37,5 @@ public interface AlarmDao extends Dao<Alarm> { | ||
36 | 37 | ||
37 | Alarm save(Alarm alarm); | 38 | Alarm save(Alarm alarm); |
38 | 39 | ||
39 | - ListenableFuture<List<Alarm>> findAlarms(AlarmQuery query); | 40 | + ListenableFuture<List<AlarmInfo>> findAlarms(AlarmQuery query); |
40 | } | 41 | } |
@@ -18,6 +18,7 @@ package org.thingsboard.server.dao.alarm; | @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.alarm; | ||
18 | import com.google.common.util.concurrent.ListenableFuture; | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | import org.thingsboard.server.common.data.alarm.Alarm; | 19 | import org.thingsboard.server.common.data.alarm.Alarm; |
20 | import org.thingsboard.server.common.data.alarm.AlarmId; | 20 | import org.thingsboard.server.common.data.alarm.AlarmId; |
21 | +import org.thingsboard.server.common.data.alarm.AlarmInfo; | ||
21 | import org.thingsboard.server.common.data.alarm.AlarmQuery; | 22 | import org.thingsboard.server.common.data.alarm.AlarmQuery; |
22 | import org.thingsboard.server.common.data.page.TimePageData; | 23 | import org.thingsboard.server.common.data.page.TimePageData; |
23 | 24 | ||
@@ -34,6 +35,8 @@ public interface AlarmService { | @@ -34,6 +35,8 @@ public interface AlarmService { | ||
34 | 35 | ||
35 | ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId); | 36 | ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId); |
36 | 37 | ||
37 | - ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query); | 38 | + ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId); |
39 | + | ||
40 | + ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query); | ||
38 | 41 | ||
39 | } | 42 | } |
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm; | @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm; | ||
17 | 17 | ||
18 | 18 | ||
19 | import com.google.common.base.Function; | 19 | import com.google.common.base.Function; |
20 | +import com.google.common.util.concurrent.AsyncFunction; | ||
20 | import com.google.common.util.concurrent.Futures; | 21 | import com.google.common.util.concurrent.Futures; |
21 | import com.google.common.util.concurrent.ListenableFuture; | 22 | import com.google.common.util.concurrent.ListenableFuture; |
22 | import lombok.extern.slf4j.Slf4j; | 23 | import lombok.extern.slf4j.Slf4j; |
@@ -24,15 +25,13 @@ import org.springframework.beans.factory.annotation.Autowired; | @@ -24,15 +25,13 @@ import org.springframework.beans.factory.annotation.Autowired; | ||
24 | import org.springframework.stereotype.Service; | 25 | import org.springframework.stereotype.Service; |
25 | import org.springframework.util.StringUtils; | 26 | import org.springframework.util.StringUtils; |
26 | import org.thingsboard.server.common.data.Tenant; | 27 | import org.thingsboard.server.common.data.Tenant; |
27 | -import org.thingsboard.server.common.data.alarm.Alarm; | ||
28 | -import org.thingsboard.server.common.data.alarm.AlarmId; | ||
29 | -import org.thingsboard.server.common.data.alarm.AlarmQuery; | ||
30 | -import org.thingsboard.server.common.data.alarm.AlarmStatus; | 28 | +import org.thingsboard.server.common.data.alarm.*; |
31 | import org.thingsboard.server.common.data.id.EntityId; | 29 | import org.thingsboard.server.common.data.id.EntityId; |
32 | import org.thingsboard.server.common.data.page.TimePageData; | 30 | import org.thingsboard.server.common.data.page.TimePageData; |
33 | import org.thingsboard.server.common.data.relation.EntityRelation; | 31 | import org.thingsboard.server.common.data.relation.EntityRelation; |
34 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; | 32 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
35 | import org.thingsboard.server.dao.entity.AbstractEntityService; | 33 | import org.thingsboard.server.dao.entity.AbstractEntityService; |
34 | +import org.thingsboard.server.dao.entity.EntityService; | ||
36 | import org.thingsboard.server.dao.exception.DataValidationException; | 35 | import org.thingsboard.server.dao.exception.DataValidationException; |
37 | import org.thingsboard.server.dao.relation.EntityRelationsQuery; | 36 | import org.thingsboard.server.dao.relation.EntityRelationsQuery; |
38 | import org.thingsboard.server.dao.relation.EntitySearchDirection; | 37 | import org.thingsboard.server.dao.relation.EntitySearchDirection; |
@@ -44,6 +43,7 @@ import org.thingsboard.server.dao.tenant.TenantDao; | @@ -44,6 +43,7 @@ import org.thingsboard.server.dao.tenant.TenantDao; | ||
44 | import javax.annotation.Nullable; | 43 | import javax.annotation.Nullable; |
45 | import javax.annotation.PostConstruct; | 44 | import javax.annotation.PostConstruct; |
46 | import javax.annotation.PreDestroy; | 45 | import javax.annotation.PreDestroy; |
46 | +import java.util.ArrayList; | ||
47 | import java.util.List; | 47 | import java.util.List; |
48 | import java.util.concurrent.ExecutionException; | 48 | import java.util.concurrent.ExecutionException; |
49 | import java.util.concurrent.ExecutorService; | 49 | import java.util.concurrent.ExecutorService; |
@@ -57,7 +57,6 @@ import static org.thingsboard.server.dao.service.Validator.validateId; | @@ -57,7 +57,6 @@ import static org.thingsboard.server.dao.service.Validator.validateId; | ||
57 | public class BaseAlarmService extends AbstractEntityService implements AlarmService { | 57 | public class BaseAlarmService extends AbstractEntityService implements AlarmService { |
58 | 58 | ||
59 | public static final String ALARM_RELATION_PREFIX = "ALARM_"; | 59 | public static final String ALARM_RELATION_PREFIX = "ALARM_"; |
60 | - public static final String ALARM_RELATION = "ALARM_ANY"; | ||
61 | 60 | ||
62 | @Autowired | 61 | @Autowired |
63 | private AlarmDao alarmDao; | 62 | private AlarmDao alarmDao; |
@@ -68,6 +67,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ | @@ -68,6 +67,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ | ||
68 | @Autowired | 67 | @Autowired |
69 | private RelationService relationService; | 68 | private RelationService relationService; |
70 | 69 | ||
70 | + @Autowired | ||
71 | + private EntityService entityService; | ||
72 | + | ||
71 | protected ExecutorService readResultsProcessingExecutor; | 73 | protected ExecutorService readResultsProcessingExecutor; |
72 | 74 | ||
73 | @PostConstruct | 75 | @PostConstruct |
@@ -114,11 +116,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ | @@ -114,11 +116,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ | ||
114 | query.setParameters(new RelationsSearchParameters(saved.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE)); | 116 | query.setParameters(new RelationsSearchParameters(saved.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE)); |
115 | List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList()); | 117 | List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList()); |
116 | for (EntityId parentId : parentEntities) { | 118 | for (EntityId parentId : parentEntities) { |
117 | - createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION, RelationTypeGroup.ALARM)); | ||
118 | - createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name(), RelationTypeGroup.ALARM)); | 119 | + createAlarmRelation(parentId, saved.getId(), saved.getStatus(), true); |
119 | } | 120 | } |
120 | - createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION, RelationTypeGroup.ALARM)); | ||
121 | - createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name(), RelationTypeGroup.ALARM)); | 121 | + createAlarmRelation(alarm.getOriginator(), saved.getId(), saved.getStatus(), true); |
122 | return saved; | 122 | return saved; |
123 | } | 123 | } |
124 | 124 | ||
@@ -197,12 +197,44 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ | @@ -197,12 +197,44 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ | ||
197 | } | 197 | } |
198 | 198 | ||
199 | @Override | 199 | @Override |
200 | - public ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query) { | ||
201 | - ListenableFuture<List<Alarm>> alarms = alarmDao.findAlarms(query); | ||
202 | - return Futures.transform(alarms, new Function<List<Alarm>, TimePageData<Alarm>>() { | 200 | + public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId) { |
201 | + log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId); | ||
202 | + validateId(alarmId, "Incorrect alarmId " + alarmId); | ||
203 | + return Futures.transform(alarmDao.findAlarmByIdAsync(alarmId.getId()), | ||
204 | + (AsyncFunction<Alarm, AlarmInfo>) alarm1 -> { | ||
205 | + AlarmInfo alarmInfo = new AlarmInfo(alarm1); | ||
206 | + return Futures.transform( | ||
207 | + entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>) | ||
208 | + originatorName -> { | ||
209 | + alarmInfo.setOriginatorName(originatorName); | ||
210 | + return alarmInfo; | ||
211 | + } | ||
212 | + ); | ||
213 | + }); | ||
214 | + } | ||
215 | + | ||
216 | + @Override | ||
217 | + public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) { | ||
218 | + ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query); | ||
219 | + if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) { | ||
220 | + alarms = Futures.transform(alarms, (AsyncFunction<List<AlarmInfo>, List<AlarmInfo>>) input -> { | ||
221 | + List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size()); | ||
222 | + for (AlarmInfo alarmInfo : input) { | ||
223 | + alarmFutures.add(Futures.transform( | ||
224 | + entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>) | ||
225 | + originatorName -> { | ||
226 | + alarmInfo.setOriginatorName(originatorName); | ||
227 | + return alarmInfo; | ||
228 | + } | ||
229 | + )); | ||
230 | + } | ||
231 | + return Futures.successfulAsList(alarmFutures); | ||
232 | + }); | ||
233 | + } | ||
234 | + return Futures.transform(alarms, new Function<List<AlarmInfo>, TimePageData<AlarmInfo>>() { | ||
203 | @Nullable | 235 | @Nullable |
204 | @Override | 236 | @Override |
205 | - public TimePageData<Alarm> apply(@Nullable List<Alarm> alarms) { | 237 | + public TimePageData<AlarmInfo> apply(@Nullable List<AlarmInfo> alarms) { |
206 | return new TimePageData<>(alarms, query.getPageLink()); | 238 | return new TimePageData<>(alarms, query.getPageLink()); |
207 | } | 239 | } |
208 | }); | 240 | }); |
@@ -243,17 +275,45 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ | @@ -243,17 +275,45 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ | ||
243 | query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE)); | 275 | query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE)); |
244 | List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList()); | 276 | List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList()); |
245 | for (EntityId parentId : parentEntities) { | 277 | for (EntityId parentId : parentEntities) { |
246 | - deleteRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name(), RelationTypeGroup.ALARM)); | ||
247 | - createRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name(), RelationTypeGroup.ALARM)); | ||
248 | - } | ||
249 | - deleteRelation(new EntityRelation(alarm.getOriginator(), alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name(), RelationTypeGroup.ALARM)); | ||
250 | - createRelation(new EntityRelation(alarm.getOriginator(), alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name(), RelationTypeGroup.ALARM)); | 278 | + updateAlarmRelation(parentId, alarm.getId(), oldStatus, newStatus); |
279 | + } | ||
280 | + updateAlarmRelation(alarm.getOriginator(), alarm.getId(), oldStatus, newStatus); | ||
251 | } catch (ExecutionException | InterruptedException e) { | 281 | } catch (ExecutionException | InterruptedException e) { |
252 | log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus); | 282 | log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus); |
253 | throw new RuntimeException(e); | 283 | throw new RuntimeException(e); |
254 | } | 284 | } |
255 | } | 285 | } |
256 | 286 | ||
287 | + private void createAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) { | ||
288 | + try { | ||
289 | + if (createAnyRelation) { | ||
290 | + createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + AlarmSearchStatus.ANY.name(), RelationTypeGroup.ALARM)); | ||
291 | + } | ||
292 | + createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM)); | ||
293 | + createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM)); | ||
294 | + createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM)); | ||
295 | + } catch (ExecutionException | InterruptedException e) { | ||
296 | + log.warn("[{}] Failed to create relation. Status: [{}]", alarmId, status); | ||
297 | + throw new RuntimeException(e); | ||
298 | + } | ||
299 | + } | ||
300 | + | ||
301 | + private void deleteAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus status) { | ||
302 | + try { | ||
303 | + deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM)); | ||
304 | + deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM)); | ||
305 | + deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM)); | ||
306 | + } catch (ExecutionException | InterruptedException e) { | ||
307 | + log.warn("[{}] Failed to delete relation. Status: [{}]", alarmId, status); | ||
308 | + throw new RuntimeException(e); | ||
309 | + } | ||
310 | + } | ||
311 | + | ||
312 | + private void updateAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) { | ||
313 | + deleteAlarmRelation(entityId, alarmId, oldStatus); | ||
314 | + createAlarmRelation(entityId, alarmId, newStatus, false); | ||
315 | + } | ||
316 | + | ||
257 | private <T> ListenableFuture<T> getAndUpdate(AlarmId alarmId, Function<Alarm, T> function) { | 317 | private <T> ListenableFuture<T> getAndUpdate(AlarmId alarmId, Function<Alarm, T> function) { |
258 | validateId(alarmId, "Alarm id should be specified!"); | 318 | validateId(alarmId, "Alarm id should be specified!"); |
259 | ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(alarmId.getId()); | 319 | ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(alarmId.getId()); |
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm; | @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm; | ||
17 | 17 | ||
18 | import com.datastax.driver.core.querybuilder.QueryBuilder; | 18 | import com.datastax.driver.core.querybuilder.QueryBuilder; |
19 | import com.datastax.driver.core.querybuilder.Select; | 19 | import com.datastax.driver.core.querybuilder.Select; |
20 | +import com.google.common.base.Function; | ||
20 | import com.google.common.util.concurrent.AsyncFunction; | 21 | import com.google.common.util.concurrent.AsyncFunction; |
21 | import com.google.common.util.concurrent.Futures; | 22 | import com.google.common.util.concurrent.Futures; |
22 | import com.google.common.util.concurrent.ListenableFuture; | 23 | import com.google.common.util.concurrent.ListenableFuture; |
@@ -26,7 +27,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | @@ -26,7 +27,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
26 | import org.springframework.stereotype.Component; | 27 | import org.springframework.stereotype.Component; |
27 | import org.thingsboard.server.common.data.EntityType; | 28 | import org.thingsboard.server.common.data.EntityType; |
28 | import org.thingsboard.server.common.data.alarm.Alarm; | 29 | import org.thingsboard.server.common.data.alarm.Alarm; |
30 | +import org.thingsboard.server.common.data.alarm.AlarmInfo; | ||
29 | import org.thingsboard.server.common.data.alarm.AlarmQuery; | 31 | import org.thingsboard.server.common.data.alarm.AlarmQuery; |
32 | +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; | ||
30 | import org.thingsboard.server.common.data.id.EntityId; | 33 | import org.thingsboard.server.common.data.id.EntityId; |
31 | import org.thingsboard.server.common.data.id.TenantId; | 34 | import org.thingsboard.server.common.data.id.TenantId; |
32 | import org.thingsboard.server.common.data.relation.EntityRelation; | 35 | import org.thingsboard.server.common.data.relation.EntityRelation; |
@@ -86,15 +89,25 @@ public class CassandraAlarmDao extends CassandraAbstractModelDao<AlarmEntity, Al | @@ -86,15 +89,25 @@ public class CassandraAlarmDao extends CassandraAbstractModelDao<AlarmEntity, Al | ||
86 | } | 89 | } |
87 | 90 | ||
88 | @Override | 91 | @Override |
89 | - public ListenableFuture<List<Alarm>> findAlarms(AlarmQuery query) { | ||
90 | - log.trace("Try to find alarms by entity [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getStatus(), query.getPageLink()); | 92 | + public ListenableFuture<List<AlarmInfo>> findAlarms(AlarmQuery query) { |
93 | + log.trace("Try to find alarms by entity [{}], searchStatus [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getSearchStatus(), query.getStatus(), query.getPageLink()); | ||
91 | EntityId affectedEntity = query.getAffectedEntityId(); | 94 | EntityId affectedEntity = query.getAffectedEntityId(); |
92 | - String relationType = query.getStatus() == null ? BaseAlarmService.ALARM_RELATION : BaseAlarmService.ALARM_RELATION_PREFIX + query.getStatus().name(); | 95 | + String searchStatusName; |
96 | + if (query.getSearchStatus() == null && query.getStatus() == null) { | ||
97 | + searchStatusName = AlarmSearchStatus.ANY.name(); | ||
98 | + } else if (query.getSearchStatus() != null) { | ||
99 | + searchStatusName = query.getSearchStatus().name(); | ||
100 | + } else { | ||
101 | + searchStatusName = query.getStatus().name(); | ||
102 | + } | ||
103 | + String relationType = BaseAlarmService.ALARM_RELATION_PREFIX + searchStatusName; | ||
93 | ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink()); | 104 | ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink()); |
94 | - return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<Alarm>>) input -> { | ||
95 | - List<ListenableFuture<Alarm>> alarmFutures = new ArrayList<>(input.size()); | 105 | + return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<AlarmInfo>>) input -> { |
106 | + List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size()); | ||
96 | for (EntityRelation relation : input) { | 107 | for (EntityRelation relation : input) { |
97 | - alarmFutures.add(findAlarmByIdAsync(relation.getTo().getId())); | 108 | + alarmFutures.add(Futures.transform( |
109 | + findAlarmByIdAsync(relation.getTo().getId()), (Function<Alarm, AlarmInfo>) | ||
110 | + alarm1 -> new AlarmInfo(alarm1))); | ||
98 | } | 111 | } |
99 | return Futures.successfulAsList(alarmFutures); | 112 | return Futures.successfulAsList(alarmFutures); |
100 | }); | 113 | }); |
@@ -179,9 +179,14 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati | @@ -179,9 +179,14 @@ public class BaseRelationDao extends CassandraAbstractAsyncDao implements Relati | ||
179 | eq(ModelConstants.RELATION_TYPE_GROUP_PROPERTY, typeGroup.name()), | 179 | eq(ModelConstants.RELATION_TYPE_GROUP_PROPERTY, typeGroup.name()), |
180 | eq(ModelConstants.RELATION_TYPE_PROPERTY, relationType), | 180 | eq(ModelConstants.RELATION_TYPE_PROPERTY, relationType), |
181 | eq(ModelConstants.RELATION_TO_TYPE_PROPERTY, childType.name())), | 181 | eq(ModelConstants.RELATION_TO_TYPE_PROPERTY, childType.name())), |
182 | - Arrays.asList(QueryBuilder.asc(ModelConstants.RELATION_TYPE_GROUP_PROPERTY), | ||
183 | - QueryBuilder.asc(ModelConstants.RELATION_TYPE_PROPERTY), | ||
184 | - QueryBuilder.asc(ModelConstants.RELATION_TO_TYPE_PROPERTY)), | 182 | + Arrays.asList( |
183 | + pageLink.isAscOrder() ? QueryBuilder.desc(ModelConstants.RELATION_TYPE_GROUP_PROPERTY) : | ||
184 | + QueryBuilder.asc(ModelConstants.RELATION_TYPE_GROUP_PROPERTY), | ||
185 | + pageLink.isAscOrder() ? QueryBuilder.desc(ModelConstants.RELATION_TYPE_PROPERTY) : | ||
186 | + QueryBuilder.asc(ModelConstants.RELATION_TYPE_PROPERTY), | ||
187 | + pageLink.isAscOrder() ? QueryBuilder.desc(ModelConstants.RELATION_TO_TYPE_PROPERTY) : | ||
188 | + QueryBuilder.asc(ModelConstants.RELATION_TO_TYPE_PROPERTY) | ||
189 | + ), | ||
185 | pageLink, ModelConstants.RELATION_TO_ID_PROPERTY); | 190 | pageLink, ModelConstants.RELATION_TO_ID_PROPERTY); |
186 | return getFuture(executeAsyncRead(query), rs -> getEntityRelations(rs)); | 191 | return getFuture(executeAsyncRead(query), rs -> getEntityRelations(rs)); |
187 | } | 192 | } |
@@ -191,7 +191,7 @@ public class BaseRelationService implements RelationService { | @@ -191,7 +191,7 @@ public class BaseRelationService implements RelationService { | ||
191 | 191 | ||
192 | @Override | 192 | @Override |
193 | public ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query) { | 193 | public ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query) { |
194 | - log.trace("Executing findByQuery [{}][{}]", query); | 194 | + log.trace("Executing findByQuery [{}]", query); |
195 | RelationsSearchParameters params = query.getParameters(); | 195 | RelationsSearchParameters params = query.getParameters(); |
196 | final List<EntityTypeFilter> filters = query.getFilters(); | 196 | final List<EntityTypeFilter> filters = query.getFilters(); |
197 | if (filters == null || filters.isEmpty()) { | 197 | if (filters == null || filters.isEmpty()) { |
@@ -224,6 +224,30 @@ public class BaseRelationService implements RelationService { | @@ -224,6 +224,30 @@ public class BaseRelationService implements RelationService { | ||
224 | } | 224 | } |
225 | } | 225 | } |
226 | 226 | ||
227 | + @Override | ||
228 | + public ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(EntityRelationsQuery query) { | ||
229 | + log.trace("Executing findInfoByQuery [{}]", query); | ||
230 | + ListenableFuture<List<EntityRelation>> relations = findByQuery(query); | ||
231 | + EntitySearchDirection direction = query.getParameters().getDirection(); | ||
232 | + ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations, | ||
233 | + (AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> { | ||
234 | + List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>(); | ||
235 | + relations1.stream().forEach(relation -> | ||
236 | + futures.add(fetchRelationInfoAsync(relation, | ||
237 | + relation2 -> direction == EntitySearchDirection.FROM ? relation2.getTo() : relation2.getFrom(), | ||
238 | + (EntityRelationInfo relationInfo, String entityName) -> { | ||
239 | + if (direction == EntitySearchDirection.FROM) { | ||
240 | + relationInfo.setToName(entityName); | ||
241 | + } else { | ||
242 | + relationInfo.setFromName(entityName); | ||
243 | + } | ||
244 | + })) | ||
245 | + ); | ||
246 | + return Futures.successfulAsList(futures); | ||
247 | + }); | ||
248 | + return relationsInfo; | ||
249 | + } | ||
250 | + | ||
227 | protected void validate(EntityRelation relation) { | 251 | protected void validate(EntityRelation relation) { |
228 | if (relation == null) { | 252 | if (relation == null) { |
229 | throw new DataValidationException("Relation type should be specified!"); | 253 | throw new DataValidationException("Relation type should be specified!"); |
@@ -52,6 +52,8 @@ public interface RelationService { | @@ -52,6 +52,8 @@ public interface RelationService { | ||
52 | 52 | ||
53 | ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query); | 53 | ListenableFuture<List<EntityRelation>> findByQuery(EntityRelationsQuery query); |
54 | 54 | ||
55 | + ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(EntityRelationsQuery query); | ||
56 | + | ||
55 | // TODO: This method may be useful for some validations in the future | 57 | // TODO: This method may be useful for some validations in the future |
56 | // ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to); | 58 | // ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to); |
57 | 59 |
@@ -22,10 +22,7 @@ import org.junit.Before; | @@ -22,10 +22,7 @@ import org.junit.Before; | ||
22 | import org.junit.Test; | 22 | import org.junit.Test; |
23 | import org.thingsboard.server.common.data.EntityType; | 23 | import org.thingsboard.server.common.data.EntityType; |
24 | import org.thingsboard.server.common.data.Tenant; | 24 | import org.thingsboard.server.common.data.Tenant; |
25 | -import org.thingsboard.server.common.data.alarm.Alarm; | ||
26 | -import org.thingsboard.server.common.data.alarm.AlarmQuery; | ||
27 | -import org.thingsboard.server.common.data.alarm.AlarmSeverity; | ||
28 | -import org.thingsboard.server.common.data.alarm.AlarmStatus; | 25 | +import org.thingsboard.server.common.data.alarm.*; |
29 | import org.thingsboard.server.common.data.id.AssetId; | 26 | import org.thingsboard.server.common.data.id.AssetId; |
30 | import org.thingsboard.server.common.data.id.DeviceId; | 27 | import org.thingsboard.server.common.data.id.DeviceId; |
31 | import org.thingsboard.server.common.data.id.TenantId; | 28 | import org.thingsboard.server.common.data.id.TenantId; |
@@ -117,7 +114,7 @@ public class AlarmServiceTest extends AbstractServiceTest { | @@ -117,7 +114,7 @@ public class AlarmServiceTest extends AbstractServiceTest { | ||
117 | Alarm created = alarmService.createOrUpdateAlarm(alarm); | 114 | Alarm created = alarmService.createOrUpdateAlarm(alarm); |
118 | 115 | ||
119 | // Check child relation | 116 | // Check child relation |
120 | - TimePageData<Alarm> alarms = alarmService.findAlarms(AlarmQuery.builder() | 117 | + TimePageData<AlarmInfo> alarms = alarmService.findAlarms(AlarmQuery.builder() |
121 | .affectedEntityId(childId) | 118 | .affectedEntityId(childId) |
122 | .status(AlarmStatus.ACTIVE_UNACK).pageLink( | 119 | .status(AlarmStatus.ACTIVE_UNACK).pageLink( |
123 | new TimePageLink(1, 0L, System.currentTimeMillis(), false) | 120 | new TimePageLink(1, 0L, System.currentTimeMillis(), false) |
1 | +/* | ||
2 | + * Copyright © 2016-2017 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 | +import 'brace/ext/language_tools'; | ||
17 | +import 'brace/mode/json'; | ||
18 | +import 'brace/theme/github'; | ||
19 | +import beautify from 'js-beautify'; | ||
20 | + | ||
21 | +import './alarm-details-dialog.scss'; | ||
22 | + | ||
23 | +const js_beautify = beautify.js; | ||
24 | + | ||
25 | +/*@ngInject*/ | ||
26 | +export default function AlarmDetailsDialogController($mdDialog, $filter, $translate, types, alarmService, alarmId, showingCallback) { | ||
27 | + | ||
28 | + var vm = this; | ||
29 | + | ||
30 | + vm.alarmId = alarmId; | ||
31 | + vm.types = types; | ||
32 | + vm.alarm = null; | ||
33 | + | ||
34 | + vm.alarmUpdated = false; | ||
35 | + | ||
36 | + showingCallback.onShowing = function(scope, element) { | ||
37 | + updateEditorSize(element); | ||
38 | + } | ||
39 | + | ||
40 | + vm.alarmDetailsOptions = { | ||
41 | + useWrapMode: false, | ||
42 | + mode: 'json', | ||
43 | + showGutter: false, | ||
44 | + showPrintMargin: false, | ||
45 | + theme: 'github', | ||
46 | + advanced: { | ||
47 | + enableSnippets: false, | ||
48 | + enableBasicAutocompletion: false, | ||
49 | + enableLiveAutocompletion: false | ||
50 | + }, | ||
51 | + onLoad: function (_ace) { | ||
52 | + vm.editor = _ace; | ||
53 | + } | ||
54 | + }; | ||
55 | + | ||
56 | + vm.close = close; | ||
57 | + vm.acknowledge = acknowledge; | ||
58 | + vm.clear = clear; | ||
59 | + | ||
60 | + loadAlarm(); | ||
61 | + | ||
62 | + function updateEditorSize(element) { | ||
63 | + var newWidth = 600; | ||
64 | + var newHeight = 200; | ||
65 | + angular.element('#tb-alarm-details', element).height(newHeight.toString() + "px") | ||
66 | + .width(newWidth.toString() + "px"); | ||
67 | + vm.editor.resize(); | ||
68 | + } | ||
69 | + | ||
70 | + function loadAlarm() { | ||
71 | + alarmService.getAlarmInfo(vm.alarmId).then( | ||
72 | + function success(alarm) { | ||
73 | + vm.alarm = alarm; | ||
74 | + loadAlarmFields(); | ||
75 | + }, | ||
76 | + function fail() { | ||
77 | + vm.alarm = null; | ||
78 | + } | ||
79 | + ); | ||
80 | + } | ||
81 | + | ||
82 | + function loadAlarmFields() { | ||
83 | + vm.createdTime = $filter('date')(vm.alarm.createdTime, 'yyyy-MM-dd HH:mm:ss'); | ||
84 | + vm.startTime = null; | ||
85 | + if (vm.alarm.startTs) { | ||
86 | + vm.startTime = $filter('date')(vm.alarm.startTs, 'yyyy-MM-dd HH:mm:ss'); | ||
87 | + } | ||
88 | + vm.endTime = null; | ||
89 | + if (vm.alarm.endTs) { | ||
90 | + vm.endTime = $filter('date')(vm.alarm.endTs, 'yyyy-MM-dd HH:mm:ss'); | ||
91 | + } | ||
92 | + vm.ackTime = null; | ||
93 | + if (vm.alarm.ackTs) { | ||
94 | + vm.ackTime = $filter('date')(vm.alarm.ackTs, 'yyyy-MM-dd HH:mm:ss') | ||
95 | + } | ||
96 | + vm.clearTime = null; | ||
97 | + if (vm.alarm.clearTs) { | ||
98 | + vm.clearTime = $filter('date')(vm.alarm.clearTs, 'yyyy-MM-dd HH:mm:ss'); | ||
99 | + } | ||
100 | + | ||
101 | + vm.alarmSeverity = $translate.instant(types.alarmSeverity[vm.alarm.severity].name); | ||
102 | + | ||
103 | + vm.alarmStatus = $translate.instant('alarm.display-status.' + vm.alarm.status); | ||
104 | + | ||
105 | + vm.alarmDetails = null; | ||
106 | + if (vm.alarm.details) { | ||
107 | + vm.alarmDetails = angular.toJson(vm.alarm.details); | ||
108 | + vm.alarmDetails = js_beautify(vm.alarmDetails, {indent_size: 4}); | ||
109 | + } | ||
110 | + } | ||
111 | + | ||
112 | + function acknowledge () { | ||
113 | + alarmService.ackAlarm(vm.alarmId).then( | ||
114 | + function success() { | ||
115 | + vm.alarmUpdated = true; | ||
116 | + loadAlarm(); | ||
117 | + } | ||
118 | + ); | ||
119 | + } | ||
120 | + | ||
121 | + function clear () { | ||
122 | + alarmService.clearAlarm(vm.alarmId).then( | ||
123 | + function success() { | ||
124 | + vm.alarmUpdated = true; | ||
125 | + loadAlarm(); | ||
126 | + } | ||
127 | + ); | ||
128 | + } | ||
129 | + | ||
130 | + function close () { | ||
131 | + $mdDialog.hide(vm.alarmUpdated ? vm.alarm : null); | ||
132 | + } | ||
133 | + | ||
134 | +} |
ui/src/app/alarm/alarm-details-dialog.scss
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2017 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 | +.tb-alarm-details-panel { | ||
18 | + margin-left: 15px; | ||
19 | + border: 1px solid #C0C0C0; | ||
20 | + height: 100%; | ||
21 | + #tb-alarm-details { | ||
22 | + min-width: 600px; | ||
23 | + min-height: 200px; | ||
24 | + width: 100%; | ||
25 | + height: 100%; | ||
26 | + } | ||
27 | +} |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2017 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 | +<md-dialog aria-label="{{ 'alarm.alarm-details' | translate }}"> | ||
19 | + <md-toolbar> | ||
20 | + <div class="md-toolbar-tools"> | ||
21 | + <h2 translate>alarm.alarm-details</h2> | ||
22 | + <span flex></span> | ||
23 | + <md-button class="md-icon-button" ng-click="vm.close()"> | ||
24 | + <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> | ||
25 | + </md-button> | ||
26 | + </div> | ||
27 | + </md-toolbar> | ||
28 | + <md-dialog-content> | ||
29 | + <div class="md-dialog-content" layout="column"> | ||
30 | + <div layout="row"> | ||
31 | + <md-input-container class="md-block"> | ||
32 | + <label translate>alarm.created-time</label> | ||
33 | + <input ng-model="vm.createdTime" readonly> | ||
34 | + </md-input-container> | ||
35 | + <md-input-container flex class="md-block"> | ||
36 | + <label translate>alarm.originator</label> | ||
37 | + <input ng-model="vm.alarm.originatorName" readonly> | ||
38 | + </md-input-container> | ||
39 | + </div> | ||
40 | + <div layout="row" ng-if="vm.startTime || vm.endTime"> | ||
41 | + <md-input-container ng-if="vm.startTime" flex class="md-block"> | ||
42 | + <label translate>alarm.start-time</label> | ||
43 | + <input ng-model="vm.startTime" readonly> | ||
44 | + </md-input-container> | ||
45 | + <md-input-container ng-if="vm.endTime" flex class="md-block"> | ||
46 | + <label translate>alarm.end-time</label> | ||
47 | + <input ng-model="vm.endTime" readonly> | ||
48 | + </md-input-container> | ||
49 | + <span flex ng-if="!vm.startTime || !vm.endTime"></span> | ||
50 | + </div> | ||
51 | + <div layout="row" ng-if="vm.ackTime || vm.clearTime"> | ||
52 | + <md-input-container ng-if="vm.ackTime" flex class="md-block"> | ||
53 | + <label translate>alarm.ack-time</label> | ||
54 | + <input ng-model="vm.ackTime" readonly> | ||
55 | + </md-input-container> | ||
56 | + <md-input-container ng-if="vm.clearTime" flex class="md-block"> | ||
57 | + <label translate>alarm.clear-time</label> | ||
58 | + <input ng-model="vm.clearTime" readonly> | ||
59 | + </md-input-container> | ||
60 | + <span flex ng-if="!vm.ackTime || !vm.clearTime"></span> | ||
61 | + </div> | ||
62 | + <div layout="row"> | ||
63 | + <md-input-container flex class="md-block"> | ||
64 | + <label translate>alarm.type</label> | ||
65 | + <input ng-model="vm.alarm.type" readonly> | ||
66 | + </md-input-container> | ||
67 | + <md-input-container flex class="md-block"> | ||
68 | + <label translate>alarm.severity</label> | ||
69 | + <input class="tb-severity" ng-class="vm.types.alarmSeverity[vm.alarm.severity].class" | ||
70 | + ng-model="vm.alarmSeverity" readonly> | ||
71 | + </md-input-container> | ||
72 | + <md-input-container flex class="md-block"> | ||
73 | + <label translate>alarm.status</label> | ||
74 | + <input ng-model="vm.alarmStatus" readonly> | ||
75 | + </md-input-container> | ||
76 | + </div> | ||
77 | + <div class="md-caption" style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>alarm.details</div> | ||
78 | + <div flex class="tb-alarm-details-panel" layout="column"> | ||
79 | + <div flex id="tb-alarm-details" readonly | ||
80 | + ui-ace="vm.alarmDetailsOptions" | ||
81 | + ng-model="vm.alarmDetails"> | ||
82 | + </div> | ||
83 | + </div> | ||
84 | + </div> | ||
85 | + </md-dialog-content> | ||
86 | + <md-dialog-actions layout="row"> | ||
87 | + <md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeUnack || | ||
88 | + vm.alarm.status==vm.types.alarmStatus.clearedUnack" | ||
89 | + class="md-raised md-primary" | ||
90 | + ng-disabled="loading" | ||
91 | + ng-click="vm.acknowledge()" | ||
92 | + style="margin-right:20px;">{{ 'alarm.acknowledge' | | ||
93 | + translate }} | ||
94 | + </md-button> | ||
95 | + <md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeAck || | ||
96 | + vm.alarm.status==vm.types.alarmStatus.activeUnack" | ||
97 | + class="md-raised md-primary" | ||
98 | + ng-disabled="loading" | ||
99 | + ng-click="vm.clear()">{{ 'alarm.clear' | | ||
100 | + translate }} | ||
101 | + </md-button> | ||
102 | + <span flex></span> | ||
103 | + <md-button ng-disabled="loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' | | ||
104 | + translate }} | ||
105 | + </md-button> | ||
106 | + </md-dialog-actions> | ||
107 | +</md-dialog> |
ui/src/app/alarm/alarm-header.directive.js
0 → 100644
1 | +/* | ||
2 | + * Copyright © 2016-2017 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 | +/* eslint-disable import/no-unresolved, import/default */ | ||
17 | + | ||
18 | +import alarmHeaderTemplate from './alarm-header.tpl.html'; | ||
19 | + | ||
20 | +/* eslint-enable import/no-unresolved, import/default */ | ||
21 | + | ||
22 | +/*@ngInject*/ | ||
23 | +export default function AlarmHeaderDirective($compile, $templateCache) { | ||
24 | + | ||
25 | + var linker = function (scope, element) { | ||
26 | + | ||
27 | + var template = $templateCache.get(alarmHeaderTemplate); | ||
28 | + element.html(template); | ||
29 | + $compile(element.contents())(scope); | ||
30 | + | ||
31 | + } | ||
32 | + | ||
33 | + return { | ||
34 | + restrict: "A", | ||
35 | + replace: false, | ||
36 | + link: linker, | ||
37 | + scope: false | ||
38 | + }; | ||
39 | +} |
ui/src/app/alarm/alarm-header.tpl.html
renamed from
ui/src/app/event/event-header-alarm.tpl.html
@@ -15,6 +15,9 @@ | @@ -15,6 +15,9 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<div translate class="tb-cell" flex="30">event.event-time</div> | ||
19 | -<div translate class="tb-cell" flex="20">event.server</div> | ||
20 | -<div translate class="tb-cell" flex="20">event.alarm</div> | 18 | +<div translate class="tb-cell" flex="30">alarm.created-time</div> |
19 | +<div translate class="tb-cell" flex="15">alarm.originator</div> | ||
20 | +<div translate class="tb-cell" flex="20">alarm.type</div> | ||
21 | +<div translate class="tb-cell" flex="15">alarm.severity</div> | ||
22 | +<div translate class="tb-cell" flex="20">alarm.status</div> | ||
23 | +<div translate class="tb-cell" flex="15">alarm.details</div> |
ui/src/app/alarm/alarm-row.directive.js
0 → 100644
1 | +/* | ||
2 | + * Copyright © 2016-2017 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 | +/* eslint-disable import/no-unresolved, import/default */ | ||
17 | + | ||
18 | +import alarmDetailsDialogTemplate from './alarm-details-dialog.tpl.html'; | ||
19 | + | ||
20 | +import alarmRowTemplate from './alarm-row.tpl.html'; | ||
21 | + | ||
22 | +/* eslint-enable import/no-unresolved, import/default */ | ||
23 | + | ||
24 | +/*@ngInject*/ | ||
25 | +export default function AlarmRowDirective($compile, $templateCache, types, $mdDialog, $document) { | ||
26 | + | ||
27 | + var linker = function (scope, element, attrs) { | ||
28 | + | ||
29 | + var template = $templateCache.get(alarmRowTemplate); | ||
30 | + element.html(template); | ||
31 | + | ||
32 | + scope.alarm = attrs.alarm; | ||
33 | + scope.types = types; | ||
34 | + | ||
35 | + scope.showAlarmDetails = function($event) { | ||
36 | + var onShowingCallback = { | ||
37 | + onShowing: function(){} | ||
38 | + } | ||
39 | + $mdDialog.show({ | ||
40 | + controller: 'AlarmDetailsDialogController', | ||
41 | + controllerAs: 'vm', | ||
42 | + templateUrl: alarmDetailsDialogTemplate, | ||
43 | + locals: {alarmId: scope.alarm.id.id, showingCallback: onShowingCallback}, | ||
44 | + parent: angular.element($document[0].body), | ||
45 | + targetEvent: $event, | ||
46 | + fullscreen: true, | ||
47 | + skipHide: true, | ||
48 | + onShowing: function(scope, element) { | ||
49 | + onShowingCallback.onShowing(scope, element); | ||
50 | + } | ||
51 | + }).then(function (alarm) { | ||
52 | + if (alarm) { | ||
53 | + scope.alarm = alarm; | ||
54 | + } | ||
55 | + }); | ||
56 | + } | ||
57 | + | ||
58 | + $compile(element.contents())(scope); | ||
59 | + } | ||
60 | + | ||
61 | + return { | ||
62 | + restrict: "A", | ||
63 | + replace: false, | ||
64 | + link: linker, | ||
65 | + scope: false | ||
66 | + }; | ||
67 | +} |
ui/src/app/alarm/alarm-row.tpl.html
renamed from
ui/src/app/event/event-row-alarm.tpl.html
@@ -15,14 +15,19 @@ | @@ -15,14 +15,19 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<div class="tb-cell" flex="30">{{event.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div> | ||
19 | -<div class="tb-cell" flex="20">{{event.body.server}}</div> | ||
20 | -<div class="tb-cell" flex="20"> | ||
21 | - <md-button ng-if="event.body.body" class="md-icon-button md-primary" | ||
22 | - ng-click="showContent($event, event.body.body, 'event.alarm')" | 18 | +<div class="tb-cell" flex="30">{{alarm.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div> |
19 | +<div class="tb-cell" flex="15">{{alarm.originatorName}}</div> | ||
20 | +<div class="tb-cell" flex="20">{{alarm.type}}</div> | ||
21 | +<div class="tb-cell tb-severity" flex="15" ng-class="types.alarmSeverity[alarm.severity].class"> | ||
22 | + {{ alarm ? (types.alarmSeverity[alarm.severity].name | translate) : '' }} | ||
23 | +</div> | ||
24 | +<div class="tb-cell" flex="20">{{ alarm ? (('alarm.display-status.' + alarm.status) | translate) : '' }}</div> | ||
25 | +<div class="tb-cell" flex="15"> | ||
26 | + <md-button class="md-icon-button md-primary" | ||
27 | + ng-click="showAlarmDetails($event)" | ||
23 | aria-label="{{ 'action.view' | translate }}"> | 28 | aria-label="{{ 'action.view' | translate }}"> |
24 | <md-tooltip md-direction="top"> | 29 | <md-tooltip md-direction="top"> |
25 | - {{ 'action.view' | translate }} | 30 | + {{ 'alarm.details' | translate }} |
26 | </md-tooltip> | 31 | </md-tooltip> |
27 | <md-icon aria-label="{{ 'action.view' | translate }}" | 32 | <md-icon aria-label="{{ 'action.view' | translate }}" |
28 | class="material-icons"> | 33 | class="material-icons"> |
ui/src/app/alarm/alarm-table.directive.js
0 → 100644
1 | +/* | ||
2 | + * Copyright © 2016-2017 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 | +import './alarm.scss'; | ||
17 | + | ||
18 | +/* eslint-disable import/no-unresolved, import/default */ | ||
19 | + | ||
20 | +import alarmTableTemplate from './alarm-table.tpl.html'; | ||
21 | + | ||
22 | +/* eslint-enable import/no-unresolved, import/default */ | ||
23 | + | ||
24 | +/*@ngInject*/ | ||
25 | +export default function AlarmTableDirective($compile, $templateCache, $rootScope, types, alarmService) { | ||
26 | + | ||
27 | + var linker = function (scope, element) { | ||
28 | + | ||
29 | + var template = $templateCache.get(alarmTableTemplate); | ||
30 | + | ||
31 | + element.html(template); | ||
32 | + | ||
33 | + scope.types = types; | ||
34 | + | ||
35 | + scope.alarmSearchStatus = types.alarmSearchStatus.any; | ||
36 | + | ||
37 | + var pageSize = 20; | ||
38 | + var startTime = 0; | ||
39 | + var endTime = 0; | ||
40 | + | ||
41 | + scope.timewindow = { | ||
42 | + history: { | ||
43 | + timewindowMs: 24 * 60 * 60 * 1000 // 1 day | ||
44 | + } | ||
45 | + } | ||
46 | + | ||
47 | + scope.topIndex = 0; | ||
48 | + | ||
49 | + scope.theAlarms = { | ||
50 | + getItemAtIndex: function (index) { | ||
51 | + if (index > scope.alarms.data.length) { | ||
52 | + scope.theAlarms.fetchMoreItems_(index); | ||
53 | + return null; | ||
54 | + } | ||
55 | + var item = scope.alarms.data[index]; | ||
56 | + if (item) { | ||
57 | + item.indexNumber = index + 1; | ||
58 | + } | ||
59 | + return item; | ||
60 | + }, | ||
61 | + | ||
62 | + getLength: function () { | ||
63 | + if (scope.alarms.hasNext) { | ||
64 | + return scope.alarms.data.length + scope.alarms.nextPageLink.limit; | ||
65 | + } else { | ||
66 | + return scope.alarms.data.length; | ||
67 | + } | ||
68 | + }, | ||
69 | + | ||
70 | + fetchMoreItems_: function () { | ||
71 | + if (scope.alarms.hasNext && !scope.alarms.pending) { | ||
72 | + if (scope.entityType && scope.entityId && scope.alarmSearchStatus) { | ||
73 | + var promise = alarmService.getAlarms(scope.entityType, scope.entityId, | ||
74 | + scope.alarms.nextPageLink, scope.alarmSearchStatus, null, true, false); | ||
75 | + if (promise) { | ||
76 | + scope.alarms.pending = true; | ||
77 | + promise.then( | ||
78 | + function success(alarms) { | ||
79 | + scope.alarms.data = scope.alarms.data.concat(alarms.data); | ||
80 | + scope.alarms.nextPageLink = alarms.nextPageLink; | ||
81 | + scope.alarms.hasNext = alarms.hasNext; | ||
82 | + if (scope.alarms.hasNext) { | ||
83 | + scope.alarms.nextPageLink.limit = pageSize; | ||
84 | + } | ||
85 | + scope.alarms.pending = false; | ||
86 | + }, | ||
87 | + function fail() { | ||
88 | + scope.alarms.hasNext = false; | ||
89 | + scope.alarms.pending = false; | ||
90 | + }); | ||
91 | + } else { | ||
92 | + scope.alarms.hasNext = false; | ||
93 | + } | ||
94 | + } else { | ||
95 | + scope.alarms.hasNext = false; | ||
96 | + } | ||
97 | + } | ||
98 | + } | ||
99 | + }; | ||
100 | + | ||
101 | + scope.$watch("entityId", function(newVal, prevVal) { | ||
102 | + if (newVal && !angular.equals(newVal, prevVal)) { | ||
103 | + resetFilter(); | ||
104 | + reload(); | ||
105 | + } | ||
106 | + }); | ||
107 | + | ||
108 | + | ||
109 | + | ||
110 | + function destroyWatchers() { | ||
111 | + if (scope.alarmSearchStatusWatchHandle) { | ||
112 | + scope.alarmSearchStatusWatchHandle(); | ||
113 | + scope.alarmSearchStatusWatchHandle = null; | ||
114 | + } | ||
115 | + if (scope.timewindowWatchHandle) { | ||
116 | + scope.timewindowWatchHandle(); | ||
117 | + scope.timewindowWatchHandle = null; | ||
118 | + } | ||
119 | + } | ||
120 | + | ||
121 | + function initWatchers() { | ||
122 | + scope.alarmSearchStatusWatchHandle = scope.$watch("alarmSearchStatus", function(newVal, prevVal) { | ||
123 | + if (newVal && !angular.equals(newVal, prevVal)) { | ||
124 | + reload(); | ||
125 | + } | ||
126 | + }); | ||
127 | + scope.timewindowWatchHandle = scope.$watch("timewindow", function(newVal, prevVal) { | ||
128 | + if (newVal && !angular.equals(newVal, prevVal)) { | ||
129 | + reload(); | ||
130 | + } | ||
131 | + }, true); | ||
132 | + } | ||
133 | + | ||
134 | + function resetFilter() { | ||
135 | + destroyWatchers(); | ||
136 | + scope.timewindow = { | ||
137 | + history: { | ||
138 | + timewindowMs: 24 * 60 * 60 * 1000 // 1 day | ||
139 | + } | ||
140 | + }; | ||
141 | + scope.alarmSearchStatus = types.alarmSearchStatus.any; | ||
142 | + initWatchers(); | ||
143 | + } | ||
144 | + | ||
145 | + function updateTimeWindowRange () { | ||
146 | + if (scope.timewindow.history.timewindowMs) { | ||
147 | + var currentTime = (new Date).getTime(); | ||
148 | + startTime = currentTime - scope.timewindow.history.timewindowMs; | ||
149 | + endTime = currentTime; | ||
150 | + } else { | ||
151 | + startTime = scope.timewindow.history.fixedTimewindow.startTimeMs; | ||
152 | + endTime = scope.timewindow.history.fixedTimewindow.endTimeMs; | ||
153 | + } | ||
154 | + } | ||
155 | + | ||
156 | + function reload () { | ||
157 | + scope.topIndex = 0; | ||
158 | + scope.selected = []; | ||
159 | + updateTimeWindowRange(); | ||
160 | + scope.alarms = { | ||
161 | + data: [], | ||
162 | + nextPageLink: { | ||
163 | + limit: pageSize, | ||
164 | + startTime: startTime, | ||
165 | + endTime: endTime | ||
166 | + }, | ||
167 | + hasNext: true, | ||
168 | + pending: false | ||
169 | + }; | ||
170 | + scope.theAlarms.getItemAtIndex(pageSize); | ||
171 | + } | ||
172 | + | ||
173 | + scope.noData = function() { | ||
174 | + return scope.alarms.data.length == 0 && !scope.alarms.hasNext; | ||
175 | + } | ||
176 | + | ||
177 | + scope.hasData = function() { | ||
178 | + return scope.alarms.data.length > 0; | ||
179 | + } | ||
180 | + | ||
181 | + scope.loading = function() { | ||
182 | + return $rootScope.loading; | ||
183 | + } | ||
184 | + | ||
185 | + scope.hasScroll = function() { | ||
186 | + var repeatContainer = scope.repeatContainer[0]; | ||
187 | + if (repeatContainer) { | ||
188 | + var scrollElement = repeatContainer.children[0]; | ||
189 | + if (scrollElement) { | ||
190 | + return scrollElement.scrollHeight > scrollElement.clientHeight; | ||
191 | + } | ||
192 | + } | ||
193 | + return false; | ||
194 | + } | ||
195 | + | ||
196 | + reload(); | ||
197 | + | ||
198 | + initWatchers(); | ||
199 | + | ||
200 | + $compile(element.contents())(scope); | ||
201 | + } | ||
202 | + | ||
203 | + return { | ||
204 | + restrict: "E", | ||
205 | + link: linker, | ||
206 | + scope: { | ||
207 | + entityType: '=', | ||
208 | + entityId: '=' | ||
209 | + } | ||
210 | + }; | ||
211 | +} |
ui/src/app/alarm/alarm-table.tpl.html
0 → 100644
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2017 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 | +<md-content flex class="md-padding tb-absolute-fill" layout="column"> | ||
19 | + <section layout="row"> | ||
20 | + <md-input-container class="md-block" style="width: 200px;"> | ||
21 | + <label translate>alarm.alarm-status</label> | ||
22 | + <md-select ng-model="alarmSearchStatus" ng-disabled="loading()"> | ||
23 | + <md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus"> | ||
24 | + {{ ('alarm.search-status.' + searchStatus) | translate }} | ||
25 | + </md-option> | ||
26 | + </md-select> | ||
27 | + </md-input-container> | ||
28 | + <tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow> | ||
29 | + </section> | ||
30 | + <div flex layout="column" class="tb-alarm-container md-whiteframe-z1"> | ||
31 | + <md-list flex layout="column" class="tb-alarm-table"> | ||
32 | + <md-list class="tb-row tb-header" layout="row" tb-alarm-header> | ||
33 | + </md-list> | ||
34 | + <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()" | ||
35 | + ng-show="loading()"></md-progress-linear> | ||
36 | + <md-divider></md-divider> | ||
37 | + <span translate layout-align="center center" | ||
38 | + style="margin-top: 25px;" | ||
39 | + class="tb-prompt" ng-show="noData()">alarm.no-alarms-prompt</span> | ||
40 | + <md-virtual-repeat-container ng-show="hasData()" flex md-top-index="topIndex" tb-scope-element="repeatContainer"> | ||
41 | + <md-list-item md-virtual-repeat="alarm in theAlarms" md-on-demand flex ng-style="hasScroll() ? {'margin-right':'-15px'} : {}"> | ||
42 | + <md-list class="tb-row" flex layout="row" tb-alarm-row alarm="{{alarm}}"> | ||
43 | + </md-list> | ||
44 | + <md-divider flex></md-divider> | ||
45 | + </md-list-item> | ||
46 | + </md-virtual-repeat-container> | ||
47 | + </md-list> | ||
48 | + </div> | ||
49 | +</md-content> |
ui/src/app/alarm/alarm.scss
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2017 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 | +.tb-alarm-container { | ||
18 | + overflow-x: auto; | ||
19 | +} | ||
20 | + | ||
21 | +md-list.tb-alarm-table { | ||
22 | + padding: 0px; | ||
23 | + min-width: 700px; | ||
24 | + | ||
25 | + md-list-item { | ||
26 | + padding: 0px; | ||
27 | + } | ||
28 | + | ||
29 | + .tb-row { | ||
30 | + height: 48px; | ||
31 | + padding: 0px; | ||
32 | + overflow: hidden; | ||
33 | + } | ||
34 | + | ||
35 | + .tb-row:hover { | ||
36 | + background-color: #EEEEEE; | ||
37 | + } | ||
38 | + | ||
39 | + .tb-header:hover { | ||
40 | + background: none; | ||
41 | + } | ||
42 | + | ||
43 | + .tb-header { | ||
44 | + .tb-cell { | ||
45 | + color: rgba(0,0,0,.54); | ||
46 | + font-size: 12px; | ||
47 | + font-weight: 700; | ||
48 | + white-space: nowrap; | ||
49 | + background: none; | ||
50 | + } | ||
51 | + } | ||
52 | + | ||
53 | + .tb-cell { | ||
54 | + padding: 0 24px; | ||
55 | + margin: auto 0; | ||
56 | + color: rgba(0,0,0,.87); | ||
57 | + font-size: 13px; | ||
58 | + vertical-align: middle; | ||
59 | + text-align: left; | ||
60 | + overflow: hidden; | ||
61 | + .md-button { | ||
62 | + padding: 0; | ||
63 | + margin: 0; | ||
64 | + } | ||
65 | + } | ||
66 | + | ||
67 | + .tb-cell.tb-number { | ||
68 | + text-align: right; | ||
69 | + } | ||
70 | + | ||
71 | +} | ||
72 | + | ||
73 | +#tb-alarm-content { | ||
74 | + min-width: 400px; | ||
75 | + min-height: 50px; | ||
76 | + width: 100%; | ||
77 | + height: 100%; | ||
78 | +} |
ui/src/app/alarm/index.js
0 → 100644
1 | +/* | ||
2 | + * Copyright © 2016-2017 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 AlarmDetailsDialogController from './alarm-details-dialog.controller'; | ||
18 | +import AlarmHeaderDirective from './alarm-header.directive'; | ||
19 | +import AlarmRowDirective from './alarm-row.directive'; | ||
20 | +import AlarmTableDirective from './alarm-table.directive'; | ||
21 | + | ||
22 | +export default angular.module('thingsboard.alarm', []) | ||
23 | + .controller('AlarmDetailsDialogController', AlarmDetailsDialogController) | ||
24 | + .directive('tbAlarmHeader', AlarmHeaderDirective) | ||
25 | + .directive('tbAlarmRow', AlarmRowDirective) | ||
26 | + .directive('tbAlarmTable', AlarmTableDirective) | ||
27 | + .name; |
@@ -21,6 +21,7 @@ export default angular.module('thingsboard.api.alarm', []) | @@ -21,6 +21,7 @@ export default angular.module('thingsboard.api.alarm', []) | ||
21 | function AlarmService($http, $q, $interval, $filter) { | 21 | function AlarmService($http, $q, $interval, $filter) { |
22 | var service = { | 22 | var service = { |
23 | getAlarm: getAlarm, | 23 | getAlarm: getAlarm, |
24 | + getAlarmInfo: getAlarmInfo, | ||
24 | saveAlarm: saveAlarm, | 25 | saveAlarm: saveAlarm, |
25 | ackAlarm: ackAlarm, | 26 | ackAlarm: ackAlarm, |
26 | clearAlarm: clearAlarm, | 27 | clearAlarm: clearAlarm, |
@@ -46,6 +47,21 @@ function AlarmService($http, $q, $interval, $filter) { | @@ -46,6 +47,21 @@ function AlarmService($http, $q, $interval, $filter) { | ||
46 | return deferred.promise; | 47 | return deferred.promise; |
47 | } | 48 | } |
48 | 49 | ||
50 | + function getAlarmInfo(alarmId, ignoreErrors, config) { | ||
51 | + var deferred = $q.defer(); | ||
52 | + var url = '/api/alarm/info/' + alarmId; | ||
53 | + if (!config) { | ||
54 | + config = {}; | ||
55 | + } | ||
56 | + config = Object.assign(config, { ignoreErrors: ignoreErrors }); | ||
57 | + $http.get(url, config).then(function success(response) { | ||
58 | + deferred.resolve(response.data); | ||
59 | + }, function fail() { | ||
60 | + deferred.reject(); | ||
61 | + }); | ||
62 | + return deferred.promise; | ||
63 | + } | ||
64 | + | ||
49 | function saveAlarm(alarm, ignoreErrors, config) { | 65 | function saveAlarm(alarm, ignoreErrors, config) { |
50 | var deferred = $q.defer(); | 66 | var deferred = $q.defer(); |
51 | var url = '/api/alarm'; | 67 | var url = '/api/alarm'; |
@@ -91,7 +107,7 @@ function AlarmService($http, $q, $interval, $filter) { | @@ -91,7 +107,7 @@ function AlarmService($http, $q, $interval, $filter) { | ||
91 | return deferred.promise; | 107 | return deferred.promise; |
92 | } | 108 | } |
93 | 109 | ||
94 | - function getAlarms(entityType, entityId, pageLink, alarmStatus, ascOrder, config) { | 110 | + function getAlarms(entityType, entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator, ascOrder, config) { |
95 | var deferred = $q.defer(); | 111 | var deferred = $q.defer(); |
96 | var url = '/api/alarm/' + entityType + '/' + entityId + '?limit=' + pageLink.limit; | 112 | var url = '/api/alarm/' + entityType + '/' + entityId + '?limit=' + pageLink.limit; |
97 | 113 | ||
@@ -104,9 +120,15 @@ function AlarmService($http, $q, $interval, $filter) { | @@ -104,9 +120,15 @@ function AlarmService($http, $q, $interval, $filter) { | ||
104 | if (angular.isDefined(pageLink.idOffset)) { | 120 | if (angular.isDefined(pageLink.idOffset)) { |
105 | url += '&offset=' + pageLink.idOffset; | 121 | url += '&offset=' + pageLink.idOffset; |
106 | } | 122 | } |
123 | + if (alarmSearchStatus) { | ||
124 | + url += '&searchStatus=' + alarmSearchStatus; | ||
125 | + } | ||
107 | if (alarmStatus) { | 126 | if (alarmStatus) { |
108 | url += '&status=' + alarmStatus; | 127 | url += '&status=' + alarmStatus; |
109 | } | 128 | } |
129 | + if (fetchOriginator) { | ||
130 | + url += '&fetchOriginator=' + ((fetchOriginator===true) ? 'true' : 'false'); | ||
131 | + } | ||
110 | if (angular.isDefined(ascOrder) && ascOrder != null) { | 132 | if (angular.isDefined(ascOrder) && ascOrder != null) { |
111 | url += '&ascOrder=' + (ascOrder ? 'true' : 'false'); | 133 | url += '&ascOrder=' + (ascOrder ? 'true' : 'false'); |
112 | } | 134 | } |
@@ -121,7 +143,8 @@ function AlarmService($http, $q, $interval, $filter) { | @@ -121,7 +143,8 @@ function AlarmService($http, $q, $interval, $filter) { | ||
121 | 143 | ||
122 | function fetchAlarms(alarmsQuery, pageLink, deferred, alarmsList) { | 144 | function fetchAlarms(alarmsQuery, pageLink, deferred, alarmsList) { |
123 | getAlarms(alarmsQuery.entityType, alarmsQuery.entityId, | 145 | getAlarms(alarmsQuery.entityType, alarmsQuery.entityId, |
124 | - pageLink, alarmsQuery.alarmStatus, false, {ignoreLoading: true}).then( | 146 | + pageLink, alarmsQuery.alarmSearchStatus, alarmsQuery.alarmStatus, |
147 | + alarmsQuery.fetchOriginator, false, {ignoreLoading: true}).then( | ||
125 | function success(alarms) { | 148 | function success(alarms) { |
126 | if (!alarmsList) { | 149 | if (!alarmsList) { |
127 | alarmsList = []; | 150 | alarmsList = []; |
@@ -171,7 +194,9 @@ function AlarmService($http, $q, $interval, $filter) { | @@ -171,7 +194,9 @@ function AlarmService($http, $q, $interval, $filter) { | ||
171 | var alarmsQuery = { | 194 | var alarmsQuery = { |
172 | entityType: entityType, | 195 | entityType: entityType, |
173 | entityId: entityId, | 196 | entityId: entityId, |
197 | + alarmSearchStatus: null, | ||
174 | alarmStatus: alarmStatus, | 198 | alarmStatus: alarmStatus, |
199 | + fetchOriginator: false, | ||
175 | interval: interval, | 200 | interval: interval, |
176 | limit: limit, | 201 | limit: limit, |
177 | onAlarms: onAlarms | 202 | onAlarms: onAlarms |
ui/src/app/api/alias-controller.js
0 → 100644
1 | +/* | ||
2 | + * Copyright © 2016-2017 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 | +const varsRegex = /\$\{([^\}]*)\}/g; | ||
18 | + | ||
19 | +export default class AliasController { | ||
20 | + | ||
21 | + constructor($scope, $q, $filter, utils, types, entityService, stateController, entityAliases) { | ||
22 | + this.$scope = $scope; | ||
23 | + this.$q = $q; | ||
24 | + this.$filter = $filter; | ||
25 | + this.utils = utils; | ||
26 | + this.types = types; | ||
27 | + this.entityService = entityService; | ||
28 | + this.stateController = stateController; | ||
29 | + this.entityAliases = angular.copy(entityAliases); | ||
30 | + this.resolvedAliases = {}; | ||
31 | + this.resolvedAliasesPromise = {}; | ||
32 | + this.resolvedAliasesToStateEntities = {}; | ||
33 | + } | ||
34 | + | ||
35 | + updateEntityAliases(newEntityAliases) { | ||
36 | + var changedAliasIds = []; | ||
37 | + for (var aliasId in newEntityAliases) { | ||
38 | + var newEntityAlias = newEntityAliases[aliasId]; | ||
39 | + var prevEntityAlias = this.entityAliases[aliasId]; | ||
40 | + if (!angular.equals(newEntityAlias, prevEntityAlias)) { | ||
41 | + changedAliasIds.push(aliasId); | ||
42 | + this.setAliasUnresolved(aliasId); | ||
43 | + } | ||
44 | + } | ||
45 | + for (aliasId in this.entityAliases) { | ||
46 | + if (!newEntityAliases[aliasId]) { | ||
47 | + changedAliasIds.push(aliasId); | ||
48 | + this.setAliasUnresolved(aliasId); | ||
49 | + } | ||
50 | + } | ||
51 | + this.entityAliases = angular.copy(newEntityAliases); | ||
52 | + if (changedAliasIds.length) { | ||
53 | + this.$scope.$broadcast('entityAliasesChanged', changedAliasIds); | ||
54 | + } | ||
55 | + } | ||
56 | + | ||
57 | + dashboardStateChanged() { | ||
58 | + var newEntityId = this.stateController.getStateParams().entityId; | ||
59 | + var changedAliasIds = []; | ||
60 | + for (var aliasId in this.resolvedAliasesToStateEntities) { | ||
61 | + var prevEntityId = this.resolvedAliasesToStateEntities[aliasId]; | ||
62 | + if (!angular.equals(newEntityId, prevEntityId)) { | ||
63 | + changedAliasIds.push(aliasId); | ||
64 | + this.setAliasUnresolved(aliasId); | ||
65 | + } | ||
66 | + } | ||
67 | + if (changedAliasIds.length) { | ||
68 | + this.$scope.$broadcast('entityAliasesChanged', changedAliasIds); | ||
69 | + } | ||
70 | + } | ||
71 | + | ||
72 | + setAliasUnresolved(aliasId) { | ||
73 | + delete this.resolvedAliases[aliasId]; | ||
74 | + delete this.resolvedAliasesPromise[aliasId]; | ||
75 | + delete this.resolvedAliasesToStateEntities[aliasId]; | ||
76 | + } | ||
77 | + | ||
78 | + getEntityAliases() { | ||
79 | + return this.entityAliases; | ||
80 | + } | ||
81 | + | ||
82 | + getAliasInfo(aliasId) { | ||
83 | + var deferred = this.$q.defer(); | ||
84 | + var aliasInfo = this.resolvedAliases[aliasId]; | ||
85 | + if (aliasInfo) { | ||
86 | + deferred.resolve(aliasInfo); | ||
87 | + return deferred.promise; | ||
88 | + } else if (this.resolvedAliasesPromise[aliasId]) { | ||
89 | + return this.resolvedAliasesPromise[aliasId]; | ||
90 | + } else { | ||
91 | + this.resolvedAliasesPromise[aliasId] = deferred.promise; | ||
92 | + var aliasCtrl = this; | ||
93 | + var entityAlias = this.entityAliases[aliasId]; | ||
94 | + if (entityAlias) { | ||
95 | + this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then( | ||
96 | + function success(aliasInfo) { | ||
97 | + aliasCtrl.resolvedAliases[aliasId] = aliasInfo; | ||
98 | + if (aliasInfo.stateEntity) { | ||
99 | + aliasCtrl.resolvedAliasesToStateEntities[aliasId] = | ||
100 | + aliasCtrl.stateController.getStateParams().entityId; | ||
101 | + } | ||
102 | + aliasCtrl.$scope.$broadcast('entityAliasResolved', aliasId); | ||
103 | + deferred.resolve(aliasInfo); | ||
104 | + }, | ||
105 | + function fail() { | ||
106 | + deferred.reject(); | ||
107 | + } | ||
108 | + ); | ||
109 | + } else { | ||
110 | + deferred.reject(); | ||
111 | + } | ||
112 | + return this.resolvedAliasesPromise[aliasId]; | ||
113 | + } | ||
114 | + } | ||
115 | + | ||
116 | + resolveDatasource(datasource) { | ||
117 | + var deferred = this.$q.defer(); | ||
118 | + if (datasource.type === this.types.datasourceType.entity) { | ||
119 | + if (datasource.entityAliasId) { | ||
120 | + this.getAliasInfo(datasource.entityAliasId).then( | ||
121 | + function success(aliasInfo) { | ||
122 | + datasource.aliasName = aliasInfo.alias; | ||
123 | + if (aliasInfo.resolveMultiple) { | ||
124 | + var newDatasource; | ||
125 | + var resolvedEntities = aliasInfo.resolvedEntities; | ||
126 | + if (resolvedEntities && resolvedEntities.length) { | ||
127 | + var datasources = []; | ||
128 | + for (var i=0;i<resolvedEntities.length;i++) { | ||
129 | + var resolvedEntity = resolvedEntities[i]; | ||
130 | + newDatasource = angular.copy(datasource); | ||
131 | + newDatasource.entityId = resolvedEntity.id; | ||
132 | + newDatasource.entityType = resolvedEntity.entityType; | ||
133 | + newDatasource.entityName = resolvedEntity.name; | ||
134 | + newDatasource.name = resolvedEntity.name; | ||
135 | + newDatasource.generated = i > 0 ? true : false; | ||
136 | + datasources.push(newDatasource); | ||
137 | + } | ||
138 | + deferred.resolve(datasources); | ||
139 | + } else { | ||
140 | + if (aliasInfo.stateEntity) { | ||
141 | + newDatasource = angular.copy(datasource); | ||
142 | + newDatasource.unresolvedStateEntity = true; | ||
143 | + deferred.resolve([newDatasource]); | ||
144 | + } else { | ||
145 | + deferred.reject(); | ||
146 | + } | ||
147 | + } | ||
148 | + } else { | ||
149 | + var entity = aliasInfo.currentEntity; | ||
150 | + if (entity) { | ||
151 | + datasource.entityId = entity.id; | ||
152 | + datasource.entityType = entity.entityType; | ||
153 | + datasource.entityName = entity.name; | ||
154 | + datasource.name = entity.name; | ||
155 | + deferred.resolve([datasource]); | ||
156 | + } else { | ||
157 | + if (aliasInfo.stateEntity) { | ||
158 | + datasource.unresolvedStateEntity = true; | ||
159 | + deferred.resolve([datasource]); | ||
160 | + } else { | ||
161 | + deferred.reject(); | ||
162 | + } | ||
163 | + } | ||
164 | + } | ||
165 | + }, | ||
166 | + function fail() { | ||
167 | + deferred.reject(); | ||
168 | + } | ||
169 | + ); | ||
170 | + } else { // entityId | ||
171 | + datasource.aliasName = datasource.entityName; | ||
172 | + datasource.name = datasource.entityName; | ||
173 | + deferred.resolve([datasource]); | ||
174 | + } | ||
175 | + } else { // function | ||
176 | + deferred.resolve([datasource]); | ||
177 | + } | ||
178 | + return deferred.promise; | ||
179 | + } | ||
180 | + | ||
181 | + resolveDatasources(datasources) { | ||
182 | + | ||
183 | + function updateDataKeyLabel(dataKey, datasource) { | ||
184 | + if (!dataKey.pattern) { | ||
185 | + dataKey.pattern = angular.copy(dataKey.label); | ||
186 | + } | ||
187 | + var pattern = dataKey.pattern; | ||
188 | + var label = dataKey.pattern; | ||
189 | + var match = varsRegex.exec(pattern); | ||
190 | + while (match !== null) { | ||
191 | + var variable = match[0]; | ||
192 | + var variableName = match[1]; | ||
193 | + if (variableName === 'dsName') { | ||
194 | + label = label.split(variable).join(datasource.name); | ||
195 | + } else if (variableName === 'entityName') { | ||
196 | + label = label.split(variable).join(datasource.entityName); | ||
197 | + } else if (variableName === 'deviceName') { | ||
198 | + label = label.split(variable).join(datasource.entityName); | ||
199 | + } else if (variableName === 'aliasName') { | ||
200 | + label = label.split(variable).join(datasource.aliasName); | ||
201 | + } | ||
202 | + match = varsRegex.exec(pattern); | ||
203 | + } | ||
204 | + dataKey.label = label; | ||
205 | + } | ||
206 | + | ||
207 | + function updateDatasourceKeyLabels(datasource) { | ||
208 | + for (var dk = 0; dk < datasource.dataKeys.length; dk++) { | ||
209 | + updateDataKeyLabel(datasource.dataKeys[dk], datasource); | ||
210 | + } | ||
211 | + } | ||
212 | + | ||
213 | + var deferred = this.$q.defer(); | ||
214 | + var newDatasources = angular.copy(datasources); | ||
215 | + var datasorceResolveTasks = []; | ||
216 | + var aliasCtrl = this; | ||
217 | + newDatasources.forEach(function (datasource) { | ||
218 | + var resolveDatasourceTask = aliasCtrl.resolveDatasource(datasource); | ||
219 | + datasorceResolveTasks.push(resolveDatasourceTask); | ||
220 | + }); | ||
221 | + this.$q.all(datasorceResolveTasks).then( | ||
222 | + function success(datasourcesArrays) { | ||
223 | + var datasources = [].concat.apply([], datasourcesArrays); | ||
224 | + datasources = aliasCtrl.$filter('orderBy')(datasources, '+generated'); | ||
225 | + var index = 0; | ||
226 | + var functionIndex = 0; | ||
227 | + datasources.forEach(function(datasource) { | ||
228 | + if (datasource.type === aliasCtrl.types.datasourceType.function) { | ||
229 | + var name; | ||
230 | + if (datasource.name && datasource.name.length) { | ||
231 | + name = datasource.name; | ||
232 | + } else { | ||
233 | + functionIndex++; | ||
234 | + name = aliasCtrl.types.datasourceType.function; | ||
235 | + if (functionIndex > 1) { | ||
236 | + name += ' ' + functionIndex; | ||
237 | + } | ||
238 | + } | ||
239 | + datasource.name = name; | ||
240 | + datasource.aliasName = name; | ||
241 | + datasource.entityName = name; | ||
242 | + } else if (datasource.unresolvedStateEntity) { | ||
243 | + datasource.name = "Unresolved"; | ||
244 | + datasource.entityName = "Unresolved"; | ||
245 | + } | ||
246 | + datasource.dataKeys.forEach(function(dataKey) { | ||
247 | + if (datasource.generated) { | ||
248 | + dataKey._hash = Math.random(); | ||
249 | + dataKey.color = aliasCtrl.utils.getMaterialColor(index); | ||
250 | + } | ||
251 | + index++; | ||
252 | + }); | ||
253 | + updateDatasourceKeyLabels(datasource); | ||
254 | + }); | ||
255 | + deferred.resolve(datasources); | ||
256 | + }, | ||
257 | + function fail() { | ||
258 | + deferred.reject(); | ||
259 | + } | ||
260 | + ); | ||
261 | + return deferred.promise; | ||
262 | + } | ||
263 | + | ||
264 | + getInstantAliasInfo(aliasId) { | ||
265 | + return this.resolvedAliases[aliasId]; | ||
266 | + } | ||
267 | + | ||
268 | + updateCurrentAliasEntity(aliasId, currentEntity) { | ||
269 | + var aliasInfo = this.resolvedAliases[aliasId]; | ||
270 | + if (aliasInfo) { | ||
271 | + var prevCurrentEntity = aliasInfo.currentEntity; | ||
272 | + if (!angular.equals(currentEntity, prevCurrentEntity)) { | ||
273 | + aliasInfo.currentEntity = currentEntity; | ||
274 | + this.$scope.$broadcast('entityAliasesChanged', [aliasId]); | ||
275 | + } | ||
276 | + } | ||
277 | + } | ||
278 | + | ||
279 | +} |
@@ -30,7 +30,8 @@ function EntityRelationService($http, $q) { | @@ -30,7 +30,8 @@ function EntityRelationService($http, $q) { | ||
30 | findByTo: findByTo, | 30 | findByTo: findByTo, |
31 | findInfoByTo: findInfoByTo, | 31 | findInfoByTo: findInfoByTo, |
32 | findByToAndType: findByToAndType, | 32 | findByToAndType: findByToAndType, |
33 | - findByQuery: findByQuery | 33 | + findByQuery: findByQuery, |
34 | + findInfoByQuery: findInfoByQuery | ||
34 | } | 35 | } |
35 | 36 | ||
36 | return service; | 37 | return service; |
@@ -159,4 +160,15 @@ function EntityRelationService($http, $q) { | @@ -159,4 +160,15 @@ function EntityRelationService($http, $q) { | ||
159 | return deferred.promise; | 160 | return deferred.promise; |
160 | } | 161 | } |
161 | 162 | ||
163 | + function findInfoByQuery(query) { | ||
164 | + var deferred = $q.defer(); | ||
165 | + var url = '/api/relations/info'; | ||
166 | + $http.post(url, query).then(function success(response) { | ||
167 | + deferred.resolve(response.data); | ||
168 | + }, function fail() { | ||
169 | + deferred.reject(); | ||
170 | + }); | ||
171 | + return deferred.promise; | ||
172 | + } | ||
173 | + | ||
162 | } | 174 | } |
@@ -27,10 +27,14 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -27,10 +27,14 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
27 | getEntity: getEntity, | 27 | getEntity: getEntity, |
28 | getEntities: getEntities, | 28 | getEntities: getEntities, |
29 | getEntitiesByNameFilter: getEntitiesByNameFilter, | 29 | getEntitiesByNameFilter: getEntitiesByNameFilter, |
30 | - processEntityAliases: processEntityAliases, | ||
31 | - getEntityKeys: getEntityKeys, | 30 | + resolveAlias: resolveAlias, |
31 | + resolveAliasFilter: resolveAliasFilter, | ||
32 | checkEntityAlias: checkEntityAlias, | 32 | checkEntityAlias: checkEntityAlias, |
33 | - createDatasoucesFromSubscriptionsInfo: createDatasoucesFromSubscriptionsInfo, | 33 | + filterAliasByEntityTypes: filterAliasByEntityTypes, |
34 | + getAliasFilterTypesByEntityTypes: getAliasFilterTypesByEntityTypes, | ||
35 | + prepareAllowedEntityTypesList: prepareAllowedEntityTypesList, | ||
36 | + getEntityKeys: getEntityKeys, | ||
37 | + createDatasourcesFromSubscriptionsInfo: createDatasourcesFromSubscriptionsInfo, | ||
34 | getRelatedEntities: getRelatedEntities, | 38 | getRelatedEntities: getRelatedEntities, |
35 | saveRelatedEntity: saveRelatedEntity, | 39 | saveRelatedEntity: saveRelatedEntity, |
36 | getRelatedEntity: getRelatedEntity, | 40 | getRelatedEntity: getRelatedEntity, |
@@ -173,6 +177,54 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -173,6 +177,54 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
173 | return deferred.promise; | 177 | return deferred.promise; |
174 | } | 178 | } |
175 | 179 | ||
180 | + function getSingleTenantByPageLinkPromise(pageLink) { | ||
181 | + var user = userService.getCurrentUser(); | ||
182 | + var tenantId = user.tenantId; | ||
183 | + var deferred = $q.defer(); | ||
184 | + tenantService.getTenant(tenantId).then( | ||
185 | + function success(tenant) { | ||
186 | + var tenantName = tenant.name; | ||
187 | + var result = { | ||
188 | + data: [], | ||
189 | + nextPageLink: pageLink, | ||
190 | + hasNext: false | ||
191 | + }; | ||
192 | + if (tenantName.toLowerCase().startsWith(pageLink.textSearch)) { | ||
193 | + result.data.push(tenant); | ||
194 | + } | ||
195 | + deferred.resolve(result); | ||
196 | + }, | ||
197 | + function fail() { | ||
198 | + deferred.reject(); | ||
199 | + } | ||
200 | + ); | ||
201 | + return deferred.promise; | ||
202 | + } | ||
203 | + | ||
204 | + function getSingleCustomerByPageLinkPromise(pageLink) { | ||
205 | + var user = userService.getCurrentUser(); | ||
206 | + var customerId = user.customerId; | ||
207 | + var deferred = $q.defer(); | ||
208 | + customerService.getCustomer(customerId).then( | ||
209 | + function success(customer) { | ||
210 | + var customerName = customer.name; | ||
211 | + var result = { | ||
212 | + data: [], | ||
213 | + nextPageLink: pageLink, | ||
214 | + hasNext: false | ||
215 | + }; | ||
216 | + if (customerName.toLowerCase().startsWith(pageLink.textSearch)) { | ||
217 | + result.data.push(customer); | ||
218 | + } | ||
219 | + deferred.resolve(result); | ||
220 | + }, | ||
221 | + function fail() { | ||
222 | + deferred.reject(); | ||
223 | + } | ||
224 | + ); | ||
225 | + return deferred.promise; | ||
226 | + } | ||
227 | + | ||
176 | function getEntitiesByPageLinkPromise(entityType, pageLink, config, subType) { | 228 | function getEntitiesByPageLinkPromise(entityType, pageLink, config, subType) { |
177 | var promise; | 229 | var promise; |
178 | var user = userService.getCurrentUser(); | 230 | var user = userService.getCurrentUser(); |
@@ -193,10 +245,18 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -193,10 +245,18 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
193 | } | 245 | } |
194 | break; | 246 | break; |
195 | case types.entityType.tenant: | 247 | case types.entityType.tenant: |
196 | - promise = tenantService.getTenants(pageLink); | 248 | + if (user.authority === 'TENANT_ADMIN') { |
249 | + promise = getSingleTenantByPageLinkPromise(pageLink); | ||
250 | + } else { | ||
251 | + promise = tenantService.getTenants(pageLink); | ||
252 | + } | ||
197 | break; | 253 | break; |
198 | case types.entityType.customer: | 254 | case types.entityType.customer: |
199 | - promise = customerService.getCustomers(pageLink); | 255 | + if (user.authority === 'CUSTOMER_USER') { |
256 | + promise = getSingleCustomerByPageLinkPromise(pageLink); | ||
257 | + } else { | ||
258 | + promise = customerService.getCustomers(pageLink); | ||
259 | + } | ||
200 | break; | 260 | break; |
201 | case types.entityType.rule: | 261 | case types.entityType.rule: |
202 | promise = ruleService.getAllRules(pageLink); | 262 | promise = ruleService.getAllRules(pageLink); |
@@ -221,17 +281,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -221,17 +281,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
221 | return promise; | 281 | return promise; |
222 | } | 282 | } |
223 | 283 | ||
224 | - function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config, subType) { | ||
225 | - var deferred = $q.defer(); | ||
226 | - var pageLink = {limit: limit, textSearch: entityNameFilter}; | 284 | + function getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred) { |
227 | var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType); | 285 | var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType); |
228 | if (promise) { | 286 | if (promise) { |
229 | promise.then( | 287 | promise.then( |
230 | function success(result) { | 288 | function success(result) { |
231 | - if (result.data && result.data.length > 0) { | ||
232 | - deferred.resolve(result.data); | 289 | + data = data.concat(result.data); |
290 | + if (result.hasNext) { | ||
291 | + pageLink = result.nextPageLink; | ||
292 | + getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred); | ||
233 | } else { | 293 | } else { |
234 | - deferred.resolve(null); | 294 | + if (data && data.length > 0) { |
295 | + deferred.resolve(data); | ||
296 | + } else { | ||
297 | + deferred.resolve(null); | ||
298 | + } | ||
235 | } | 299 | } |
236 | }, | 300 | }, |
237 | function fail() { | 301 | function fail() { |
@@ -241,92 +305,418 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -241,92 +305,418 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
241 | } else { | 305 | } else { |
242 | deferred.resolve(null); | 306 | deferred.resolve(null); |
243 | } | 307 | } |
308 | + } | ||
309 | + | ||
310 | + function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config, subType) { | ||
311 | + var deferred = $q.defer(); | ||
312 | + var pageLink = {limit: limit, textSearch: entityNameFilter}; | ||
313 | + if (limit == -1) { // all | ||
314 | + var data = []; | ||
315 | + pageLink.limit = 100; | ||
316 | + getEntitiesByPageLink(entityType, pageLink, config, subType, data, deferred); | ||
317 | + } else { | ||
318 | + var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType); | ||
319 | + if (promise) { | ||
320 | + promise.then( | ||
321 | + function success(result) { | ||
322 | + if (result.data && result.data.length > 0) { | ||
323 | + deferred.resolve(result.data); | ||
324 | + } else { | ||
325 | + deferred.resolve(null); | ||
326 | + } | ||
327 | + }, | ||
328 | + function fail() { | ||
329 | + deferred.resolve(null); | ||
330 | + } | ||
331 | + ); | ||
332 | + } else { | ||
333 | + deferred.resolve(null); | ||
334 | + } | ||
335 | + } | ||
244 | return deferred.promise; | 336 | return deferred.promise; |
245 | } | 337 | } |
246 | 338 | ||
247 | - function entityToEntityInfo(entityType, entity) { | ||
248 | - return { name: entity.name, entityType: entityType, id: entity.id.id }; | 339 | + function entityToEntityInfo(entity) { |
340 | + return { name: entity.name, entityType: entity.id.entityType, id: entity.id.id }; | ||
249 | } | 341 | } |
250 | 342 | ||
251 | - function entitiesToEntitiesInfo(entityType, entities) { | 343 | + function entityRelationInfoToEntityInfo(entityRelationInfo, direction) { |
344 | + var entityId = direction == types.entitySearchDirection.from ? entityRelationInfo.to : entityRelationInfo.from; | ||
345 | + var name = direction == types.entitySearchDirection.from ? entityRelationInfo.toName : entityRelationInfo.fromName; | ||
346 | + return { | ||
347 | + name: name, | ||
348 | + entityType: entityId.entityType, | ||
349 | + id: entityId.id | ||
350 | + }; | ||
351 | + } | ||
352 | + | ||
353 | + function entitiesToEntitiesInfo(entities) { | ||
252 | var entitiesInfo = []; | 354 | var entitiesInfo = []; |
253 | for (var d = 0; d < entities.length; d++) { | 355 | for (var d = 0; d < entities.length; d++) { |
254 | - entitiesInfo.push(entityToEntityInfo(entityType, entities[d])); | 356 | + entitiesInfo.push(entityToEntityInfo(entities[d])); |
255 | } | 357 | } |
256 | return entitiesInfo; | 358 | return entitiesInfo; |
257 | } | 359 | } |
258 | 360 | ||
259 | - function processEntityAlias(index, aliasIds, entityAliases, resolution, deferred) { | ||
260 | - if (index < aliasIds.length) { | ||
261 | - var aliasId = aliasIds[index]; | ||
262 | - var entityAlias = entityAliases[aliasId]; | ||
263 | - var alias = entityAlias.alias; | ||
264 | - var entityFilter = entityAlias.entityFilter; | ||
265 | - if (entityFilter.useFilter) { | ||
266 | - var entityNameFilter = entityFilter.entityNameFilter; | ||
267 | - getEntitiesByNameFilter(entityAlias.entityType, entityNameFilter, 100).then( | ||
268 | - function(entities) { | ||
269 | - if (entities && entities != null) { | ||
270 | - var resolvedAlias = {alias: alias, entityType: entityAlias.entityType, entityId: entities[0].id.id}; | ||
271 | - resolution.aliasesInfo.entityAliases[aliasId] = resolvedAlias; | ||
272 | - resolution.aliasesInfo.entityAliasesInfo[aliasId] = entitiesToEntitiesInfo(entityAlias.entityType, entities); | ||
273 | - index++; | ||
274 | - processEntityAlias(index, aliasIds, entityAliases, resolution, deferred); | 361 | + function entityRelationInfosToEntitiesInfo(entityRelations, direction) { |
362 | + var entitiesInfo = []; | ||
363 | + for (var d = 0; d < entityRelations.length; d++) { | ||
364 | + entitiesInfo.push(entityRelationInfoToEntityInfo(entityRelations[d], direction)); | ||
365 | + } | ||
366 | + return entitiesInfo; | ||
367 | + } | ||
368 | + | ||
369 | + | ||
370 | + function resolveAlias(entityAlias, stateParams) { | ||
371 | + var deferred = $q.defer(); | ||
372 | + var filter = entityAlias.filter; | ||
373 | + resolveAliasFilter(filter, stateParams, -1).then( | ||
374 | + function (result) { | ||
375 | + var aliasInfo = { | ||
376 | + alias: entityAlias.alias, | ||
377 | + stateEntity: result.stateEntity, | ||
378 | + resolveMultiple: filter.resolveMultiple | ||
379 | + }; | ||
380 | + aliasInfo.resolvedEntities = result.entities; | ||
381 | + aliasInfo.currentEntity = null; | ||
382 | + if (aliasInfo.resolvedEntities.length) { | ||
383 | + aliasInfo.currentEntity = aliasInfo.resolvedEntities[0]; | ||
384 | + } | ||
385 | + deferred.resolve(aliasInfo); | ||
386 | + }, | ||
387 | + function fail() { | ||
388 | + deferred.reject(); | ||
389 | + } | ||
390 | + ); | ||
391 | + return deferred.promise; | ||
392 | + } | ||
393 | + | ||
394 | + function resolveAliasFilter(filter, stateParams, maxItems) { | ||
395 | + var deferred = $q.defer(); | ||
396 | + var result = { | ||
397 | + entities: [], | ||
398 | + stateEntity: false | ||
399 | + }; | ||
400 | + switch (filter.type) { | ||
401 | + case types.aliasFilterType.entityList.value: | ||
402 | + getEntities(filter.entityType, filter.entityList).then( | ||
403 | + function success(entities) { | ||
404 | + if (entities && entities.length) { | ||
405 | + result.entities = entitiesToEntitiesInfo(entities); | ||
406 | + deferred.resolve(result); | ||
275 | } else { | 407 | } else { |
276 | - if (!resolution.error) { | ||
277 | - resolution.error = 'dashboard.invalid-aliases-config'; | ||
278 | - } | ||
279 | - index++; | ||
280 | - processEntityAlias(index, aliasIds, entityAliases, resolution, deferred); | 408 | + deferred.reject(); |
281 | } | 409 | } |
282 | - }); | ||
283 | - } else { | ||
284 | - var entityList = entityFilter.entityList; | ||
285 | - getEntities(entityAlias.entityType, entityList).then( | 410 | + }, |
411 | + function fail() { | ||
412 | + deferred.reject(); | ||
413 | + } | ||
414 | + ); | ||
415 | + break; | ||
416 | + case types.aliasFilterType.entityName.value: | ||
417 | + getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems).then( | ||
286 | function success(entities) { | 418 | function success(entities) { |
287 | - if (entities && entities.length > 0) { | ||
288 | - var resolvedAlias = {alias: alias, entityType: entityAlias.entityType, entityId: entities[0].id.id}; | ||
289 | - resolution.aliasesInfo.entityAliases[aliasId] = resolvedAlias; | ||
290 | - resolution.aliasesInfo.entityAliasesInfo[aliasId] = entitiesToEntitiesInfo(entityAlias.entityType, entities); | ||
291 | - index++; | ||
292 | - processEntityAlias(index, aliasIds, entityAliases, resolution, deferred); | 419 | + if (entities && entities.length) { |
420 | + result.entities = entitiesToEntitiesInfo(entities); | ||
421 | + deferred.resolve(result); | ||
293 | } else { | 422 | } else { |
294 | - if (!resolution.error) { | ||
295 | - resolution.error = 'dashboard.invalid-aliases-config'; | ||
296 | - } | ||
297 | - index++; | ||
298 | - processEntityAlias(index, aliasIds, entityAliases, resolution, deferred); | 423 | + deferred.reject(); |
424 | + } | ||
425 | + }, | ||
426 | + function fail() { | ||
427 | + deferred.reject(); | ||
428 | + } | ||
429 | + ); | ||
430 | + break; | ||
431 | + case types.aliasFilterType.stateEntity.value: | ||
432 | + result.stateEntity = true; | ||
433 | + if (stateParams && stateParams.entityId) { | ||
434 | + getEntity(stateParams.entityId.entityType, stateParams.entityId.id).then( | ||
435 | + function success(entity) { | ||
436 | + result.entities = entitiesToEntitiesInfo([entity]); | ||
437 | + deferred.resolve(result); | ||
438 | + }, | ||
439 | + function fail() { | ||
440 | + deferred.reject(); | ||
441 | + } | ||
442 | + ); | ||
443 | + } else { | ||
444 | + deferred.resolve(result); | ||
445 | + } | ||
446 | + break; | ||
447 | + case types.aliasFilterType.assetType.value: | ||
448 | + getEntitiesByNameFilter(types.entityType.asset, filter.assetNameFilter, maxItems, null, filter.assetType).then( | ||
449 | + function success(entities) { | ||
450 | + if (entities && entities.length) { | ||
451 | + result.entities = entitiesToEntitiesInfo(entities); | ||
452 | + deferred.resolve(result); | ||
453 | + } else { | ||
454 | + deferred.reject(); | ||
299 | } | 455 | } |
300 | }, | 456 | }, |
301 | function fail() { | 457 | function fail() { |
302 | - if (!resolution.error) { | ||
303 | - resolution.error = 'dashboard.invalid-aliases-config'; | 458 | + deferred.reject(); |
459 | + } | ||
460 | + ); | ||
461 | + break; | ||
462 | + case types.aliasFilterType.deviceType.value: | ||
463 | + getEntitiesByNameFilter(types.entityType.device, filter.deviceNameFilter, maxItems, null, filter.deviceType).then( | ||
464 | + function success(entities) { | ||
465 | + if (entities && entities.length) { | ||
466 | + result.entities = entitiesToEntitiesInfo(entities); | ||
467 | + deferred.resolve(result); | ||
468 | + } else { | ||
469 | + deferred.reject(); | ||
304 | } | 470 | } |
305 | - index++; | ||
306 | - processEntityAlias(index, aliasIds, entityAliases, resolution, deferred); | 471 | + }, |
472 | + function fail() { | ||
473 | + deferred.reject(); | ||
307 | } | 474 | } |
308 | ); | 475 | ); |
476 | + break; | ||
477 | + case types.aliasFilterType.relationsQuery.value: | ||
478 | + result.stateEntity = filter.rootStateEntity; | ||
479 | + var rootEntityType; | ||
480 | + var rootEntityId; | ||
481 | + if (result.stateEntity && stateParams && stateParams.entityId) { | ||
482 | + rootEntityType = stateParams.entityId.entityType; | ||
483 | + rootEntityId = stateParams.entityId.id; | ||
484 | + } else if (!result.stateEntity) { | ||
485 | + rootEntityType = filter.rootEntity.entityType; | ||
486 | + rootEntityId = filter.rootEntity.id; | ||
487 | + } | ||
488 | + if (rootEntityType && rootEntityId) { | ||
489 | + var searchQuery = { | ||
490 | + parameters: { | ||
491 | + rootId: rootEntityId, | ||
492 | + rootType: rootEntityType, | ||
493 | + direction: filter.direction | ||
494 | + }, | ||
495 | + filters: filter.filters | ||
496 | + }; | ||
497 | + searchQuery.parameters.maxLevel = filter.maxLevel && filter.maxLevel > 0 ? filter.maxLevel : -1; | ||
498 | + entityRelationService.findInfoByQuery(searchQuery).then( | ||
499 | + function success(allRelations) { | ||
500 | + if (allRelations && allRelations.length) { | ||
501 | + if (angular.isDefined(maxItems) && maxItems > 0) { | ||
502 | + var limit = Math.min(allRelations.length, maxItems); | ||
503 | + allRelations.length = limit; | ||
504 | + } | ||
505 | + result.entities = entityRelationInfosToEntitiesInfo(allRelations, filter.direction); | ||
506 | + deferred.resolve(result); | ||
507 | + } else { | ||
508 | + deferred.reject(); | ||
509 | + } | ||
510 | + }, | ||
511 | + function fail() { | ||
512 | + deferred.reject(); | ||
513 | + } | ||
514 | + ); | ||
515 | + } else { | ||
516 | + deferred.resolve(result); | ||
517 | + } | ||
518 | + break; | ||
519 | + case types.aliasFilterType.assetSearchQuery.value: | ||
520 | + case types.aliasFilterType.deviceSearchQuery.value: | ||
521 | + result.stateEntity = filter.rootStateEntity; | ||
522 | + if (result.stateEntity && stateParams && stateParams.entityId) { | ||
523 | + rootEntityType = stateParams.entityId.entityType; | ||
524 | + rootEntityId = stateParams.entityId.id; | ||
525 | + } else if (!result.stateEntity) { | ||
526 | + rootEntityType = filter.rootEntity.entityType; | ||
527 | + rootEntityId = filter.rootEntity.id; | ||
528 | + } | ||
529 | + if (rootEntityType && rootEntityId) { | ||
530 | + searchQuery = { | ||
531 | + parameters: { | ||
532 | + rootId: rootEntityId, | ||
533 | + rootType: rootEntityType, | ||
534 | + direction: filter.direction | ||
535 | + }, | ||
536 | + relationType: filter.relationType | ||
537 | + }; | ||
538 | + searchQuery.parameters.maxLevel = filter.maxLevel && filter.maxLevel > 0 ? filter.maxLevel : -1; | ||
539 | + var findByQueryPromise; | ||
540 | + if (filter.type == types.aliasFilterType.assetSearchQuery.value) { | ||
541 | + searchQuery.assetTypes = filter.assetTypes; | ||
542 | + findByQueryPromise = assetService.findByQuery(searchQuery, false); | ||
543 | + } else if (filter.type == types.aliasFilterType.deviceSearchQuery.value) { | ||
544 | + searchQuery.deviceTypes = filter.deviceTypes; | ||
545 | + findByQueryPromise = deviceService.findByQuery(searchQuery, false); | ||
546 | + } | ||
547 | + findByQueryPromise.then( | ||
548 | + function success(entities) { | ||
549 | + if (entities && entities.length) { | ||
550 | + if (angular.isDefined(maxItems) && maxItems > 0) { | ||
551 | + var limit = Math.min(entities.length, maxItems); | ||
552 | + entities.length = limit; | ||
553 | + } | ||
554 | + result.entities = entitiesToEntitiesInfo(entities); | ||
555 | + deferred.resolve(result); | ||
556 | + } else { | ||
557 | + deferred.reject(); | ||
558 | + } | ||
559 | + }, | ||
560 | + function fail() { | ||
561 | + deferred.reject(); | ||
562 | + } | ||
563 | + ); | ||
564 | + } else { | ||
565 | + deferred.resolve(result); | ||
566 | + } | ||
567 | + break; | ||
568 | + } | ||
569 | + return deferred.promise; | ||
570 | + } | ||
571 | + | ||
572 | + function filterAliasByEntityTypes(entityAlias, entityTypes) { | ||
573 | + var filter = entityAlias.filter; | ||
574 | + if (filterAliasFilterTypeByEntityTypes(filter.type, entityTypes)) { | ||
575 | + switch (filter.type) { | ||
576 | + case types.aliasFilterType.entityList.value: | ||
577 | + return entityTypes.indexOf(filter.entityType) > -1 ? true : false; | ||
578 | + case types.aliasFilterType.entityName.value: | ||
579 | + return entityTypes.indexOf(filter.entityType) > -1 ? true : false; | ||
580 | + case types.aliasFilterType.stateEntity.value: | ||
581 | + return true; | ||
582 | + case types.aliasFilterType.assetType.value: | ||
583 | + return entityTypes.indexOf(types.entityType.asset) > -1 ? true : false; | ||
584 | + case types.aliasFilterType.deviceType.value: | ||
585 | + return entityTypes.indexOf(types.entityType.device) > -1 ? true : false; | ||
586 | + case types.aliasFilterType.relationsQuery.value: | ||
587 | + if (filter.filters && filter.filters.length) { | ||
588 | + var match = false; | ||
589 | + for (var f=0;f<filter.filters.length;f++) { | ||
590 | + var relationFilter = filter.filters[f]; | ||
591 | + if (relationFilter.entityTypes && relationFilter.entityTypes.length) { | ||
592 | + for (var et=0;et<relationFilter.entityTypes.length;et++) { | ||
593 | + if (entityTypes.indexOf(relationFilter.entityTypes[et]) > -1) { | ||
594 | + match = true; | ||
595 | + break; | ||
596 | + } | ||
597 | + } | ||
598 | + } else { | ||
599 | + match = true; | ||
600 | + break; | ||
601 | + } | ||
602 | + } | ||
603 | + return match; | ||
604 | + } else { | ||
605 | + return true; | ||
606 | + } | ||
607 | + case types.aliasFilterType.assetSearchQuery.value: | ||
608 | + return entityTypes.indexOf(types.entityType.asset) > -1 ? true : false; | ||
609 | + case types.aliasFilterType.deviceSearchQuery.value: | ||
610 | + return entityTypes.indexOf(types.entityType.device) > -1 ? true : false; | ||
309 | } | 611 | } |
310 | - } else { | ||
311 | - deferred.resolve(resolution); | ||
312 | } | 612 | } |
613 | + return false; | ||
313 | } | 614 | } |
314 | 615 | ||
315 | - function processEntityAliases(entityAliases) { | ||
316 | - var deferred = $q.defer(); | ||
317 | - var resolution = { | ||
318 | - aliasesInfo: { | ||
319 | - entityAliases: {}, | ||
320 | - entityAliasesInfo: {} | 616 | + function filterAliasFilterTypeByEntityType(aliasFilterType, entityType) { |
617 | + switch (aliasFilterType) { | ||
618 | + case types.aliasFilterType.entityList.value: | ||
619 | + return true; | ||
620 | + case types.aliasFilterType.entityName.value: | ||
621 | + return true; | ||
622 | + case types.aliasFilterType.stateEntity.value: | ||
623 | + return true; | ||
624 | + case types.aliasFilterType.assetType.value: | ||
625 | + return entityType === types.entityType.asset; | ||
626 | + case types.aliasFilterType.deviceType.value: | ||
627 | + return entityType === types.entityType.device; | ||
628 | + case types.aliasFilterType.relationsQuery.value: | ||
629 | + return true; | ||
630 | + case types.aliasFilterType.assetSearchQuery.value: | ||
631 | + return entityType === types.entityType.asset; | ||
632 | + case types.aliasFilterType.deviceSearchQuery.value: | ||
633 | + return entityType === types.entityType.device; | ||
634 | + } | ||
635 | + return false; | ||
636 | + } | ||
637 | + | ||
638 | + function filterAliasFilterTypeByEntityTypes(aliasFilterType, entityTypes) { | ||
639 | + if (!entityTypes || !entityTypes.length) { | ||
640 | + return true; | ||
641 | + } | ||
642 | + var valid = false; | ||
643 | + entityTypes.forEach(function(entityType) { | ||
644 | + valid = valid || filterAliasFilterTypeByEntityType(aliasFilterType, entityType); | ||
645 | + }); | ||
646 | + return valid; | ||
647 | + } | ||
648 | + | ||
649 | + function getAliasFilterTypesByEntityTypes(entityTypes) { | ||
650 | + var allAliasFilterTypes = types.aliasFilterType; | ||
651 | + if (!entityTypes || !entityTypes.length) { | ||
652 | + return allAliasFilterTypes; | ||
653 | + } | ||
654 | + var result = {}; | ||
655 | + for (var type in allAliasFilterTypes) { | ||
656 | + var aliasFilterType = allAliasFilterTypes[type]; | ||
657 | + if (filterAliasFilterTypeByEntityTypes(aliasFilterType.value, entityTypes)) { | ||
658 | + result[type] = aliasFilterType; | ||
321 | } | 659 | } |
322 | - }; | ||
323 | - var aliasIds = []; | ||
324 | - if (entityAliases) { | ||
325 | - for (var aliasId in entityAliases) { | ||
326 | - aliasIds.push(aliasId); | 660 | + } |
661 | + return result; | ||
662 | + } | ||
663 | + | ||
664 | + function prepareAllowedEntityTypesList(allowedEntityTypes) { | ||
665 | + var authority = userService.getAuthority(); | ||
666 | + var entityTypes = {}; | ||
667 | + switch(authority) { | ||
668 | + case 'SYS_ADMIN': | ||
669 | + entityTypes.tenant = types.entityType.tenant; | ||
670 | + entityTypes.rule = types.entityType.rule; | ||
671 | + entityTypes.plugin = types.entityType.plugin; | ||
672 | + break; | ||
673 | + case 'TENANT_ADMIN': | ||
674 | + entityTypes.device = types.entityType.device; | ||
675 | + entityTypes.asset = types.entityType.asset; | ||
676 | + entityTypes.tenant = types.entityType.tenant; | ||
677 | + entityTypes.customer = types.entityType.customer; | ||
678 | + entityTypes.rule = types.entityType.rule; | ||
679 | + entityTypes.plugin = types.entityType.plugin; | ||
680 | + entityTypes.dashboard = types.entityType.dashboard; | ||
681 | + break; | ||
682 | + case 'CUSTOMER_USER': | ||
683 | + entityTypes.device = types.entityType.device; | ||
684 | + entityTypes.asset = types.entityType.asset; | ||
685 | + entityTypes.customer = types.entityType.customer; | ||
686 | + entityTypes.dashboard = types.entityType.dashboard; | ||
687 | + break; | ||
688 | + } | ||
689 | + | ||
690 | + if (allowedEntityTypes) { | ||
691 | + for (var entityType in entityTypes) { | ||
692 | + if (allowedEntityTypes.indexOf(entityTypes[entityType]) === -1) { | ||
693 | + delete entityTypes[entityType]; | ||
694 | + } | ||
327 | } | 695 | } |
328 | } | 696 | } |
329 | - processEntityAlias(0, aliasIds, entityAliases, resolution, deferred); | 697 | + return entityTypes; |
698 | + } | ||
699 | + | ||
700 | + | ||
701 | + function checkEntityAlias(entityAlias) { | ||
702 | + var deferred = $q.defer(); | ||
703 | + resolveAliasFilter(entityAlias.filter, null, 1).then( | ||
704 | + function success(result) { | ||
705 | + if (result.stateEntity) { | ||
706 | + deferred.resolve(true); | ||
707 | + } else { | ||
708 | + var entities = result.entities; | ||
709 | + if (entities && entities.length) { | ||
710 | + deferred.resolve(true); | ||
711 | + } else { | ||
712 | + deferred.resolve(false); | ||
713 | + } | ||
714 | + } | ||
715 | + }, | ||
716 | + function fail() { | ||
717 | + deferred.resolve(false); | ||
718 | + } | ||
719 | + ); | ||
330 | return deferred.promise; | 720 | return deferred.promise; |
331 | } | 721 | } |
332 | 722 | ||
@@ -354,40 +744,13 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -354,40 +744,13 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
354 | } | 744 | } |
355 | } | 745 | } |
356 | deferred.resolve(result); | 746 | deferred.resolve(result); |
357 | - }, function fail(response) { | ||
358 | - deferred.reject(response.data); | 747 | + }, function fail() { |
748 | + deferred.reject(); | ||
359 | }); | 749 | }); |
360 | return deferred.promise; | 750 | return deferred.promise; |
361 | } | 751 | } |
362 | 752 | ||
363 | - function checkEntityAlias(entityAlias) { | ||
364 | - var deferred = $q.defer(); | ||
365 | - var entityType = entityAlias.entityType; | ||
366 | - var entityFilter = entityAlias.entityFilter; | ||
367 | - var promise; | ||
368 | - if (entityFilter.useFilter) { | ||
369 | - var entityNameFilter = entityFilter.entityNameFilter; | ||
370 | - promise = getEntitiesByNameFilter(entityType, entityNameFilter, 1); | ||
371 | - } else { | ||
372 | - var entityList = entityFilter.entityList; | ||
373 | - promise = getEntities(entityType, entityList); | ||
374 | - } | ||
375 | - promise.then( | ||
376 | - function success(entities) { | ||
377 | - if (entities && entities.length > 0) { | ||
378 | - deferred.resolve(true); | ||
379 | - } else { | ||
380 | - deferred.resolve(false); | ||
381 | - } | ||
382 | - }, | ||
383 | - function fail() { | ||
384 | - deferred.resolve(false); | ||
385 | - } | ||
386 | - ); | ||
387 | - return deferred.promise; | ||
388 | - } | ||
389 | - | ||
390 | - function createDatasoucesFromSubscriptionsInfo(subscriptionsInfo) { | 753 | + function createDatasourcesFromSubscriptionsInfo(subscriptionsInfo) { |
391 | var deferred = $q.defer(); | 754 | var deferred = $q.defer(); |
392 | var datasources = []; | 755 | var datasources = []; |
393 | processSubscriptionsInfo(0, subscriptionsInfo, datasources, deferred); | 756 | processSubscriptionsInfo(0, subscriptionsInfo, datasources, deferred); |
@@ -822,4 +1185,4 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -822,4 +1185,4 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
822 | } | 1185 | } |
823 | } | 1186 | } |
824 | 1187 | ||
825 | -} | ||
1188 | +} |
@@ -39,6 +39,9 @@ export default class Subscription { | @@ -39,6 +39,9 @@ export default class Subscription { | ||
39 | this.cafs = {}; | 39 | this.cafs = {}; |
40 | this.registrations = []; | 40 | this.registrations = []; |
41 | 41 | ||
42 | + var subscription = this; | ||
43 | + var deferred = this.ctx.$q.defer(); | ||
44 | + | ||
42 | if (this.type === this.ctx.types.widgetType.rpc.value) { | 45 | if (this.type === this.ctx.types.widgetType.rpc.value) { |
43 | this.callbacks.rpcStateChanged = this.callbacks.rpcStateChanged || function(){}; | 46 | this.callbacks.rpcStateChanged = this.callbacks.rpcStateChanged || function(){}; |
44 | this.callbacks.onRpcSuccess = this.callbacks.onRpcSuccess || function(){}; | 47 | this.callbacks.onRpcSuccess = this.callbacks.onRpcSuccess || function(){}; |
@@ -56,7 +59,11 @@ export default class Subscription { | @@ -56,7 +59,11 @@ export default class Subscription { | ||
56 | this.rpcEnabled = false; | 59 | this.rpcEnabled = false; |
57 | this.executingRpcRequest = false; | 60 | this.executingRpcRequest = false; |
58 | this.executingPromises = []; | 61 | this.executingPromises = []; |
59 | - this.initRpc(); | 62 | + this.initRpc().then( |
63 | + function() { | ||
64 | + deferred.resolve(subscription); | ||
65 | + } | ||
66 | + ); | ||
60 | } else { | 67 | } else { |
61 | this.callbacks.onDataUpdated = this.callbacks.onDataUpdated || function(){}; | 68 | this.callbacks.onDataUpdated = this.callbacks.onDataUpdated || function(){}; |
62 | this.callbacks.onDataUpdateError = this.callbacks.onDataUpdateError || function(){}; | 69 | this.callbacks.onDataUpdateError = this.callbacks.onDataUpdateError || function(){}; |
@@ -66,6 +73,15 @@ export default class Subscription { | @@ -66,6 +73,15 @@ export default class Subscription { | ||
66 | 73 | ||
67 | this.datasources = this.ctx.utils.validateDatasources(options.datasources); | 74 | this.datasources = this.ctx.utils.validateDatasources(options.datasources); |
68 | this.datasourceListeners = []; | 75 | this.datasourceListeners = []; |
76 | + | ||
77 | + /* | ||
78 | + * data = array of datasourceData | ||
79 | + * datasourceData = { | ||
80 | + * tbDatasource, | ||
81 | + * dataKey, { name, config } | ||
82 | + * data = array of [time, value] | ||
83 | + * } | ||
84 | + */ | ||
69 | this.data = []; | 85 | this.data = []; |
70 | this.hiddenData = []; | 86 | this.hiddenData = []; |
71 | this.originalTimewindow = null; | 87 | this.originalTimewindow = null; |
@@ -103,11 +119,41 @@ export default class Subscription { | @@ -103,11 +119,41 @@ export default class Subscription { | ||
103 | this.legendConfig.showMax === true || | 119 | this.legendConfig.showMax === true || |
104 | this.legendConfig.showAvg === true || | 120 | this.legendConfig.showAvg === true || |
105 | this.legendConfig.showTotal === true); | 121 | this.legendConfig.showTotal === true); |
106 | - this.initDataSubscription(); | 122 | + this.initDataSubscription().then( |
123 | + function success() { | ||
124 | + deferred.resolve(subscription); | ||
125 | + }, | ||
126 | + function fail() { | ||
127 | + deferred.reject(); | ||
128 | + } | ||
129 | + ); | ||
107 | } | 130 | } |
131 | + | ||
132 | + return deferred.promise; | ||
108 | } | 133 | } |
109 | 134 | ||
110 | initDataSubscription() { | 135 | initDataSubscription() { |
136 | + var deferred = this.ctx.$q.defer(); | ||
137 | + if (!this.ctx.aliasController) { | ||
138 | + this.configureData(); | ||
139 | + deferred.resolve(); | ||
140 | + } else { | ||
141 | + var subscription = this; | ||
142 | + this.ctx.aliasController.resolveDatasources(this.datasources).then( | ||
143 | + function success(datasources) { | ||
144 | + subscription.datasources = datasources; | ||
145 | + subscription.configureData(); | ||
146 | + deferred.resolve(); | ||
147 | + }, | ||
148 | + function fail() { | ||
149 | + deferred.reject(); | ||
150 | + } | ||
151 | + ); | ||
152 | + } | ||
153 | + return deferred.promise; | ||
154 | + } | ||
155 | + | ||
156 | + configureData() { | ||
111 | var dataIndex = 0; | 157 | var dataIndex = 0; |
112 | for (var i = 0; i < this.datasources.length; i++) { | 158 | for (var i = 0; i < this.datasources.length; i++) { |
113 | var datasource = this.datasources[i]; | 159 | var datasource = this.datasources[i]; |
@@ -199,21 +245,46 @@ export default class Subscription { | @@ -199,21 +245,46 @@ export default class Subscription { | ||
199 | } | 245 | } |
200 | 246 | ||
201 | initRpc() { | 247 | initRpc() { |
248 | + var deferred = this.ctx.$q.defer(); | ||
202 | if (this.targetDeviceAliasIds && this.targetDeviceAliasIds.length > 0) { | 249 | if (this.targetDeviceAliasIds && this.targetDeviceAliasIds.length > 0) { |
203 | this.targetDeviceAliasId = this.targetDeviceAliasIds[0]; | 250 | this.targetDeviceAliasId = this.targetDeviceAliasIds[0]; |
204 | - if (this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId]) { | ||
205 | - this.targetDeviceId = this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId].entityId; | 251 | + var subscription = this; |
252 | + this.ctx.aliasController.getAliasInfo(this.targetDeviceAliasId).then( | ||
253 | + function success(aliasInfo) { | ||
254 | + if (aliasInfo.currentEntity && aliasInfo.currentEntity.entityType == subscription.ctx.types.entityType.device) { | ||
255 | + subscription.targetDeviceId = aliasInfo.currentEntity.id; | ||
256 | + if (subscription.targetDeviceId) { | ||
257 | + subscription.rpcEnabled = true; | ||
258 | + } else { | ||
259 | + subscription.rpcEnabled = subscription.ctx.$scope.widgetEditMode ? true : false; | ||
260 | + } | ||
261 | + subscription.callbacks.rpcStateChanged(subscription); | ||
262 | + deferred.resolve(); | ||
263 | + } else { | ||
264 | + subscription.rpcEnabled = false; | ||
265 | + subscription.callbacks.rpcStateChanged(subscription); | ||
266 | + deferred.resolve(); | ||
267 | + } | ||
268 | + }, | ||
269 | + function fail () { | ||
270 | + subscription.rpcEnabled = false; | ||
271 | + subscription.callbacks.rpcStateChanged(subscription); | ||
272 | + deferred.resolve(); | ||
273 | + } | ||
274 | + ); | ||
275 | + } else { | ||
276 | + if (this.targetDeviceIds && this.targetDeviceIds.length > 0) { | ||
277 | + this.targetDeviceId = this.targetDeviceIds[0]; | ||
206 | } | 278 | } |
207 | - } else if (this.targetDeviceIds && this.targetDeviceIds.length > 0) { | ||
208 | - this.targetDeviceId = this.targetDeviceIds[0]; | ||
209 | - } | ||
210 | - | ||
211 | - if (this.targetDeviceId) { | ||
212 | - this.rpcEnabled = true; | ||
213 | - } else { | ||
214 | - this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false; | 279 | + if (this.targetDeviceId) { |
280 | + this.rpcEnabled = true; | ||
281 | + } else { | ||
282 | + this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false; | ||
283 | + } | ||
284 | + this.callbacks.rpcStateChanged(this); | ||
285 | + deferred.resolve(); | ||
215 | } | 286 | } |
216 | - this.callbacks.rpcStateChanged(this); | 287 | + return deferred.promise; |
217 | } | 288 | } |
218 | 289 | ||
219 | clearRpcError() { | 290 | clearRpcError() { |
@@ -319,11 +390,11 @@ export default class Subscription { | @@ -319,11 +390,11 @@ export default class Subscription { | ||
319 | this.onDataUpdated(); | 390 | this.onDataUpdated(); |
320 | } | 391 | } |
321 | 392 | ||
322 | - onAliasesChanged() { | 393 | + onAliasesChanged(aliasIds) { |
323 | if (this.type === this.ctx.types.widgetType.rpc.value) { | 394 | if (this.type === this.ctx.types.widgetType.rpc.value) { |
324 | - this.checkRpcTarget(); | 395 | + return this.checkRpcTarget(aliasIds); |
325 | } else { | 396 | } else { |
326 | - this.checkSubscriptions(); | 397 | + return this.checkSubscriptions(aliasIds); |
327 | } | 398 | } |
328 | } | 399 | } |
329 | 400 | ||
@@ -481,39 +552,6 @@ export default class Subscription { | @@ -481,39 +552,6 @@ export default class Subscription { | ||
481 | var datasource = this.datasources[i]; | 552 | var datasource = this.datasources[i]; |
482 | if (angular.isFunction(datasource)) | 553 | if (angular.isFunction(datasource)) |
483 | continue; | 554 | continue; |
484 | - var entityId = null; | ||
485 | - var entityType = null; | ||
486 | - if (datasource.type === this.ctx.types.datasourceType.entity) { | ||
487 | - var aliasName = null; | ||
488 | - var entityName = null; | ||
489 | - if (datasource.entityId) { | ||
490 | - entityId = datasource.entityId; | ||
491 | - entityType = datasource.entityType; | ||
492 | - datasource.name = datasource.entityName; | ||
493 | - aliasName = datasource.entityName; | ||
494 | - entityName = datasource.entityName; | ||
495 | - } else if (datasource.entityAliasId) { | ||
496 | - if (this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId]) { | ||
497 | - entityId = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].entityId; | ||
498 | - entityType = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].entityType; | ||
499 | - datasource.name = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].alias; | ||
500 | - aliasName = this.ctx.aliasesInfo.entityAliases[datasource.entityAliasId].alias; | ||
501 | - entityName = ''; | ||
502 | - var entitiesInfo = this.ctx.aliasesInfo.entityAliasesInfo[datasource.entityAliasId]; | ||
503 | - for (var d = 0; d < entitiesInfo.length; d++) { | ||
504 | - if (entitiesInfo[d].id === entityId) { | ||
505 | - entityName = entitiesInfo[d].name; | ||
506 | - break; | ||
507 | - } | ||
508 | - } | ||
509 | - } | ||
510 | - } | ||
511 | - } else { | ||
512 | - datasource.name = datasource.name || this.ctx.types.datasourceType.function; | ||
513 | - } | ||
514 | - for (var dk = 0; dk < datasource.dataKeys.length; dk++) { | ||
515 | - updateDataKeyLabel(datasource.dataKeys[dk], datasource.name, entityName, aliasName); | ||
516 | - } | ||
517 | 555 | ||
518 | var subscription = this; | 556 | var subscription = this; |
519 | 557 | ||
@@ -521,8 +559,8 @@ export default class Subscription { | @@ -521,8 +559,8 @@ export default class Subscription { | ||
521 | subscriptionType: this.type, | 559 | subscriptionType: this.type, |
522 | subscriptionTimewindow: this.subscriptionTimewindow, | 560 | subscriptionTimewindow: this.subscriptionTimewindow, |
523 | datasource: datasource, | 561 | datasource: datasource, |
524 | - entityType: entityType, | ||
525 | - entityId: entityId, | 562 | + entityType: datasource.entityType, |
563 | + entityId: datasource.entityId, | ||
526 | dataUpdated: function (data, datasourceIndex, dataKeyIndex, apply) { | 564 | dataUpdated: function (data, datasourceIndex, dataKeyIndex, apply) { |
527 | subscription.dataUpdated(data, datasourceIndex, dataKeyIndex, apply); | 565 | subscription.dataUpdated(data, datasourceIndex, dataKeyIndex, apply); |
528 | }, | 566 | }, |
@@ -544,6 +582,10 @@ export default class Subscription { | @@ -544,6 +582,10 @@ export default class Subscription { | ||
544 | 582 | ||
545 | this.datasourceListeners.push(listener); | 583 | this.datasourceListeners.push(listener); |
546 | this.ctx.datasourceService.subscribeToDatasource(listener); | 584 | this.ctx.datasourceService.subscribeToDatasource(listener); |
585 | + if (datasource.unresolvedStateEntity) { | ||
586 | + this.notifyDataLoaded(); | ||
587 | + this.onDataUpdated(); | ||
588 | + } | ||
547 | } | 589 | } |
548 | } | 590 | } |
549 | 591 | ||
@@ -557,48 +599,26 @@ export default class Subscription { | @@ -557,48 +599,26 @@ export default class Subscription { | ||
557 | } | 599 | } |
558 | } | 600 | } |
559 | 601 | ||
560 | - checkRpcTarget() { | ||
561 | - var deviceId = null; | ||
562 | - if (this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId]) { | ||
563 | - deviceId = this.ctx.aliasesInfo.entityAliases[this.targetDeviceAliasId].entityId; | ||
564 | - } | ||
565 | - if (!angular.equals(deviceId, this.targetDeviceId)) { | ||
566 | - this.targetDeviceId = deviceId; | ||
567 | - if (this.targetDeviceId) { | ||
568 | - this.rpcEnabled = true; | ||
569 | - } else { | ||
570 | - this.rpcEnabled = this.ctx.$scope.widgetEditMode ? true : false; | ||
571 | - } | ||
572 | - this.callbacks.rpcStateChanged(this); | 602 | + checkRpcTarget(aliasIds) { |
603 | + if (aliasIds.indexOf(this.targetDeviceAliasId) > -1) { | ||
604 | + return true; | ||
605 | + } else { | ||
606 | + return false; | ||
573 | } | 607 | } |
574 | } | 608 | } |
575 | 609 | ||
576 | - checkSubscriptions() { | 610 | + checkSubscriptions(aliasIds) { |
577 | var subscriptionsChanged = false; | 611 | var subscriptionsChanged = false; |
578 | for (var i = 0; i < this.datasourceListeners.length; i++) { | 612 | for (var i = 0; i < this.datasourceListeners.length; i++) { |
579 | var listener = this.datasourceListeners[i]; | 613 | var listener = this.datasourceListeners[i]; |
580 | - var entityId = null; | ||
581 | - var entityType = null; | ||
582 | - var aliasName = null; | ||
583 | - if (listener.datasource.type === this.ctx.types.datasourceType.entity) { | ||
584 | - if (listener.datasource.entityAliasId && | ||
585 | - this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId]) { | ||
586 | - entityId = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].entityId; | ||
587 | - entityType = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].entityType; | ||
588 | - aliasName = this.ctx.aliasesInfo.entityAliases[listener.datasource.entityAliasId].alias; | ||
589 | - } | ||
590 | - if (!angular.equals(entityId, listener.entityId) || | ||
591 | - !angular.equals(entityType, listener.entityType) || | ||
592 | - !angular.equals(aliasName, listener.datasource.name)) { | 614 | + if (listener.datasource.entityAliasId) { |
615 | + if (aliasIds.indexOf(listener.datasource.entityAliasId) > -1) { | ||
593 | subscriptionsChanged = true; | 616 | subscriptionsChanged = true; |
594 | break; | 617 | break; |
595 | } | 618 | } |
596 | } | 619 | } |
597 | } | 620 | } |
598 | - if (subscriptionsChanged) { | ||
599 | - this.unsubscribe(); | ||
600 | - this.subscribe(); | ||
601 | - } | 621 | + return subscriptionsChanged; |
602 | } | 622 | } |
603 | 623 | ||
604 | destroy() { | 624 | destroy() { |
@@ -617,29 +637,6 @@ export default class Subscription { | @@ -617,29 +637,6 @@ export default class Subscription { | ||
617 | 637 | ||
618 | } | 638 | } |
619 | 639 | ||
620 | -const varsRegex = /\$\{([^\}]*)\}/g; | ||
621 | - | ||
622 | -function updateDataKeyLabel(dataKey, dsName, entityName, aliasName) { | ||
623 | - var pattern = dataKey.pattern; | ||
624 | - var label = dataKey.pattern; | ||
625 | - var match = varsRegex.exec(pattern); | ||
626 | - while (match !== null) { | ||
627 | - var variable = match[0]; | ||
628 | - var variableName = match[1]; | ||
629 | - if (variableName === 'dsName') { | ||
630 | - label = label.split(variable).join(dsName); | ||
631 | - } else if (variableName === 'entityName') { | ||
632 | - label = label.split(variable).join(entityName); | ||
633 | - } else if (variableName === 'deviceName') { | ||
634 | - label = label.split(variable).join(entityName); | ||
635 | - } else if (variableName === 'aliasName') { | ||
636 | - label = label.split(variable).join(aliasName); | ||
637 | - } | ||
638 | - match = varsRegex.exec(pattern); | ||
639 | - } | ||
640 | - dataKey.label = label; | ||
641 | -} | ||
642 | - | ||
643 | function calculateMin(data) { | 640 | function calculateMin(data) { |
644 | if (data.length > 0) { | 641 | if (data.length > 0) { |
645 | var result = Number(data[0][1]); | 642 | var result = Number(data[0][1]); |
@@ -48,11 +48,16 @@ | @@ -48,11 +48,16 @@ | ||
48 | disable-attribute-scope-selection="true"> | 48 | disable-attribute-scope-selection="true"> |
49 | </tb-attribute-table> | 49 | </tb-attribute-table> |
50 | </md-tab> | 50 | </md-tab> |
51 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}"> | ||
52 | + <tb-alarm-table flex entity-type="vm.types.entityType.asset" | ||
53 | + entity-id="vm.grid.operatingItem().id.id"> | ||
54 | + </tb-alarm-table> | ||
55 | + </md-tab> | ||
51 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'asset.events' | translate }}"> | 56 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'asset.events' | translate }}"> |
52 | <tb-event-table flex entity-type="vm.types.entityType.asset" | 57 | <tb-event-table flex entity-type="vm.types.entityType.asset" |
53 | entity-id="vm.grid.operatingItem().id.id" | 58 | entity-id="vm.grid.operatingItem().id.id" |
54 | tenant-id="vm.grid.operatingItem().tenantId.id" | 59 | tenant-id="vm.grid.operatingItem().tenantId.id" |
55 | - default-event-type="{{vm.types.eventType.alarm.value}}"> | 60 | + default-event-type="{{vm.types.eventType.error.value}}"> |
56 | </tb-event-table> | 61 | </tb-event-table> |
57 | </md-tab> | 62 | </md-tab> |
58 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> | 63 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> |
@@ -15,7 +15,6 @@ | @@ -15,7 +15,6 @@ | ||
15 | */ | 15 | */ |
16 | import uiRouter from 'angular-ui-router'; | 16 | import uiRouter from 'angular-ui-router'; |
17 | import thingsboardGrid from '../components/grid.directive'; | 17 | import thingsboardGrid from '../components/grid.directive'; |
18 | -import thingsboardEvent from '../event'; | ||
19 | import thingsboardApiUser from '../api/user.service'; | 18 | import thingsboardApiUser from '../api/user.service'; |
20 | import thingsboardApiAsset from '../api/asset.service'; | 19 | import thingsboardApiAsset from '../api/asset.service'; |
21 | import thingsboardApiCustomer from '../api/customer.service'; | 20 | import thingsboardApiCustomer from '../api/customer.service'; |
@@ -29,7 +28,6 @@ import AssetDirective from './asset.directive'; | @@ -29,7 +28,6 @@ import AssetDirective from './asset.directive'; | ||
29 | export default angular.module('thingsboard.asset', [ | 28 | export default angular.module('thingsboard.asset', [ |
30 | uiRouter, | 29 | uiRouter, |
31 | thingsboardGrid, | 30 | thingsboardGrid, |
32 | - thingsboardEvent, | ||
33 | thingsboardApiUser, | 31 | thingsboardApiUser, |
34 | thingsboardApiAsset, | 32 | thingsboardApiAsset, |
35 | thingsboardApiCustomer | 33 | thingsboardApiCustomer |
@@ -23,8 +23,10 @@ function DashboardUtils(types, utils, timeService) { | @@ -23,8 +23,10 @@ function DashboardUtils(types, utils, timeService) { | ||
23 | 23 | ||
24 | var service = { | 24 | var service = { |
25 | validateAndUpdateDashboard: validateAndUpdateDashboard, | 25 | validateAndUpdateDashboard: validateAndUpdateDashboard, |
26 | + validateAndUpdateWidget: validateAndUpdateWidget, | ||
26 | getRootStateId: getRootStateId, | 27 | getRootStateId: getRootStateId, |
27 | createSingleWidgetDashboard: createSingleWidgetDashboard, | 28 | createSingleWidgetDashboard: createSingleWidgetDashboard, |
29 | + createSingleEntityFilter: createSingleEntityFilter, | ||
28 | getStateLayoutsData: getStateLayoutsData, | 30 | getStateLayoutsData: getStateLayoutsData, |
29 | createDefaultState: createDefaultState, | 31 | createDefaultState: createDefaultState, |
30 | createDefaultLayoutData: createDefaultLayoutData, | 32 | createDefaultLayoutData: createDefaultLayoutData, |
@@ -39,40 +41,104 @@ function DashboardUtils(types, utils, timeService) { | @@ -39,40 +41,104 @@ function DashboardUtils(types, utils, timeService) { | ||
39 | 41 | ||
40 | return service; | 42 | return service; |
41 | 43 | ||
42 | - function validateAndUpdateEntityAliases(configuration) { | 44 | + function validateAndUpdateEntityAliases(configuration, datasourcesByAliasId, targetDevicesByAliasId) { |
45 | + var aliasId, entityAlias; | ||
43 | if (angular.isUndefined(configuration.entityAliases)) { | 46 | if (angular.isUndefined(configuration.entityAliases)) { |
44 | configuration.entityAliases = {}; | 47 | configuration.entityAliases = {}; |
45 | if (configuration.deviceAliases) { | 48 | if (configuration.deviceAliases) { |
46 | var deviceAliases = configuration.deviceAliases; | 49 | var deviceAliases = configuration.deviceAliases; |
47 | - for (var aliasId in deviceAliases) { | 50 | + for (aliasId in deviceAliases) { |
48 | var deviceAlias = deviceAliases[aliasId]; | 51 | var deviceAlias = deviceAliases[aliasId]; |
49 | - var alias = deviceAlias.alias; | ||
50 | - var entityFilter = { | ||
51 | - useFilter: false, | ||
52 | - entityNameFilter: '', | ||
53 | - entityList: [] | ||
54 | - } | ||
55 | - if (deviceAlias.deviceFilter) { | ||
56 | - entityFilter.useFilter = deviceAlias.deviceFilter.useFilter; | ||
57 | - entityFilter.entityNameFilter = deviceAlias.deviceFilter.deviceNameFilter; | ||
58 | - entityFilter.entityList = deviceAlias.deviceFilter.deviceList; | ||
59 | - } else if (deviceAlias.deviceId) { | ||
60 | - entityFilter.entityList = [deviceAlias.deviceId]; | ||
61 | - } | ||
62 | - var entityAlias = { | ||
63 | - id: aliasId, | ||
64 | - alias: alias, | ||
65 | - entityType: types.entityType.device, | ||
66 | - entityFilter: entityFilter | ||
67 | - }; | ||
68 | - configuration.entityAliases[aliasId] = entityAlias; | 52 | + entityAlias = validateAndUpdateDeviceAlias(aliasId, deviceAlias, datasourcesByAliasId, targetDevicesByAliasId); |
53 | + configuration.entityAliases[entityAlias.id] = entityAlias; | ||
69 | } | 54 | } |
70 | delete configuration.deviceAliases; | 55 | delete configuration.deviceAliases; |
71 | } | 56 | } |
57 | + } else { | ||
58 | + var entityAliases = configuration.entityAliases; | ||
59 | + for (aliasId in entityAliases) { | ||
60 | + entityAlias = entityAliases[aliasId]; | ||
61 | + entityAlias = validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId); | ||
62 | + if (aliasId != entityAlias.id) { | ||
63 | + delete entityAliases[aliasId]; | ||
64 | + } | ||
65 | + entityAliases[entityAlias.id] = entityAlias; | ||
66 | + } | ||
72 | } | 67 | } |
73 | return configuration; | 68 | return configuration; |
74 | } | 69 | } |
75 | 70 | ||
71 | + function validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId) { | ||
72 | + if (!aliasId || !angular.isString(aliasId) || aliasId.length != 36) { | ||
73 | + var newAliasId = utils.guid(); | ||
74 | + var aliasDatasources = datasourcesByAliasId[aliasId]; | ||
75 | + if (aliasDatasources) { | ||
76 | + aliasDatasources.forEach( | ||
77 | + function(datasource) { | ||
78 | + datasource.entityAliasId = newAliasId; | ||
79 | + } | ||
80 | + ); | ||
81 | + } | ||
82 | + var targetDeviceAliasIdsList = targetDevicesByAliasId[aliasId]; | ||
83 | + if (targetDeviceAliasIdsList) { | ||
84 | + targetDeviceAliasIdsList.forEach( | ||
85 | + function(targetDeviceAliasIds) { | ||
86 | + targetDeviceAliasIds[0] = newAliasId; | ||
87 | + } | ||
88 | + ); | ||
89 | + } | ||
90 | + return newAliasId; | ||
91 | + } else { | ||
92 | + return aliasId; | ||
93 | + } | ||
94 | + } | ||
95 | + | ||
96 | + function validateAndUpdateDeviceAlias(aliasId, deviceAlias, datasourcesByAliasId, targetDevicesByAliasId) { | ||
97 | + aliasId = validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId); | ||
98 | + var alias = deviceAlias.alias; | ||
99 | + var entityAlias = { | ||
100 | + id: aliasId, | ||
101 | + alias: alias, | ||
102 | + filter: { | ||
103 | + type: null, | ||
104 | + entityType: types.entityType.device, | ||
105 | + resolveMultiple: false | ||
106 | + }, | ||
107 | + } | ||
108 | + if (deviceAlias.deviceFilter) { | ||
109 | + entityAlias.filter.type = | ||
110 | + deviceAlias.deviceFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value; | ||
111 | + if (entityAlias.filter.type == types.aliasFilterType.entityList.value) { | ||
112 | + entityAlias.filter.entityList = deviceAlias.deviceFilter.deviceList; | ||
113 | + } else { | ||
114 | + entityAlias.filter.entityNameFilter = deviceAlias.deviceFilter.deviceNameFilter; | ||
115 | + } | ||
116 | + } else { | ||
117 | + entityAlias.filter.type = types.aliasFilterType.entityList.value; | ||
118 | + entityAlias.filter.entityList = [deviceAlias.deviceId]; | ||
119 | + } | ||
120 | + return entityAlias; | ||
121 | + } | ||
122 | + | ||
123 | + function validateAndUpdateEntityAlias(aliasId, entityAlias, datasourcesByAliasId, targetDevicesByAliasId) { | ||
124 | + entityAlias.id = validateAliasId(aliasId, datasourcesByAliasId, targetDevicesByAliasId); | ||
125 | + if (!entityAlias.filter) { | ||
126 | + entityAlias.filter = { | ||
127 | + type: entityAlias.entityFilter.useFilter ? types.aliasFilterType.entityName.value : types.aliasFilterType.entityList.value, | ||
128 | + entityType: entityAlias.entityType, | ||
129 | + resolveMultiple: false | ||
130 | + } | ||
131 | + if (entityAlias.filter.type == types.aliasFilterType.entityList.value) { | ||
132 | + entityAlias.filter.entityList = entityAlias.entityFilter.entityList; | ||
133 | + } else { | ||
134 | + entityAlias.filter.entityNameFilter = entityAlias.entityFilter.entityNameFilter; | ||
135 | + } | ||
136 | + delete entityAlias.entityType; | ||
137 | + delete entityAlias.entityFilter; | ||
138 | + } | ||
139 | + return entityAlias; | ||
140 | + } | ||
141 | + | ||
76 | function validateAndUpdateWidget(widget) { | 142 | function validateAndUpdateWidget(widget) { |
77 | if (!widget.config) { | 143 | if (!widget.config) { |
78 | widget.config = {}; | 144 | widget.config = {}; |
@@ -166,7 +232,34 @@ function DashboardUtils(types, utils, timeService) { | @@ -166,7 +232,34 @@ function DashboardUtils(types, utils, timeService) { | ||
166 | states[firstStateId].root = true; | 232 | states[firstStateId].root = true; |
167 | } | 233 | } |
168 | } | 234 | } |
169 | - dashboard.configuration = validateAndUpdateEntityAliases(dashboard.configuration); | 235 | + |
236 | + var datasourcesByAliasId = {}; | ||
237 | + var targetDevicesByAliasId = {}; | ||
238 | + for (var widgetId in dashboard.configuration.widgets) { | ||
239 | + widget = dashboard.configuration.widgets[widgetId]; | ||
240 | + widget.config.datasources.forEach(function (datasource) { | ||
241 | + if (datasource.entityAliasId) { | ||
242 | + var aliasId = datasource.entityAliasId; | ||
243 | + var aliasDatasources = datasourcesByAliasId[aliasId]; | ||
244 | + if (!aliasDatasources) { | ||
245 | + aliasDatasources = []; | ||
246 | + datasourcesByAliasId[aliasId] = aliasDatasources; | ||
247 | + } | ||
248 | + aliasDatasources.push(datasource); | ||
249 | + } | ||
250 | + }); | ||
251 | + if (widget.config.targetDeviceAliasIds && widget.config.targetDeviceAliasIds.length) { | ||
252 | + var aliasId = widget.config.targetDeviceAliasIds[0]; | ||
253 | + var targetDeviceAliasIdsList = targetDevicesByAliasId[aliasId]; | ||
254 | + if (!targetDeviceAliasIdsList) { | ||
255 | + targetDeviceAliasIdsList = []; | ||
256 | + targetDevicesByAliasId[aliasId] = targetDeviceAliasIdsList; | ||
257 | + } | ||
258 | + targetDeviceAliasIdsList.push(widget.config.targetDeviceAliasIds); | ||
259 | + } | ||
260 | + } | ||
261 | + | ||
262 | + dashboard.configuration = validateAndUpdateEntityAliases(dashboard.configuration, datasourcesByAliasId, targetDevicesByAliasId); | ||
170 | 263 | ||
171 | if (angular.isUndefined(dashboard.configuration.timewindow)) { | 264 | if (angular.isUndefined(dashboard.configuration.timewindow)) { |
172 | dashboard.configuration.timewindow = timeService.defaultTimewindow(); | 265 | dashboard.configuration.timewindow = timeService.defaultTimewindow(); |
@@ -243,6 +336,15 @@ function DashboardUtils(types, utils, timeService) { | @@ -243,6 +336,15 @@ function DashboardUtils(types, utils, timeService) { | ||
243 | return dashboard; | 336 | return dashboard; |
244 | } | 337 | } |
245 | 338 | ||
339 | + function createSingleEntityFilter(entityType, entityId) { | ||
340 | + return { | ||
341 | + type: types.aliasFilterType.entityList.value, | ||
342 | + entityList: [entityId], | ||
343 | + entityType: entityType, | ||
344 | + resolveMultiple: false | ||
345 | + }; | ||
346 | + } | ||
347 | + | ||
246 | function getStateLayoutsData(dashboard, targetState) { | 348 | function getStateLayoutsData(dashboard, targetState) { |
247 | var dashboardConfiguration = dashboard.configuration; | 349 | var dashboardConfiguration = dashboard.configuration; |
248 | var states = dashboardConfiguration.states; | 350 | var states = dashboardConfiguration.states; |
@@ -65,6 +65,69 @@ export default angular.module('thingsboard.types', []) | @@ -65,6 +65,69 @@ export default angular.module('thingsboard.types', []) | ||
65 | clearedUnack: "CLEARED_UNACK", | 65 | clearedUnack: "CLEARED_UNACK", |
66 | clearedAck: "CLEARED_ACK" | 66 | clearedAck: "CLEARED_ACK" |
67 | }, | 67 | }, |
68 | + alarmSearchStatus: { | ||
69 | + any: "ANY", | ||
70 | + active: "ACTIVE", | ||
71 | + cleared: "CLEARED", | ||
72 | + ack: "ACK", | ||
73 | + unack: "UNACK" | ||
74 | + }, | ||
75 | + alarmSeverity: { | ||
76 | + "CRITICAL": { | ||
77 | + name: "alarm.severity-critical", | ||
78 | + class: "tb-critical" | ||
79 | + }, | ||
80 | + "MAJOR": { | ||
81 | + name: "alarm.severity-major", | ||
82 | + class: "tb-major" | ||
83 | + }, | ||
84 | + "MINOR": { | ||
85 | + name: "alarm.severity-minor", | ||
86 | + class: "tb-minor" | ||
87 | + }, | ||
88 | + "WARNING": { | ||
89 | + name: "alarm.severity-warning", | ||
90 | + class: "tb-warning" | ||
91 | + }, | ||
92 | + "INDETERMINATE": { | ||
93 | + name: "alarm.severity-indeterminate", | ||
94 | + class: "tb-indeterminate" | ||
95 | + } | ||
96 | + }, | ||
97 | + aliasFilterType: { | ||
98 | + entityList: { | ||
99 | + value: 'entityList', | ||
100 | + name: 'alias.filter-type-entity-list' | ||
101 | + }, | ||
102 | + entityName: { | ||
103 | + value: 'entityName', | ||
104 | + name: 'alias.filter-type-entity-name' | ||
105 | + }, | ||
106 | + stateEntity: { | ||
107 | + value: 'stateEntity', | ||
108 | + name: 'alias.filter-type-state-entity' | ||
109 | + }, | ||
110 | + assetType: { | ||
111 | + value: 'assetType', | ||
112 | + name: 'alias.filter-type-asset-type' | ||
113 | + }, | ||
114 | + deviceType: { | ||
115 | + value: 'deviceType', | ||
116 | + name: 'alias.filter-type-device-type' | ||
117 | + }, | ||
118 | + relationsQuery: { | ||
119 | + value: 'relationsQuery', | ||
120 | + name: 'alias.filter-type-relations-query' | ||
121 | + }, | ||
122 | + assetSearchQuery: { | ||
123 | + value: 'assetSearchQuery', | ||
124 | + name: 'alias.filter-type-asset-search-query' | ||
125 | + }, | ||
126 | + deviceSearchQuery: { | ||
127 | + value: 'deviceSearchQuery', | ||
128 | + name: 'alias.filter-type-device-search-query' | ||
129 | + } | ||
130 | + }, | ||
68 | position: { | 131 | position: { |
69 | top: { | 132 | top: { |
70 | value: "top", | 133 | value: "top", |
@@ -109,6 +172,62 @@ export default angular.module('thingsboard.types', []) | @@ -109,6 +172,62 @@ export default angular.module('thingsboard.types', []) | ||
109 | dashboard: "DASHBOARD", | 172 | dashboard: "DASHBOARD", |
110 | alarm: "ALARM" | 173 | alarm: "ALARM" |
111 | }, | 174 | }, |
175 | + entityTypeTranslations: { | ||
176 | + "DEVICE": { | ||
177 | + type: 'entity.type-device', | ||
178 | + typePlural: 'entity.type-devices', | ||
179 | + list: 'entity.list-of-devices', | ||
180 | + nameStartsWith: 'entity.device-name-starts-with' | ||
181 | + }, | ||
182 | + "ASSET": { | ||
183 | + type: 'entity.type-asset', | ||
184 | + typePlural: 'entity.type-assets', | ||
185 | + list: 'entity.list-of-assets', | ||
186 | + nameStartsWith: 'entity.asset-name-starts-with' | ||
187 | + }, | ||
188 | + "RULE": { | ||
189 | + type: 'entity.type-rule', | ||
190 | + typePlural: 'entity.type-rules', | ||
191 | + list: 'entity.list-of-rules', | ||
192 | + nameStartsWith: 'entity.rule-name-starts-with' | ||
193 | + }, | ||
194 | + "PLUGIN": { | ||
195 | + type: 'entity.type-plugin', | ||
196 | + typePlural: 'entity.type-plugins', | ||
197 | + list: 'entity.list-of-plugins', | ||
198 | + nameStartsWith: 'entity.plugin-name-starts-with' | ||
199 | + }, | ||
200 | + "TENANT": { | ||
201 | + type: 'entity.type-tenant', | ||
202 | + typePlural: 'entity.type-tenants', | ||
203 | + list: 'entity.list-of-tenants', | ||
204 | + nameStartsWith: 'entity.tenant-name-starts-with' | ||
205 | + }, | ||
206 | + "CUSTOMER": { | ||
207 | + type: 'entity.type-customer', | ||
208 | + typePlural: 'entity.type-customers', | ||
209 | + list: 'entity.list-of-customers', | ||
210 | + nameStartsWith: 'entity.customer-name-starts-with' | ||
211 | + }, | ||
212 | + "USER": { | ||
213 | + type: 'entity.type-user', | ||
214 | + typePlural: 'entity.type-users', | ||
215 | + list: 'entity.list-of-users', | ||
216 | + nameStartsWith: 'entity.user-name-starts-with' | ||
217 | + }, | ||
218 | + "DASHBOARD": { | ||
219 | + type: 'entity.type-dashboard', | ||
220 | + typePlural: 'entity.type-dashboards', | ||
221 | + list: 'entity.list-of-dashboards', | ||
222 | + nameStartsWith: 'entity.dashboard-name-starts-with' | ||
223 | + }, | ||
224 | + "ALARM": { | ||
225 | + type: 'entity.type-alarm', | ||
226 | + typePlural: 'entity.type-alarms', | ||
227 | + list: 'entity.list-of-alarms', | ||
228 | + nameStartsWith: 'entity.alarm-name-starts-with' | ||
229 | + } | ||
230 | + }, | ||
112 | entitySearchDirection: { | 231 | entitySearchDirection: { |
113 | from: "FROM", | 232 | from: "FROM", |
114 | to: "TO" | 233 | to: "TO" |
@@ -118,10 +237,6 @@ export default angular.module('thingsboard.types', []) | @@ -118,10 +237,6 @@ export default angular.module('thingsboard.types', []) | ||
118 | manages: "Manages" | 237 | manages: "Manages" |
119 | }, | 238 | }, |
120 | eventType: { | 239 | eventType: { |
121 | - alarm: { | ||
122 | - value: "ALARM", | ||
123 | - name: "event.type-alarm" | ||
124 | - }, | ||
125 | error: { | 240 | error: { |
126 | value: "ERROR", | 241 | value: "ERROR", |
127 | name: "event.type-error" | 242 | name: "event.type-error" |
@@ -106,10 +106,10 @@ function Utils($mdColorPalette, $rootScope, $window, types) { | @@ -106,10 +106,10 @@ function Utils($mdColorPalette, $rootScope, $window, types) { | ||
106 | isDescriptorSchemaNotEmpty: isDescriptorSchemaNotEmpty, | 106 | isDescriptorSchemaNotEmpty: isDescriptorSchemaNotEmpty, |
107 | filterSearchTextEntities: filterSearchTextEntities, | 107 | filterSearchTextEntities: filterSearchTextEntities, |
108 | guid: guid, | 108 | guid: guid, |
109 | + cleanCopy: cleanCopy, | ||
109 | isLocalUrl: isLocalUrl, | 110 | isLocalUrl: isLocalUrl, |
110 | validateDatasources: validateDatasources, | 111 | validateDatasources: validateDatasources, |
111 | - createKey: createKey, | ||
112 | - entityTypeName: entityTypeName | 112 | + createKey: createKey |
113 | } | 113 | } |
114 | 114 | ||
115 | return service; | 115 | return service; |
@@ -291,6 +291,16 @@ function Utils($mdColorPalette, $rootScope, $window, types) { | @@ -291,6 +291,16 @@ function Utils($mdColorPalette, $rootScope, $window, types) { | ||
291 | s4() + '-' + s4() + s4() + s4(); | 291 | s4() + '-' + s4() + s4() + s4(); |
292 | } | 292 | } |
293 | 293 | ||
294 | + function cleanCopy(object) { | ||
295 | + var copy = angular.copy(object); | ||
296 | + for (var prop in copy) { | ||
297 | + if (prop && prop.startsWith('$$')) { | ||
298 | + delete copy[prop]; | ||
299 | + } | ||
300 | + } | ||
301 | + return copy; | ||
302 | + } | ||
303 | + | ||
294 | function genNextColor(datasources) { | 304 | function genNextColor(datasources) { |
295 | var index = 0; | 305 | var index = 0; |
296 | if (datasources) { | 306 | if (datasources) { |
@@ -347,27 +357,4 @@ function Utils($mdColorPalette, $rootScope, $window, types) { | @@ -347,27 +357,4 @@ function Utils($mdColorPalette, $rootScope, $window, types) { | ||
347 | return dataKey; | 357 | return dataKey; |
348 | } | 358 | } |
349 | 359 | ||
350 | - function entityTypeName (type) { | ||
351 | - switch (type) { | ||
352 | - case types.entityType.device: | ||
353 | - return 'entity.type-device'; | ||
354 | - case types.entityType.asset: | ||
355 | - return 'entity.type-asset'; | ||
356 | - case types.entityType.rule: | ||
357 | - return 'entity.type-rule'; | ||
358 | - case types.entityType.plugin: | ||
359 | - return 'entity.type-plugin'; | ||
360 | - case types.entityType.tenant: | ||
361 | - return 'entity.type-tenant'; | ||
362 | - case types.entityType.customer: | ||
363 | - return 'entity.type-customer'; | ||
364 | - case types.entityType.user: | ||
365 | - return 'entity.type-user'; | ||
366 | - case types.entityType.dashboard: | ||
367 | - return 'entity.type-dashboard'; | ||
368 | - case types.entityType.alarm: | ||
369 | - return 'entity.type-alarm'; | ||
370 | - } | ||
371 | - } | ||
372 | - | ||
373 | } | 360 | } |
@@ -52,7 +52,7 @@ function Dashboard() { | @@ -52,7 +52,7 @@ function Dashboard() { | ||
52 | bindToController: { | 52 | bindToController: { |
53 | widgets: '=', | 53 | widgets: '=', |
54 | widgetLayouts: '=?', | 54 | widgetLayouts: '=?', |
55 | - aliasesInfo: '=', | 55 | + aliasController: '=', |
56 | stateController: '=', | 56 | stateController: '=', |
57 | dashboardTimewindow: '=?', | 57 | dashboardTimewindow: '=?', |
58 | columns: '=', | 58 | columns: '=', |
@@ -85,7 +85,7 @@ function Dashboard() { | @@ -85,7 +85,7 @@ function Dashboard() { | ||
85 | } | 85 | } |
86 | 86 | ||
87 | /*@ngInject*/ | 87 | /*@ngInject*/ |
88 | -function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, timeService, types, utils) { | 88 | +function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, $mdUtil, timeService, types, utils) { |
89 | 89 | ||
90 | var highlightedMode = false; | 90 | var highlightedMode = false; |
91 | var highlightedWidget = null; | 91 | var highlightedWidget = null; |
@@ -329,10 +329,6 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t | @@ -329,10 +329,6 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t | ||
329 | $scope.$broadcast('toggleDashboardEditMode', vm.isEdit); | 329 | $scope.$broadcast('toggleDashboardEditMode', vm.isEdit); |
330 | }); | 330 | }); |
331 | 331 | ||
332 | - $scope.$watch('vm.aliasesInfo.entityAliases', function () { | ||
333 | - $scope.$broadcast('entityAliasListChanged', vm.aliasesInfo); | ||
334 | - }, true); | ||
335 | - | ||
336 | $scope.$on('gridster-resized', function (event, sizes, theGridster) { | 332 | $scope.$on('gridster-resized', function (event, sizes, theGridster) { |
337 | if (checkIsLocalGridsterElement(theGridster)) { | 333 | if (checkIsLocalGridsterElement(theGridster)) { |
338 | vm.gridster = theGridster; | 334 | vm.gridster = theGridster; |
@@ -796,7 +792,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t | @@ -796,7 +792,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t | ||
796 | } | 792 | } |
797 | 793 | ||
798 | function dashboardLoaded() { | 794 | function dashboardLoaded() { |
799 | - $timeout(function () { | 795 | + $mdUtil.nextTick(function () { |
800 | if (vm.dashboardTimewindowWatch) { | 796 | if (vm.dashboardTimewindowWatch) { |
801 | vm.dashboardTimewindowWatch(); | 797 | vm.dashboardTimewindowWatch(); |
802 | vm.dashboardTimewindowWatch = null; | 798 | vm.dashboardTimewindowWatch = null; |
@@ -806,14 +802,27 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t | @@ -806,14 +802,27 @@ function DashboardController($scope, $rootScope, $element, $timeout, $mdMedia, t | ||
806 | }, true); | 802 | }, true); |
807 | adoptMaxRows(); | 803 | adoptMaxRows(); |
808 | vm.dashboardLoading = false; | 804 | vm.dashboardLoading = false; |
809 | - $timeout(function () { | ||
810 | - var gridsterScope = gridsterElement.scope(); | ||
811 | - vm.gridster = gridsterScope.gridster; | ||
812 | - if (vm.onInit) { | ||
813 | - vm.onInit({dashboard: vm}); | 805 | + if ($scope.gridsterScopeWatcher) { |
806 | + $scope.gridsterScopeWatcher(); | ||
807 | + } | ||
808 | + $scope.gridsterScopeWatcher = $scope.$watch( | ||
809 | + function() { | ||
810 | + var hasScope = gridsterElement.scope() ? true : false; | ||
811 | + return hasScope; | ||
812 | + }, | ||
813 | + function(hasScope) { | ||
814 | + if (hasScope) { | ||
815 | + $scope.gridsterScopeWatcher(); | ||
816 | + $scope.gridsterScopeWatcher = null; | ||
817 | + var gridsterScope = gridsterElement.scope(); | ||
818 | + vm.gridster = gridsterScope.gridster; | ||
819 | + if (vm.onInit) { | ||
820 | + vm.onInit({dashboard: vm}); | ||
821 | + } | ||
822 | + } | ||
814 | } | 823 | } |
815 | - }, 0, false); | ||
816 | - }, 0, false); | 824 | + ); |
825 | + }); | ||
817 | } | 826 | } |
818 | 827 | ||
819 | function loading() { | 828 | function loading() { |
@@ -89,7 +89,7 @@ | @@ -89,7 +89,7 @@ | ||
89 | <div flex tb-widget | 89 | <div flex tb-widget |
90 | locals="{ visibleRect: vm.visibleRect, | 90 | locals="{ visibleRect: vm.visibleRect, |
91 | widget: widget, | 91 | widget: widget, |
92 | - aliasesInfo: vm.aliasesInfo, | 92 | + aliasController: vm.aliasController, |
93 | stateController: vm.stateController, | 93 | stateController: vm.stateController, |
94 | isEdit: vm.isEdit, | 94 | isEdit: vm.isEdit, |
95 | stDiff: vm.stDiff, | 95 | stDiff: vm.stDiff, |
@@ -20,14 +20,14 @@ export default angular.module('thingsboard.dialogs.datakeyConfigDialog', [things | @@ -20,14 +20,14 @@ export default angular.module('thingsboard.dialogs.datakeyConfigDialog', [things | ||
20 | .name; | 20 | .name; |
21 | 21 | ||
22 | /*@ngInject*/ | 22 | /*@ngInject*/ |
23 | -function DatakeyConfigDialogController($scope, $mdDialog, entityService, dataKey, dataKeySettingsSchema, entityAlias, entityAliases) { | 23 | +function DatakeyConfigDialogController($scope, $mdDialog, $q, entityService, dataKey, dataKeySettingsSchema, entityAlias, aliasController) { |
24 | 24 | ||
25 | var vm = this; | 25 | var vm = this; |
26 | 26 | ||
27 | vm.dataKey = dataKey; | 27 | vm.dataKey = dataKey; |
28 | vm.dataKeySettingsSchema = dataKeySettingsSchema; | 28 | vm.dataKeySettingsSchema = dataKeySettingsSchema; |
29 | vm.entityAlias = entityAlias; | 29 | vm.entityAlias = entityAlias; |
30 | - vm.entityAliases = entityAliases; | 30 | + vm.aliasController = aliasController; |
31 | 31 | ||
32 | vm.hide = function () { | 32 | vm.hide = function () { |
33 | $mdDialog.hide(); | 33 | $mdDialog.hide(); |
@@ -38,12 +38,28 @@ function DatakeyConfigDialogController($scope, $mdDialog, entityService, dataKey | @@ -38,12 +38,28 @@ function DatakeyConfigDialogController($scope, $mdDialog, entityService, dataKey | ||
38 | }; | 38 | }; |
39 | 39 | ||
40 | vm.fetchEntityKeys = function (entityAliasId, query, type) { | 40 | vm.fetchEntityKeys = function (entityAliasId, query, type) { |
41 | - var alias = vm.entityAliases[entityAliasId]; | ||
42 | - if (alias) { | ||
43 | - return entityService.getEntityKeys(alias.entityType, alias.entityId, query, type); | ||
44 | - } else { | ||
45 | - return []; | ||
46 | - } | 41 | + var deferred = $q.defer(); |
42 | + vm.aliasController.getAliasInfo(entityAliasId).then( | ||
43 | + function success(aliasInfo) { | ||
44 | + var entity = aliasInfo.currentEntity; | ||
45 | + if (entity) { | ||
46 | + entityService.getEntityKeys(entity.entityType, entity.id, query, type).then( | ||
47 | + function success(keys) { | ||
48 | + deferred.resolve(keys); | ||
49 | + }, | ||
50 | + function fail() { | ||
51 | + deferred.resolve([]); | ||
52 | + } | ||
53 | + ); | ||
54 | + } else { | ||
55 | + deferred.resolve([]); | ||
56 | + } | ||
57 | + }, | ||
58 | + function fail() { | ||
59 | + deferred.resolve([]); | ||
60 | + } | ||
61 | + ); | ||
62 | + return deferred.promise; | ||
47 | }; | 63 | }; |
48 | 64 | ||
49 | vm.save = function () { | 65 | vm.save = function () { |
@@ -103,10 +103,9 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc | @@ -103,10 +103,9 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc | ||
103 | ngModelCtrl.$render = function () { | 103 | ngModelCtrl.$render = function () { |
104 | if (ngModelCtrl.$viewValue) { | 104 | if (ngModelCtrl.$viewValue) { |
105 | var entityAliasId = ngModelCtrl.$viewValue.entityAliasId; | 105 | var entityAliasId = ngModelCtrl.$viewValue.entityAliasId; |
106 | - if (scope.entityAliases[entityAliasId]) { | ||
107 | - scope.entityAlias = {id: entityAliasId, alias: scope.entityAliases[entityAliasId].alias, | ||
108 | - entityType: scope.entityAliases[entityAliasId].entityType, | ||
109 | - entityId: scope.entityAliases[entityAliasId].entityId}; | 106 | + var entityAliases = scope.aliasController.getEntityAliases(); |
107 | + if (entityAliases[entityAliasId]) { | ||
108 | + scope.entityAlias = entityAliases[entityAliasId]; | ||
110 | } else { | 109 | } else { |
111 | scope.entityAlias = null; | 110 | scope.entityAlias = null; |
112 | } | 111 | } |
@@ -182,7 +181,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc | @@ -182,7 +181,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc | ||
182 | dataKey: angular.copy(dataKey), | 181 | dataKey: angular.copy(dataKey), |
183 | dataKeySettingsSchema: scope.datakeySettingsSchema, | 182 | dataKeySettingsSchema: scope.datakeySettingsSchema, |
184 | entityAlias: scope.entityAlias, | 183 | entityAlias: scope.entityAlias, |
185 | - entityAliases: scope.entityAliases | 184 | + aliasController: scope.aliasController |
186 | }, | 185 | }, |
187 | parent: angular.element($document[0].body), | 186 | parent: angular.element($document[0].body), |
188 | fullscreen: true, | 187 | fullscreen: true, |
@@ -236,7 +235,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc | @@ -236,7 +235,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc | ||
236 | require: "^ngModel", | 235 | require: "^ngModel", |
237 | scope: { | 236 | scope: { |
238 | widgetType: '=', | 237 | widgetType: '=', |
239 | - entityAliases: '=', | 238 | + aliasController: '=', |
240 | datakeySettingsSchema: '=', | 239 | datakeySettingsSchema: '=', |
241 | generateDataKey: '&', | 240 | generateDataKey: '&', |
242 | fetchEntityKeys: '&', | 241 | fetchEntityKeys: '&', |
@@ -18,7 +18,7 @@ | @@ -18,7 +18,7 @@ | ||
18 | <section flex layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center"> | 18 | <section flex layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center"> |
19 | <tb-entity-alias-select | 19 | <tb-entity-alias-select |
20 | tb-required="true" | 20 | tb-required="true" |
21 | - entity-aliases="entityAliases" | 21 | + alias-controller="aliasController" |
22 | ng-model="entityAlias" | 22 | ng-model="entityAlias" |
23 | on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias})"> | 23 | on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias})"> |
24 | </tb-entity-alias-select> | 24 | </tb-entity-alias-select> |
@@ -71,6 +71,13 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document, | @@ -71,6 +71,13 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document, | ||
71 | } | 71 | } |
72 | }, true); | 72 | }, true); |
73 | 73 | ||
74 | + scope.$watch('datasourceName', function () { | ||
75 | + if (ngModelCtrl.$viewValue) { | ||
76 | + ngModelCtrl.$viewValue.name = scope.datasourceName; | ||
77 | + scope.updateValidity(); | ||
78 | + } | ||
79 | + }); | ||
80 | + | ||
74 | ngModelCtrl.$render = function () { | 81 | ngModelCtrl.$render = function () { |
75 | if (ngModelCtrl.$viewValue) { | 82 | if (ngModelCtrl.$viewValue) { |
76 | var funcDataKeys = []; | 83 | var funcDataKeys = []; |
@@ -78,6 +85,7 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document, | @@ -78,6 +85,7 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document, | ||
78 | funcDataKeys = funcDataKeys.concat(ngModelCtrl.$viewValue.dataKeys); | 85 | funcDataKeys = funcDataKeys.concat(ngModelCtrl.$viewValue.dataKeys); |
79 | } | 86 | } |
80 | scope.funcDataKeys = funcDataKeys; | 87 | scope.funcDataKeys = funcDataKeys; |
88 | + scope.datasourceName = ngModelCtrl.$viewValue.name; | ||
81 | } | 89 | } |
82 | }; | 90 | }; |
83 | 91 |
@@ -15,23 +15,29 @@ | @@ -15,23 +15,29 @@ | ||
15 | */ | 15 | */ |
16 | @import '../../scss/constants'; | 16 | @import '../../scss/constants'; |
17 | 17 | ||
18 | -.tb-func-datakey-autocomplete { | ||
19 | - .tb-not-found { | ||
20 | - display: block; | ||
21 | - line-height: 1.5; | ||
22 | - height: 48px; | ||
23 | - .tb-no-entries { | ||
24 | - line-height: 48px; | ||
25 | - } | 18 | +.tb-datasource-func { |
19 | + @media (min-width: $layout-breakpoint-gt-sm) { | ||
20 | + padding-left: 8px; | ||
26 | } | 21 | } |
27 | - li { | ||
28 | - height: auto !important; | ||
29 | - white-space: normal !important; | 22 | + |
23 | + md-input-container.tb-datasource-name { | ||
24 | + .md-errors-spacer { | ||
25 | + display: none; | ||
26 | + } | ||
30 | } | 27 | } |
31 | -} | ||
32 | 28 | ||
33 | -tb-datasource-func { | ||
34 | - @media (min-width: $layout-breakpoint-gt-sm) { | ||
35 | - padding-left: 8px; | 29 | + .tb-func-datakey-autocomplete { |
30 | + .tb-not-found { | ||
31 | + display: block; | ||
32 | + line-height: 1.5; | ||
33 | + height: 48px; | ||
34 | + .tb-no-entries { | ||
35 | + line-height: 48px; | ||
36 | + } | ||
37 | + } | ||
38 | + li { | ||
39 | + height: auto !important; | ||
40 | + white-space: normal !important; | ||
41 | + } | ||
36 | } | 42 | } |
37 | -} | ||
43 | +} |
@@ -15,59 +15,68 @@ | @@ -15,59 +15,68 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<section flex layout='column' style="padding-left: 4px;"> | ||
19 | - <md-chips flex | ||
20 | - id="function_datakey_chips" | ||
21 | - ng-required="true" | ||
22 | - ng-model="funcDataKeys" md-autocomplete-snap | ||
23 | - md-transform-chip="transformDataKeyChip($chip)" | ||
24 | - md-require-match="false"> | ||
25 | - <md-autocomplete | ||
26 | - md-no-cache="false" | ||
27 | - id="dataKey" | ||
28 | - md-selected-item="selectedDataKey" | ||
29 | - md-search-text="dataKeySearchText" | ||
30 | - md-items="item in dataKeysSearch(dataKeySearchText)" | ||
31 | - md-item-text="item.name" | ||
32 | - md-min-length="0" | ||
33 | - placeholder="{{ 'datakey.function-types' | translate }}" | ||
34 | - md-menu-class="tb-func-datakey-autocomplete"> | ||
35 | - <span md-highlight-text="dataKeySearchText" md-highlight-flags="^i">{{item}}</span> | ||
36 | - <md-not-found> | ||
37 | - <div class="tb-not-found"> | ||
38 | - <div class="tb-no-entries" ng-if="!textIsNotEmpty(dataKeySearchText)"> | ||
39 | - <span translate>device.no-keys-found</span> | 18 | +<section class="tb-datasource-func" flex layout='column' |
19 | + layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center"> | ||
20 | + <md-input-container class="tb-datasource-name" md-no-float style="min-width: 200px;"> | ||
21 | + <input name="datasourceName" | ||
22 | + placeholder="{{ 'datasource.name' | translate }}" | ||
23 | + ng-model="datasourceName" | ||
24 | + aria-label="{{ 'datasource.name' | translate }}"> | ||
25 | + </md-input-container> | ||
26 | + <section flex layout='column' style="padding-left: 4px;"> | ||
27 | + <md-chips flex | ||
28 | + id="function_datakey_chips" | ||
29 | + ng-required="true" | ||
30 | + ng-model="funcDataKeys" md-autocomplete-snap | ||
31 | + md-transform-chip="transformDataKeyChip($chip)" | ||
32 | + md-require-match="false"> | ||
33 | + <md-autocomplete | ||
34 | + md-no-cache="false" | ||
35 | + id="dataKey" | ||
36 | + md-selected-item="selectedDataKey" | ||
37 | + md-search-text="dataKeySearchText" | ||
38 | + md-items="item in dataKeysSearch(dataKeySearchText)" | ||
39 | + md-item-text="item.name" | ||
40 | + md-min-length="0" | ||
41 | + placeholder="{{ 'datakey.function-types' | translate }}" | ||
42 | + md-menu-class="tb-func-datakey-autocomplete"> | ||
43 | + <span md-highlight-text="dataKeySearchText" md-highlight-flags="^i">{{item}}</span> | ||
44 | + <md-not-found> | ||
45 | + <div class="tb-not-found"> | ||
46 | + <div class="tb-no-entries" ng-if="!textIsNotEmpty(dataKeySearchText)"> | ||
47 | + <span translate>device.no-keys-found</span> | ||
48 | + </div> | ||
49 | + <div ng-if="textIsNotEmpty(dataKeySearchText)"> | ||
50 | + <span translate translate-values='{ key: "{{dataKeySearchText | truncate:true:6:'...'}}" }'>device.no-key-matching</span> | ||
51 | + <span> | ||
52 | + <a translate ng-click="createKey($event, '#function_datakey_chips')">device.create-new-key</a> | ||
53 | + </span> | ||
54 | + </div> | ||
40 | </div> | 55 | </div> |
41 | - <div ng-if="textIsNotEmpty(dataKeySearchText)"> | ||
42 | - <span translate translate-values='{ key: "{{dataKeySearchText | truncate:true:6:'...'}}" }'>device.no-key-matching</span> | ||
43 | - <span> | ||
44 | - <a translate ng-click="createKey($event, '#function_datakey_chips')">device.create-new-key</a> | ||
45 | - </span> | ||
46 | - </div> | ||
47 | - </div> | ||
48 | - </md-not-found> | ||
49 | - </md-autocomplete> | ||
50 | - <md-chip-template> | ||
51 | - <div layout="row" layout-align="start center" class="tb-attribute-chip"> | ||
52 | - <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;"> | ||
53 | - <div class="tb-color-result" ng-style="{background: $chip.color}"></div> | ||
54 | - </div> | ||
55 | - <div layout="row" flex> | ||
56 | - <div class="tb-chip-label"> | ||
57 | - {{$chip.label}} | 56 | + </md-not-found> |
57 | + </md-autocomplete> | ||
58 | + <md-chip-template> | ||
59 | + <div layout="row" layout-align="start center" class="tb-attribute-chip"> | ||
60 | + <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;"> | ||
61 | + <div class="tb-color-result" ng-style="{background: $chip.color}"></div> | ||
58 | </div> | 62 | </div> |
59 | - <div class="tb-chip-separator">: </div> | ||
60 | - <div class="tb-chip-label"> | ||
61 | - <strong>{{$chip.name}}</strong> | 63 | + <div layout="row" flex> |
64 | + <div class="tb-chip-label"> | ||
65 | + {{$chip.label}} | ||
66 | + </div> | ||
67 | + <div class="tb-chip-separator">: </div> | ||
68 | + <div class="tb-chip-label"> | ||
69 | + <strong>{{$chip.name}}</strong> | ||
70 | + </div> | ||
62 | </div> | 71 | </div> |
72 | + <md-button ng-click="editDataKey($event, $chip, $index)" class="md-icon-button tb-md-32"> | ||
73 | + <md-icon aria-label="edit" class="material-icons tb-md-20">edit</md-icon> | ||
74 | + </md-button> | ||
63 | </div> | 75 | </div> |
64 | - <md-button ng-click="editDataKey($event, $chip, $index)" class="md-icon-button tb-md-32"> | ||
65 | - <md-icon aria-label="edit" class="material-icons tb-md-20">edit</md-icon> | ||
66 | - </md-button> | ||
67 | - </div> | ||
68 | - </md-chip-template> | ||
69 | - </md-chips> | ||
70 | - <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert"> | ||
71 | - <div translate ng-message="funcTypes" class="tb-error-message">datakey.function-types-required</div> | ||
72 | - </div> | 76 | + </md-chip-template> |
77 | + </md-chips> | ||
78 | + <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert"> | ||
79 | + <div translate ng-message="funcTypes" class="tb-error-message">datakey.function-types-required</div> | ||
80 | + </div> | ||
81 | + </section> | ||
73 | </section> | 82 | </section> |
@@ -76,7 +76,7 @@ function Datasource($compile, $templateCache, types) { | @@ -76,7 +76,7 @@ function Datasource($compile, $templateCache, types) { | ||
76 | restrict: "E", | 76 | restrict: "E", |
77 | require: "^ngModel", | 77 | require: "^ngModel", |
78 | scope: { | 78 | scope: { |
79 | - entityAliases: '=', | 79 | + aliasController: '=', |
80 | widgetType: '=', | 80 | widgetType: '=', |
81 | functionsOnly: '=', | 81 | functionsOnly: '=', |
82 | datakeySettingsSchema: '=', | 82 | datakeySettingsSchema: '=', |
@@ -37,7 +37,7 @@ | @@ -37,7 +37,7 @@ | ||
37 | ng-switch-when="entity" | 37 | ng-switch-when="entity" |
38 | ng-required="model.type === types.datasourceType.entity" | 38 | ng-required="model.type === types.datasourceType.entity" |
39 | widget-type="widgetType" | 39 | widget-type="widgetType" |
40 | - entity-aliases="entityAliases" | 40 | + alias-controller="aliasController" |
41 | generate-data-key="generateDataKey({chip: chip, type: type})" | 41 | generate-data-key="generateDataKey({chip: chip, type: type})" |
42 | fetch-entity-keys="fetchEntityKeys({entityAliasId: entityAliasId, query: query, type: type})" | 42 | fetch-entity-keys="fetchEntityKeys({entityAliasId: entityAliasId, query: query, type: type})" |
43 | on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias})"> | 43 | on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias})"> |
@@ -31,7 +31,7 @@ export default angular.module('thingsboard.directives.entityAliasSelect', []) | @@ -31,7 +31,7 @@ export default angular.module('thingsboard.directives.entityAliasSelect', []) | ||
31 | .name; | 31 | .name; |
32 | 32 | ||
33 | /*@ngInject*/ | 33 | /*@ngInject*/ |
34 | -function EntityAliasSelect($compile, $templateCache, $mdConstant) { | 34 | +function EntityAliasSelect($compile, $templateCache, $mdConstant, entityService) { |
35 | 35 | ||
36 | var linker = function (scope, element, attrs, ngModelCtrl) { | 36 | var linker = function (scope, element, attrs, ngModelCtrl) { |
37 | var template = $templateCache.get(entityAliasSelectTemplate); | 37 | var template = $templateCache.get(entityAliasSelectTemplate); |
@@ -49,19 +49,18 @@ function EntityAliasSelect($compile, $templateCache, $mdConstant) { | @@ -49,19 +49,18 @@ function EntityAliasSelect($compile, $templateCache, $mdConstant) { | ||
49 | ngModelCtrl.$setValidity('entityAlias', valid); | 49 | ngModelCtrl.$setValidity('entityAlias', valid); |
50 | }; | 50 | }; |
51 | 51 | ||
52 | - scope.$watch('entityAliases', function () { | 52 | + scope.$watch('aliasController', function () { |
53 | scope.entityAliasList = []; | 53 | scope.entityAliasList = []; |
54 | - for (var aliasId in scope.entityAliases) { | 54 | + var entityAliases = scope.aliasController.getEntityAliases(); |
55 | + for (var aliasId in entityAliases) { | ||
55 | if (scope.allowedEntityTypes) { | 56 | if (scope.allowedEntityTypes) { |
56 | - if (scope.allowedEntityTypes.indexOf(scope.entityAliases[aliasId].entityType) === -1) { | 57 | + if (!entityService.filterAliasByEntityTypes(entityAliases[aliasId], scope.allowedEntityTypes)) { |
57 | continue; | 58 | continue; |
58 | } | 59 | } |
59 | } | 60 | } |
60 | - var entityAlias = {id: aliasId, alias: scope.entityAliases[aliasId].alias, | ||
61 | - entityType: scope.entityAliases[aliasId].entityType, entityId: scope.entityAliases[aliasId].entityId}; | ||
62 | - scope.entityAliasList.push(entityAlias); | 61 | + scope.entityAliasList.push(entityAliases[aliasId]); |
63 | } | 62 | } |
64 | - }, true); | 63 | + }); |
65 | 64 | ||
66 | scope.$watch('entityAlias', function () { | 65 | scope.$watch('entityAlias', function () { |
67 | scope.updateView(); | 66 | scope.updateView(); |
@@ -141,7 +140,7 @@ function EntityAliasSelect($compile, $templateCache, $mdConstant) { | @@ -141,7 +140,7 @@ function EntityAliasSelect($compile, $templateCache, $mdConstant) { | ||
141 | link: linker, | 140 | link: linker, |
142 | scope: { | 141 | scope: { |
143 | tbRequired: '=?', | 142 | tbRequired: '=?', |
144 | - entityAliases: '=', | 143 | + aliasController: '=', |
145 | allowedEntityTypes: '=?', | 144 | allowedEntityTypes: '=?', |
146 | onCreateEntityAlias: '&' | 145 | onCreateEntityAlias: '&' |
147 | } | 146 | } |
@@ -128,13 +128,9 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti | @@ -128,13 +128,9 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti | ||
128 | } else if (scope.widgetType === types.widgetType.rpc.value && scope.isDataEnabled) { | 128 | } else if (scope.widgetType === types.widgetType.rpc.value && scope.isDataEnabled) { |
129 | if (config.targetDeviceAliasIds && config.targetDeviceAliasIds.length > 0) { | 129 | if (config.targetDeviceAliasIds && config.targetDeviceAliasIds.length > 0) { |
130 | var aliasId = config.targetDeviceAliasIds[0]; | 130 | var aliasId = config.targetDeviceAliasIds[0]; |
131 | - if (scope.entityAliases[aliasId]) { | ||
132 | - scope.targetDeviceAlias.value = { | ||
133 | - id: aliasId, | ||
134 | - alias: scope.entityAliases[aliasId].alias, | ||
135 | - entityType: scope.entityAliases[aliasId].entityType, | ||
136 | - entityId: scope.entityAliases[aliasId].entityId | ||
137 | - }; | 131 | + var entityAliases = scope.aliasController.getEntityAliases(); |
132 | + if (entityAliases[aliasId]) { | ||
133 | + scope.targetDeviceAlias.value = entityAliases[aliasId]; | ||
138 | } else { | 134 | } else { |
139 | scope.targetDeviceAlias.value = null; | 135 | scope.targetDeviceAlias.value = null; |
140 | } | 136 | } |
@@ -395,7 +391,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti | @@ -395,7 +391,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $timeout, types, uti | ||
395 | widgetType: '=', | 391 | widgetType: '=', |
396 | widgetSettingsSchema: '=', | 392 | widgetSettingsSchema: '=', |
397 | datakeySettingsSchema: '=', | 393 | datakeySettingsSchema: '=', |
398 | - entityAliases: '=', | 394 | + aliasController: '=', |
399 | functionsOnly: '=', | 395 | functionsOnly: '=', |
400 | fetchEntityKeys: '&', | 396 | fetchEntityKeys: '&', |
401 | onCreateEntityAlias: '&', | 397 | onCreateEntityAlias: '&', |
@@ -60,7 +60,7 @@ | @@ -60,7 +60,7 @@ | ||
60 | style="padding: 0 0 0 10px; margin: 5px;"> | 60 | style="padding: 0 0 0 10px; margin: 5px;"> |
61 | <tb-datasource flex ng-model="datasource.value" | 61 | <tb-datasource flex ng-model="datasource.value" |
62 | widget-type="widgetType" | 62 | widget-type="widgetType" |
63 | - entity-aliases="entityAliases" | 63 | + alias-controller="aliasController" |
64 | functions-only="functionsOnly" | 64 | functions-only="functionsOnly" |
65 | datakey-settings-schema="datakeySettingsSchema" | 65 | datakey-settings-schema="datakeySettingsSchema" |
66 | generate-data-key="generateDataKey(chip,type)" | 66 | generate-data-key="generateDataKey(chip,type)" |
@@ -104,7 +104,7 @@ | @@ -104,7 +104,7 @@ | ||
104 | <v-pane-content style="padding: 0 5px;"> | 104 | <v-pane-content style="padding: 0 5px;"> |
105 | <tb-entity-alias-select flex | 105 | <tb-entity-alias-select flex |
106 | tb-required="widgetType === types.widgetType.rpc.value && !widgetEditMode" | 106 | tb-required="widgetType === types.widgetType.rpc.value && !widgetEditMode" |
107 | - entity-aliases="entityAliases" | 107 | + alias-controller="aliasController" |
108 | allowed-entity-types="[types.entityType.device]" | 108 | allowed-entity-types="[types.entityType.device]" |
109 | ng-model="targetDeviceAlias.value" | 109 | ng-model="targetDeviceAlias.value" |
110 | on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias, allowedEntityTypes: allowedEntityTypes})"> | 110 | on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias, allowedEntityTypes: allowedEntityTypes})"> |
@@ -20,9 +20,9 @@ import Subscription from '../api/subscription'; | @@ -20,9 +20,9 @@ import Subscription from '../api/subscription'; | ||
20 | /* eslint-disable angular/angularelement */ | 20 | /* eslint-disable angular/angularelement */ |
21 | 21 | ||
22 | /*@ngInject*/ | 22 | /*@ngInject*/ |
23 | -export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, tbRaf, types, utils, timeService, | 23 | +export default function WidgetController($scope, $timeout, $window, $element, $q, $log, $injector, $filter, $compile, tbRaf, types, utils, timeService, |
24 | datasourceService, entityService, deviceService, visibleRect, isEdit, stDiff, dashboardTimewindow, | 24 | datasourceService, entityService, deviceService, visibleRect, isEdit, stDiff, dashboardTimewindow, |
25 | - dashboardTimewindowApi, widget, aliasesInfo, stateController, widgetType) { | 25 | + dashboardTimewindowApi, widget, aliasController, stateController, widgetInfo, widgetType) { |
26 | 26 | ||
27 | var vm = this; | 27 | var vm = this; |
28 | 28 | ||
@@ -37,23 +37,16 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -37,23 +37,16 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
37 | $scope.executingRpcRequest = false; | 37 | $scope.executingRpcRequest = false; |
38 | 38 | ||
39 | var gridsterItemInited = false; | 39 | var gridsterItemInited = false; |
40 | + var subscriptionInited = false; | ||
41 | + var widgetSizeDetected = false; | ||
40 | 42 | ||
41 | var cafs = {}; | 43 | var cafs = {}; |
42 | 44 | ||
43 | - /* | ||
44 | - * data = array of datasourceData | ||
45 | - * datasourceData = { | ||
46 | - * tbDatasource, | ||
47 | - * dataKey, { name, config } | ||
48 | - * data = array of [time, value] | ||
49 | - * } | ||
50 | - */ | ||
51 | - | ||
52 | var widgetContext = { | 45 | var widgetContext = { |
53 | inited: false, | 46 | inited: false, |
54 | $scope: $scope, | 47 | $scope: $scope, |
55 | - $container: $('#container', $element), | ||
56 | - $containerParent: $($element), | 48 | + $container: null, |
49 | + $containerParent: null, | ||
57 | width: 0, | 50 | width: 0, |
58 | height: 0, | 51 | height: 0, |
59 | isEdit: isEdit, | 52 | isEdit: isEdit, |
@@ -80,30 +73,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -80,30 +73,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
80 | createSubscription: function(options, subscribe) { | 73 | createSubscription: function(options, subscribe) { |
81 | return createSubscription(options, subscribe); | 74 | return createSubscription(options, subscribe); |
82 | }, | 75 | }, |
83 | - | ||
84 | - | ||
85 | - // type: "timeseries" or "latest" or "rpc" | ||
86 | - /* subscriptionInfo = [ | ||
87 | - { | ||
88 | - entityType: "" | ||
89 | - entityId: "" | ||
90 | - entityName: "" | ||
91 | - timeseries: [{ name: "", label: "" }, ..] | ||
92 | - attributes: [{ name: "", label: "" }, ..] | ||
93 | - } | ||
94 | - .. | ||
95 | - ]*/ | ||
96 | - | ||
97 | - // options = { | ||
98 | - // timeWindowConfig, | ||
99 | - // useDashboardTimewindow, | ||
100 | - // legendConfig, | ||
101 | - // decimals, | ||
102 | - // units, | ||
103 | - // callbacks [ onDataUpdated(subscription, apply) ] | ||
104 | - // } | ||
105 | - // | ||
106 | - | ||
107 | createSubscriptionFromInfo: function (type, subscriptionsInfo, options, useDefaultComponents, subscribe) { | 76 | createSubscriptionFromInfo: function (type, subscriptionsInfo, options, useDefaultComponents, subscribe) { |
108 | return createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe); | 77 | return createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe); |
109 | }, | 78 | }, |
@@ -149,7 +118,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -149,7 +118,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
149 | dashboardTimewindowApi: dashboardTimewindowApi, | 118 | dashboardTimewindowApi: dashboardTimewindowApi, |
150 | types: types, | 119 | types: types, |
151 | stDiff: stDiff, | 120 | stDiff: stDiff, |
152 | - aliasesInfo: aliasesInfo | 121 | + aliasController: aliasController |
153 | }; | 122 | }; |
154 | 123 | ||
155 | var widgetTypeInstance; | 124 | var widgetTypeInstance; |
@@ -203,23 +172,11 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -203,23 +172,11 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
203 | 172 | ||
204 | vm.gridsterItemInitialized = gridsterItemInitialized; | 173 | vm.gridsterItemInitialized = gridsterItemInitialized; |
205 | 174 | ||
206 | - initialize(); | ||
207 | - | ||
208 | - | ||
209 | - /* | ||
210 | - options = { | ||
211 | - type, | ||
212 | - targetDeviceAliasIds, // RPC | ||
213 | - targetDeviceIds, // RPC | ||
214 | - datasources, | ||
215 | - timeWindowConfig, | ||
216 | - useDashboardTimewindow, | ||
217 | - legendConfig, | ||
218 | - decimals, | ||
219 | - units, | ||
220 | - callbacks | ||
221 | - } | ||
222 | - */ | 175 | + initialize().then( |
176 | + function(){ | ||
177 | + onInit(); | ||
178 | + } | ||
179 | + ); | ||
223 | 180 | ||
224 | function createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe) { | 181 | function createSubscriptionFromInfo(type, subscriptionsInfo, options, useDefaultComponents, subscribe) { |
225 | var deferred = $q.defer(); | 182 | var deferred = $q.defer(); |
@@ -233,28 +190,42 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -233,28 +190,42 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
233 | } | 190 | } |
234 | } | 191 | } |
235 | 192 | ||
236 | - entityService.createDatasoucesFromSubscriptionsInfo(subscriptionsInfo).then( | 193 | + entityService.createDatasourcesFromSubscriptionsInfo(subscriptionsInfo).then( |
237 | function (datasources) { | 194 | function (datasources) { |
238 | options.datasources = datasources; | 195 | options.datasources = datasources; |
239 | - var subscription = createSubscription(options, subscribe); | ||
240 | - if (useDefaultComponents) { | ||
241 | - defaultSubscriptionOptions(subscription, options); | ||
242 | - } | ||
243 | - deferred.resolve(subscription); | 196 | + createSubscription(options, subscribe).then( |
197 | + function success(subscription) { | ||
198 | + if (useDefaultComponents) { | ||
199 | + defaultSubscriptionOptions(subscription, options); | ||
200 | + } | ||
201 | + deferred.resolve(subscription); | ||
202 | + }, | ||
203 | + function fail() { | ||
204 | + deferred.reject(); | ||
205 | + } | ||
206 | + ); | ||
244 | } | 207 | } |
245 | ); | 208 | ); |
246 | return deferred.promise; | 209 | return deferred.promise; |
247 | } | 210 | } |
248 | 211 | ||
249 | function createSubscription(options, subscribe) { | 212 | function createSubscription(options, subscribe) { |
213 | + var deferred = $q.defer(); | ||
250 | options.dashboardTimewindow = dashboardTimewindow; | 214 | options.dashboardTimewindow = dashboardTimewindow; |
251 | - var subscription = | ||
252 | - new Subscription(subscriptionContext, options); | ||
253 | - widgetContext.subscriptions[subscription.id] = subscription; | ||
254 | - if (subscribe) { | ||
255 | - subscription.subscribe(); | ||
256 | - } | ||
257 | - return subscription; | 215 | + new Subscription(subscriptionContext, options).then( |
216 | + function success(subscription) { | ||
217 | + widgetContext.subscriptions[subscription.id] = subscription; | ||
218 | + if (subscribe) { | ||
219 | + subscription.subscribe(); | ||
220 | + } | ||
221 | + deferred.resolve(subscription); | ||
222 | + }, | ||
223 | + function fail() { | ||
224 | + deferred.reject(); | ||
225 | + } | ||
226 | + ); | ||
227 | + | ||
228 | + return deferred.promise; | ||
258 | } | 229 | } |
259 | 230 | ||
260 | function defaultComponentsOptions(options) { | 231 | function defaultComponentsOptions(options) { |
@@ -310,8 +281,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -310,8 +281,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
310 | } | 281 | } |
311 | 282 | ||
312 | function createDefaultSubscription() { | 283 | function createDefaultSubscription() { |
313 | - var subscription; | ||
314 | var options; | 284 | var options; |
285 | + var deferred = $q.defer(); | ||
315 | if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) { | 286 | if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) { |
316 | options = { | 287 | options = { |
317 | type: widget.type, | 288 | type: widget.type, |
@@ -319,16 +290,23 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -319,16 +290,23 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
319 | }; | 290 | }; |
320 | defaultComponentsOptions(options); | 291 | defaultComponentsOptions(options); |
321 | 292 | ||
322 | - subscription = createSubscription(options); | ||
323 | - | ||
324 | - defaultSubscriptionOptions(subscription, options); | 293 | + createSubscription(options).then( |
294 | + function success(subscription) { | ||
295 | + defaultSubscriptionOptions(subscription, options); | ||
325 | 296 | ||
326 | - // backward compatibility | 297 | + // backward compatibility |
327 | 298 | ||
328 | - widgetContext.datasources = subscription.datasources; | ||
329 | - widgetContext.data = subscription.data; | ||
330 | - widgetContext.hiddenData = subscription.hiddenData; | ||
331 | - widgetContext.timeWindow = subscription.timeWindow; | 299 | + widgetContext.datasources = subscription.datasources; |
300 | + widgetContext.data = subscription.data; | ||
301 | + widgetContext.hiddenData = subscription.hiddenData; | ||
302 | + widgetContext.timeWindow = subscription.timeWindow; | ||
303 | + widgetContext.defaultSubscription = subscription; | ||
304 | + deferred.resolve(); | ||
305 | + }, | ||
306 | + function fail() { | ||
307 | + deferred.reject(); | ||
308 | + } | ||
309 | + ); | ||
332 | 310 | ||
333 | } else if (widget.type === types.widgetType.rpc.value) { | 311 | } else if (widget.type === types.widgetType.rpc.value) { |
334 | $scope.loadingData = false; | 312 | $scope.loadingData = false; |
@@ -356,30 +334,126 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -356,30 +334,126 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
356 | $scope.rpcRejection = null; | 334 | $scope.rpcRejection = null; |
357 | } | 335 | } |
358 | } | 336 | } |
359 | - subscription = createSubscription(options); | 337 | + createSubscription(options).then( |
338 | + function success(subscription) { | ||
339 | + widgetContext.defaultSubscription = subscription; | ||
340 | + deferred.resolve(); | ||
341 | + }, | ||
342 | + function fail() { | ||
343 | + deferred.reject(); | ||
344 | + } | ||
345 | + ); | ||
360 | } else if (widget.type === types.widgetType.static.value) { | 346 | } else if (widget.type === types.widgetType.static.value) { |
361 | $scope.loadingData = false; | 347 | $scope.loadingData = false; |
348 | + deferred.resolve(); | ||
349 | + } else { | ||
350 | + deferred.resolve(); | ||
362 | } | 351 | } |
363 | - if (subscription) { | ||
364 | - widgetContext.defaultSubscription = subscription; | ||
365 | - } | 352 | + return deferred.promise; |
366 | } | 353 | } |
367 | 354 | ||
355 | + function configureWidgetElement() { | ||
368 | 356 | ||
369 | - function initialize() { | 357 | + $scope.displayLegend = angular.isDefined(widget.config.showLegend) ? |
358 | + widget.config.showLegend : widget.type === types.widgetType.timeseries.value; | ||
370 | 359 | ||
371 | - if (!vm.useCustomDatasources) { | ||
372 | - createDefaultSubscription(); | 360 | + if ($scope.displayLegend) { |
361 | + $scope.legendConfig = widget.config.legendConfig || | ||
362 | + { | ||
363 | + position: types.position.bottom.value, | ||
364 | + showMin: false, | ||
365 | + showMax: false, | ||
366 | + showAvg: widget.type === types.widgetType.timeseries.value, | ||
367 | + showTotal: false | ||
368 | + }; | ||
369 | + $scope.legendData = { | ||
370 | + keys: [], | ||
371 | + data: [] | ||
372 | + }; | ||
373 | + } | ||
374 | + | ||
375 | + var html = '<div class="tb-absolute-fill tb-widget-error" ng-if="widgetErrorData">' + | ||
376 | + '<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>' + | ||
377 | + '</div>' + | ||
378 | + '<div class="tb-absolute-fill tb-widget-loading" ng-show="loadingData" layout="column" layout-align="center center">' + | ||
379 | + '<md-progress-circular md-mode="indeterminate" ng-disabled="!loadingData" class="md-accent" md-diameter="40"></md-progress-circular>' + | ||
380 | + '</div>'; | ||
381 | + | ||
382 | + var containerHtml = '<div id="container">' + widgetInfo.templateHtml + '</div>'; | ||
383 | + if ($scope.displayLegend) { | ||
384 | + var layoutType; | ||
385 | + if ($scope.legendConfig.position === types.position.top.value || | ||
386 | + $scope.legendConfig.position === types.position.bottom.value) { | ||
387 | + layoutType = 'column'; | ||
388 | + } else { | ||
389 | + layoutType = 'row'; | ||
390 | + } | ||
391 | + | ||
392 | + var legendStyle; | ||
393 | + switch($scope.legendConfig.position) { | ||
394 | + case types.position.top.value: | ||
395 | + legendStyle = 'padding-bottom: 8px;'; | ||
396 | + break; | ||
397 | + case types.position.bottom.value: | ||
398 | + legendStyle = 'padding-top: 8px;'; | ||
399 | + break; | ||
400 | + case types.position.left.value: | ||
401 | + legendStyle = 'padding-right: 0px;'; | ||
402 | + break; | ||
403 | + case types.position.right.value: | ||
404 | + legendStyle = 'padding-left: 0px;'; | ||
405 | + break; | ||
406 | + } | ||
407 | + | ||
408 | + var legendHtml = '<tb-legend style="'+legendStyle+'" legend-config="legendConfig" legend-data="legendData"></tb-legend>'; | ||
409 | + containerHtml = '<div flex id="widget-container">' + containerHtml + '</div>'; | ||
410 | + html += '<div class="tb-absolute-fill" layout="'+layoutType+'">'; | ||
411 | + if ($scope.legendConfig.position === types.position.top.value || | ||
412 | + $scope.legendConfig.position === types.position.left.value) { | ||
413 | + html += legendHtml; | ||
414 | + html += containerHtml; | ||
415 | + } else { | ||
416 | + html += containerHtml; | ||
417 | + html += legendHtml; | ||
418 | + } | ||
419 | + html += '</div>'; | ||
373 | } else { | 420 | } else { |
374 | - $scope.loadingData = false; | 421 | + html += containerHtml; |
375 | } | 422 | } |
376 | 423 | ||
424 | + //TODO: | ||
425 | + /*if (progressElement) { | ||
426 | + progressScope.$destroy(); | ||
427 | + progressScope = null; | ||
428 | + | ||
429 | + progressElement.remove(); | ||
430 | + progressElement = null; | ||
431 | + }*/ | ||
432 | + | ||
433 | + $element.html(html); | ||
434 | + | ||
435 | + var containerElement = $scope.displayLegend ? angular.element($element[0].querySelector('#widget-container')) : $element; | ||
436 | + widgetContext.$container = $('#container', containerElement); | ||
437 | + widgetContext.$containerParent = $(containerElement); | ||
438 | + | ||
439 | + $compile($element.contents())($scope); | ||
440 | + | ||
441 | + addResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef | ||
442 | + } | ||
443 | + | ||
444 | + function destroyWidgetElement() { | ||
445 | + removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef | ||
446 | + $element.html(''); | ||
447 | + widgetContext.$container = null; | ||
448 | + widgetContext.$containerParent = null; | ||
449 | + } | ||
450 | + | ||
451 | + function initialize() { | ||
452 | + | ||
377 | $scope.$on('toggleDashboardEditMode', function (event, isEdit) { | 453 | $scope.$on('toggleDashboardEditMode', function (event, isEdit) { |
378 | onEditModeChanged(isEdit); | 454 | onEditModeChanged(isEdit); |
379 | }); | 455 | }); |
380 | 456 | ||
381 | - addResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef | ||
382 | - | ||
383 | $scope.$watch(function () { | 457 | $scope.$watch(function () { |
384 | return widget.row + ',' + widget.col + ',' + widget.config.mobileOrder; | 458 | return widget.row + ',' + widget.col + ',' + widget.config.mobileOrder; |
385 | }, function () { | 459 | }, function () { |
@@ -398,18 +472,60 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -398,18 +472,60 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
398 | onMobileModeChanged(newIsMobile); | 472 | onMobileModeChanged(newIsMobile); |
399 | }); | 473 | }); |
400 | 474 | ||
401 | - $scope.$on('entityAliasListChanged', function (event, aliasesInfo) { | ||
402 | - subscriptionContext.aliasesInfo = aliasesInfo; | 475 | + $scope.$on('entityAliasesChanged', function (event, aliasIds) { |
476 | + var subscriptionChanged = false; | ||
403 | for (var id in widgetContext.subscriptions) { | 477 | for (var id in widgetContext.subscriptions) { |
404 | var subscription = widgetContext.subscriptions[id]; | 478 | var subscription = widgetContext.subscriptions[id]; |
405 | - subscription.onAliasesChanged(); | 479 | + subscriptionChanged = subscriptionChanged || subscription.onAliasesChanged(aliasIds); |
480 | + } | ||
481 | + if (subscriptionChanged && !vm.useCustomDatasources) { | ||
482 | + reInit(); | ||
406 | } | 483 | } |
407 | }); | 484 | }); |
408 | 485 | ||
409 | $scope.$on("$destroy", function () { | 486 | $scope.$on("$destroy", function () { |
410 | - removeResizeListener(widgetContext.$containerParent[0], onResize); // eslint-disable-line no-undef | ||
411 | onDestroy(); | 487 | onDestroy(); |
412 | }); | 488 | }); |
489 | + | ||
490 | + configureWidgetElement(); | ||
491 | + var deferred = $q.defer(); | ||
492 | + if (!vm.useCustomDatasources) { | ||
493 | + createDefaultSubscription().then( | ||
494 | + function success() { | ||
495 | + subscriptionInited = true; | ||
496 | + deferred.resolve(); | ||
497 | + }, | ||
498 | + function fail() { | ||
499 | + subscriptionInited = true; | ||
500 | + deferred.reject(); | ||
501 | + } | ||
502 | + ); | ||
503 | + } else { | ||
504 | + $scope.loadingData = false; | ||
505 | + subscriptionInited = true; | ||
506 | + deferred.resolve(); | ||
507 | + } | ||
508 | + return deferred.promise; | ||
509 | + } | ||
510 | + | ||
511 | + function reInit() { | ||
512 | + onDestroy(); | ||
513 | + configureWidgetElement(); | ||
514 | + if (!vm.useCustomDatasources) { | ||
515 | + createDefaultSubscription().then( | ||
516 | + function success() { | ||
517 | + subscriptionInited = true; | ||
518 | + onInit(); | ||
519 | + }, | ||
520 | + function fail() { | ||
521 | + subscriptionInited = true; | ||
522 | + onInit(); | ||
523 | + } | ||
524 | + ); | ||
525 | + } else { | ||
526 | + subscriptionInited = true; | ||
527 | + onInit(); | ||
528 | + } | ||
413 | } | 529 | } |
414 | 530 | ||
415 | function handleWidgetException(e) { | 531 | function handleWidgetException(e) { |
@@ -417,8 +533,15 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -417,8 +533,15 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
417 | $scope.widgetErrorData = utils.processWidgetException(e); | 533 | $scope.widgetErrorData = utils.processWidgetException(e); |
418 | } | 534 | } |
419 | 535 | ||
420 | - function onInit() { | ||
421 | - if (!widgetContext.inited) { | 536 | + function isReady() { |
537 | + return subscriptionInited && gridsterItemInited && widgetSizeDetected; | ||
538 | + } | ||
539 | + | ||
540 | + function onInit(skipSizeCheck) { | ||
541 | + if (!skipSizeCheck) { | ||
542 | + checkSize(); | ||
543 | + } | ||
544 | + if (!widgetContext.inited && isReady()) { | ||
422 | widgetContext.inited = true; | 545 | widgetContext.inited = true; |
423 | try { | 546 | try { |
424 | widgetTypeInstance.onInit(); | 547 | widgetTypeInstance.onInit(); |
@@ -443,6 +566,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -443,6 +566,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
443 | widgetContext.width = width; | 566 | widgetContext.width = width; |
444 | widgetContext.height = height; | 567 | widgetContext.height = height; |
445 | sizeChanged = true; | 568 | sizeChanged = true; |
569 | + widgetSizeDetected = true; | ||
446 | } | 570 | } |
447 | } | 571 | } |
448 | return sizeChanged; | 572 | return sizeChanged; |
@@ -462,8 +586,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -462,8 +586,8 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
462 | handleWidgetException(e); | 586 | handleWidgetException(e); |
463 | } | 587 | } |
464 | }); | 588 | }); |
465 | - } else if (gridsterItemInited) { | ||
466 | - onInit(); | 589 | + } else { |
590 | + onInit(true); | ||
467 | } | 591 | } |
468 | } | 592 | } |
469 | } | 593 | } |
@@ -472,9 +596,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -472,9 +596,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
472 | if (item && item.gridster) { | 596 | if (item && item.gridster) { |
473 | widgetContext.isMobile = item.gridster.isMobile; | 597 | widgetContext.isMobile = item.gridster.isMobile; |
474 | gridsterItemInited = true; | 598 | gridsterItemInited = true; |
475 | - if (checkSize()) { | ||
476 | - onInit(); | ||
477 | - } | 599 | + onInit(); |
478 | // gridsterItemElement = $(item.$element); | 600 | // gridsterItemElement = $(item.$element); |
479 | //updateVisibility(); | 601 | //updateVisibility(); |
480 | } | 602 | } |
@@ -544,6 +666,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -544,6 +666,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
544 | var subscription = widgetContext.subscriptions[id]; | 666 | var subscription = widgetContext.subscriptions[id]; |
545 | subscription.destroy(); | 667 | subscription.destroy(); |
546 | } | 668 | } |
669 | + subscriptionInited = false; | ||
547 | widgetContext.subscriptions = []; | 670 | widgetContext.subscriptions = []; |
548 | if (widgetContext.inited) { | 671 | if (widgetContext.inited) { |
549 | widgetContext.inited = false; | 672 | widgetContext.inited = false; |
@@ -559,6 +682,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | @@ -559,6 +682,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q | ||
559 | handleWidgetException(e); | 682 | handleWidgetException(e); |
560 | } | 683 | } |
561 | } | 684 | } |
685 | + destroyWidgetElement(); | ||
562 | } | 686 | } |
563 | 687 | ||
564 | //TODO: widgets visibility | 688 | //TODO: widgets visibility |
@@ -28,7 +28,7 @@ export default angular.module('thingsboard.directives.widget', [thingsboardLegen | @@ -28,7 +28,7 @@ export default angular.module('thingsboard.directives.widget', [thingsboardLegen | ||
28 | .name; | 28 | .name; |
29 | 29 | ||
30 | /*@ngInject*/ | 30 | /*@ngInject*/ |
31 | -function Widget($controller, $compile, types, widgetService) { | 31 | +function Widget($controller, widgetService) { |
32 | return { | 32 | return { |
33 | scope: true, | 33 | scope: true, |
34 | link: function (scope, elem, attrs) { | 34 | link: function (scope, elem, attrs) { |
@@ -81,90 +81,9 @@ function Widget($controller, $compile, types, widgetService) { | @@ -81,90 +81,9 @@ function Widget($controller, $compile, types, widgetService) { | ||
81 | 81 | ||
82 | elem.addClass(widgetNamespace); | 82 | elem.addClass(widgetNamespace); |
83 | 83 | ||
84 | - var html = '<div class="tb-absolute-fill tb-widget-error" ng-if="widgetErrorData">' + | ||
85 | - '<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>' + | ||
86 | - '</div>' + | ||
87 | - '<div class="tb-absolute-fill tb-widget-loading" ng-show="loadingData" layout="column" layout-align="center center">' + | ||
88 | - '<md-progress-circular md-mode="indeterminate" ng-disabled="!loadingData" class="md-accent" md-diameter="40"></md-progress-circular>' + | ||
89 | - '</div>'; | ||
90 | - | ||
91 | - scope.displayLegend = angular.isDefined(widget.config.showLegend) ? | ||
92 | - widget.config.showLegend : widget.type === types.widgetType.timeseries.value; | ||
93 | - | ||
94 | - | ||
95 | - var containerHtml = '<div id="container">' + widgetInfo.templateHtml + '</div>'; | ||
96 | - if (scope.displayLegend) { | ||
97 | - scope.legendConfig = widget.config.legendConfig || | ||
98 | - { | ||
99 | - position: types.position.bottom.value, | ||
100 | - showMin: false, | ||
101 | - showMax: false, | ||
102 | - showAvg: widget.type === types.widgetType.timeseries.value, | ||
103 | - showTotal: false | ||
104 | - }; | ||
105 | - scope.legendData = { | ||
106 | - keys: [], | ||
107 | - data: [] | ||
108 | - }; | ||
109 | - | ||
110 | - var layoutType; | ||
111 | - if (scope.legendConfig.position === types.position.top.value || | ||
112 | - scope.legendConfig.position === types.position.bottom.value) { | ||
113 | - layoutType = 'column'; | ||
114 | - } else { | ||
115 | - layoutType = 'row'; | ||
116 | - } | ||
117 | - | ||
118 | - var legendStyle; | ||
119 | - switch(scope.legendConfig.position) { | ||
120 | - case types.position.top.value: | ||
121 | - legendStyle = 'padding-bottom: 8px;'; | ||
122 | - break; | ||
123 | - case types.position.bottom.value: | ||
124 | - legendStyle = 'padding-top: 8px;'; | ||
125 | - break; | ||
126 | - case types.position.left.value: | ||
127 | - legendStyle = 'padding-right: 0px;'; | ||
128 | - break; | ||
129 | - case types.position.right.value: | ||
130 | - legendStyle = 'padding-left: 0px;'; | ||
131 | - break; | ||
132 | - } | ||
133 | - | ||
134 | - var legendHtml = '<tb-legend style="'+legendStyle+'" legend-config="legendConfig" legend-data="legendData"></tb-legend>'; | ||
135 | - containerHtml = '<div flex id="widget-container">' + containerHtml + '</div>'; | ||
136 | - html += '<div class="tb-absolute-fill" layout="'+layoutType+'">'; | ||
137 | - if (scope.legendConfig.position === types.position.top.value || | ||
138 | - scope.legendConfig.position === types.position.left.value) { | ||
139 | - html += legendHtml; | ||
140 | - html += containerHtml; | ||
141 | - } else { | ||
142 | - html += containerHtml; | ||
143 | - html += legendHtml; | ||
144 | - } | ||
145 | - html += '</div>'; | ||
146 | - } else { | ||
147 | - html += containerHtml; | ||
148 | - } | ||
149 | - | ||
150 | - //TODO: | ||
151 | - /*if (progressElement) { | ||
152 | - progressScope.$destroy(); | ||
153 | - progressScope = null; | ||
154 | - | ||
155 | - progressElement.remove(); | ||
156 | - progressElement = null; | ||
157 | - }*/ | ||
158 | - | ||
159 | - elem.html(html); | ||
160 | - | ||
161 | - var containerElement = scope.displayLegend ? angular.element(elem[0].querySelector('#widget-container')) : elem; | ||
162 | - | ||
163 | - $compile(elem.contents())(scope); | ||
164 | - | ||
165 | var widgetType = widgetService.getWidgetTypeFunction(widget.bundleAlias, widget.typeAlias, widget.isSystemType); | 84 | var widgetType = widgetService.getWidgetTypeFunction(widget.bundleAlias, widget.typeAlias, widget.isSystemType); |
166 | 85 | ||
167 | - angular.extend(locals, {$scope: scope, $element: containerElement, widgetType: widgetType}); | 86 | + angular.extend(locals, {$scope: scope, $element: elem, widgetInfo: widgetInfo, widgetType: widgetType}); |
168 | 87 | ||
169 | widgetController = $controller('WidgetController', locals); | 88 | widgetController = $controller('WidgetController', locals); |
170 | 89 |
@@ -48,11 +48,16 @@ | @@ -48,11 +48,16 @@ | ||
48 | disable-attribute-scope-selection="true"> | 48 | disable-attribute-scope-selection="true"> |
49 | </tb-attribute-table> | 49 | </tb-attribute-table> |
50 | </md-tab> | 50 | </md-tab> |
51 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}"> | ||
52 | + <tb-alarm-table flex entity-type="vm.types.entityType.customer" | ||
53 | + entity-id="vm.grid.operatingItem().id.id"> | ||
54 | + </tb-alarm-table> | ||
55 | + </md-tab> | ||
51 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'customer.events' | translate }}"> | 56 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'customer.events' | translate }}"> |
52 | <tb-event-table flex entity-type="vm.types.entityType.customer" | 57 | <tb-event-table flex entity-type="vm.types.entityType.customer" |
53 | entity-id="vm.grid.operatingItem().id.id" | 58 | entity-id="vm.grid.operatingItem().id.id" |
54 | tenant-id="vm.grid.operatingItem().tenantId.id" | 59 | tenant-id="vm.grid.operatingItem().tenantId.id" |
55 | - default-event-type="{{vm.types.eventType.alarm.value}}"> | 60 | + default-event-type="{{vm.types.eventType.error.value}}"> |
56 | </tb-event-table> | 61 | </tb-event-table> |
57 | </md-tab> | 62 | </md-tab> |
58 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> | 63 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> |
@@ -15,17 +15,18 @@ | @@ -15,17 +15,18 @@ | ||
15 | */ | 15 | */ |
16 | /* eslint-disable import/no-unresolved, import/default */ | 16 | /* eslint-disable import/no-unresolved, import/default */ |
17 | 17 | ||
18 | -import entityAliasesTemplate from '../entity/entity-aliases.tpl.html'; | 18 | +import entityAliasDialogTemplate from '../entity/alias/entity-alias-dialog.tpl.html'; |
19 | 19 | ||
20 | /* eslint-enable import/no-unresolved, import/default */ | 20 | /* eslint-enable import/no-unresolved, import/default */ |
21 | 21 | ||
22 | /*@ngInject*/ | 22 | /*@ngInject*/ |
23 | -export default function AddWidgetController($scope, widgetService, entityService, $mdDialog, $q, $document, types, dashboard, aliasesInfo, widget, widgetInfo) { | 23 | +export default function AddWidgetController($scope, widgetService, entityService, $mdDialog, $q, $document, types, dashboard, |
24 | + aliasController, widget, widgetInfo) { | ||
24 | 25 | ||
25 | var vm = this; | 26 | var vm = this; |
26 | 27 | ||
27 | vm.dashboard = dashboard; | 28 | vm.dashboard = dashboard; |
28 | - vm.aliasesInfo = aliasesInfo; | 29 | + vm.aliasController = aliasController; |
29 | vm.widget = widget; | 30 | vm.widget = widget; |
30 | vm.widgetInfo = widgetInfo; | 31 | vm.widgetInfo = widgetInfo; |
31 | 32 | ||
@@ -85,7 +86,7 @@ export default function AddWidgetController($scope, widgetService, entityService | @@ -85,7 +86,7 @@ export default function AddWidgetController($scope, widgetService, entityService | ||
85 | } | 86 | } |
86 | 87 | ||
87 | function cancel () { | 88 | function cancel () { |
88 | - $mdDialog.cancel({aliasesInfo: vm.aliasesInfo}); | 89 | + $mdDialog.cancel(); |
89 | } | 90 | } |
90 | 91 | ||
91 | function add () { | 92 | function add () { |
@@ -94,52 +95,58 @@ export default function AddWidgetController($scope, widgetService, entityService | @@ -94,52 +95,58 @@ export default function AddWidgetController($scope, widgetService, entityService | ||
94 | vm.widget.config = vm.widgetConfig.config; | 95 | vm.widget.config = vm.widgetConfig.config; |
95 | vm.widget.config.mobileOrder = vm.widgetConfig.layout.mobileOrder; | 96 | vm.widget.config.mobileOrder = vm.widgetConfig.layout.mobileOrder; |
96 | vm.widget.config.mobileHeight = vm.widgetConfig.layout.mobileHeight; | 97 | vm.widget.config.mobileHeight = vm.widgetConfig.layout.mobileHeight; |
97 | - $mdDialog.hide({widget: vm.widget, aliasesInfo: vm.aliasesInfo}); | 98 | + $mdDialog.hide({widget: vm.widget}); |
98 | } | 99 | } |
99 | } | 100 | } |
100 | 101 | ||
101 | function fetchEntityKeys (entityAliasId, query, type) { | 102 | function fetchEntityKeys (entityAliasId, query, type) { |
102 | - var entityAlias = vm.aliasesInfo.entityAliases[entityAliasId]; | ||
103 | - if (entityAlias && entityAlias.entityId) { | ||
104 | - return entityService.getEntityKeys(entityAlias.entityType, entityAlias.entityId, query, type); | ||
105 | - } else { | ||
106 | - return $q.when([]); | ||
107 | - } | 103 | + var deferred = $q.defer(); |
104 | + vm.aliasController.getAliasInfo(entityAliasId).then( | ||
105 | + function success(aliasInfo) { | ||
106 | + var entity = aliasInfo.currentEntity; | ||
107 | + if (entity) { | ||
108 | + entityService.getEntityKeys(entity.entityType, entity.id, query, type).then( | ||
109 | + function success(keys) { | ||
110 | + deferred.resolve(keys); | ||
111 | + }, | ||
112 | + function fail() { | ||
113 | + deferred.resolve([]); | ||
114 | + } | ||
115 | + ); | ||
116 | + } else { | ||
117 | + deferred.resolve([]); | ||
118 | + } | ||
119 | + }, | ||
120 | + function fail() { | ||
121 | + deferred.resolve([]); | ||
122 | + } | ||
123 | + ); | ||
124 | + return deferred.promise; | ||
108 | } | 125 | } |
109 | 126 | ||
110 | function createEntityAlias (event, alias, allowedEntityTypes) { | 127 | function createEntityAlias (event, alias, allowedEntityTypes) { |
111 | 128 | ||
112 | var deferred = $q.defer(); | 129 | var deferred = $q.defer(); |
113 | - var singleEntityAlias = {id: null, alias: alias, entityType: types.entityType.device, entityFilter: null}; | 130 | + var singleEntityAlias = {id: null, alias: alias, filter: {}}; |
114 | 131 | ||
115 | $mdDialog.show({ | 132 | $mdDialog.show({ |
116 | - controller: 'EntityAliasesController', | 133 | + controller: 'EntityAliasDialogController', |
117 | controllerAs: 'vm', | 134 | controllerAs: 'vm', |
118 | - templateUrl: entityAliasesTemplate, | 135 | + templateUrl: entityAliasDialogTemplate, |
119 | locals: { | 136 | locals: { |
120 | - config: { | ||
121 | - entityAliases: angular.copy(vm.dashboard.configuration.entityAliases), | ||
122 | - widgets: null, | ||
123 | - isSingleEntityAlias: true, | ||
124 | - singleEntityAlias: singleEntityAlias, | ||
125 | - allowedEntityTypes: allowedEntityTypes | ||
126 | - } | 137 | + isAdd: true, |
138 | + allowedEntityTypes: allowedEntityTypes, | ||
139 | + entityAliases: vm.dashboard.configuration.entityAliases, | ||
140 | + alias: singleEntityAlias | ||
127 | }, | 141 | }, |
128 | parent: angular.element($document[0].body), | 142 | parent: angular.element($document[0].body), |
129 | fullscreen: true, | 143 | fullscreen: true, |
130 | skipHide: true, | 144 | skipHide: true, |
131 | targetEvent: event | 145 | targetEvent: event |
132 | }).then(function (singleEntityAlias) { | 146 | }).then(function (singleEntityAlias) { |
133 | - vm.dashboard.configuration.entityAliases[singleEntityAlias.id] = | ||
134 | - { alias: singleEntityAlias.alias, entityType: singleEntityAlias.entityType, entityFilter: singleEntityAlias.entityFilter }; | ||
135 | - entityService.processEntityAliases(vm.dashboard.configuration.entityAliases).then( | ||
136 | - function(resolution) { | ||
137 | - if (!resolution.error) { | ||
138 | - vm.aliasesInfo = resolution.aliasesInfo; | ||
139 | - } | ||
140 | - deferred.resolve(singleEntityAlias); | ||
141 | - } | ||
142 | - ); | 147 | + vm.dashboard.configuration.entityAliases[singleEntityAlias.id] = singleEntityAlias; |
148 | + vm.aliasController.updateEntityAliases(vm.dashboard.configuration.entityAliases); | ||
149 | + deferred.resolve(singleEntityAlias); | ||
143 | }, function () { | 150 | }, function () { |
144 | deferred.reject(); | 151 | deferred.reject(); |
145 | }); | 152 | }); |
@@ -37,7 +37,7 @@ | @@ -37,7 +37,7 @@ | ||
37 | ng-model="vm.widgetConfig" | 37 | ng-model="vm.widgetConfig" |
38 | widget-settings-schema="vm.settingsSchema" | 38 | widget-settings-schema="vm.settingsSchema" |
39 | datakey-settings-schema="vm.dataKeySettingsSchema" | 39 | datakey-settings-schema="vm.dataKeySettingsSchema" |
40 | - entity-aliases="vm.aliasesInfo.entityAliases" | 40 | + alias-controller="vm.aliasController" |
41 | functions-only="vm.functionsOnly" | 41 | functions-only="vm.functionsOnly" |
42 | fetch-entity-keys="vm.fetchEntityKeys(entityAliasId, query, type)" | 42 | fetch-entity-keys="vm.fetchEntityKeys(entityAliasId, query, type)" |
43 | on-create-entity-alias="vm.createEntityAlias(event, alias, allowedEntityTypes)" | 43 | on-create-entity-alias="vm.createEntityAlias(event, alias, allowedEntityTypes)" |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | /* eslint-disable import/no-unresolved, import/default */ | 16 | /* eslint-disable import/no-unresolved, import/default */ |
17 | 17 | ||
18 | -import entityAliasesTemplate from '../entity/entity-aliases.tpl.html'; | 18 | +import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; |
19 | import dashboardSettingsTemplate from './dashboard-settings.tpl.html'; | 19 | import dashboardSettingsTemplate from './dashboard-settings.tpl.html'; |
20 | import manageDashboardLayoutsTemplate from './layouts/manage-dashboard-layouts.tpl.html'; | 20 | import manageDashboardLayoutsTemplate from './layouts/manage-dashboard-layouts.tpl.html'; |
21 | import manageDashboardStatesTemplate from './states/manage-dashboard-states.tpl.html'; | 21 | import manageDashboardStatesTemplate from './states/manage-dashboard-states.tpl.html'; |
@@ -24,8 +24,10 @@ import selectTargetLayoutTemplate from './layouts/select-target-layout.tpl.html' | @@ -24,8 +24,10 @@ import selectTargetLayoutTemplate from './layouts/select-target-layout.tpl.html' | ||
24 | 24 | ||
25 | /* eslint-enable import/no-unresolved, import/default */ | 25 | /* eslint-enable import/no-unresolved, import/default */ |
26 | 26 | ||
27 | +import AliasController from '../api/alias-controller'; | ||
28 | + | ||
27 | /*@ngInject*/ | 29 | /*@ngInject*/ |
28 | -export default function DashboardController(types, dashboardUtils, widgetService, userService, | 30 | +export default function DashboardController(types, utils, dashboardUtils, widgetService, userService, |
29 | dashboardService, timeService, entityService, itembuffer, importExport, hotkeys, $window, $rootScope, | 31 | dashboardService, timeService, entityService, itembuffer, importExport, hotkeys, $window, $rootScope, |
30 | $scope, $element, $state, $stateParams, $mdDialog, $mdMedia, $timeout, $document, $q, $translate, $filter) { | 32 | $scope, $element, $state, $stateParams, $mdDialog, $mdMedia, $timeout, $document, $q, $translate, $filter) { |
31 | 33 | ||
@@ -342,6 +344,8 @@ export default function DashboardController(types, dashboardUtils, widgetService | @@ -342,6 +344,8 @@ export default function DashboardController(types, dashboardUtils, widgetService | ||
342 | vm.dashboardConfiguration = vm.dashboard.configuration; | 344 | vm.dashboardConfiguration = vm.dashboard.configuration; |
343 | vm.dashboardCtx.dashboard = vm.dashboard; | 345 | vm.dashboardCtx.dashboard = vm.dashboard; |
344 | vm.dashboardCtx.dashboardTimewindow = vm.dashboardConfiguration.timewindow; | 346 | vm.dashboardCtx.dashboardTimewindow = vm.dashboardConfiguration.timewindow; |
347 | + vm.dashboardCtx.aliasController = new AliasController($scope, $q, $filter, utils, | ||
348 | + types, entityService, vm.dashboardCtx.stateController, vm.dashboardConfiguration.entityAliases); | ||
345 | var parentScope = $window.parent.angular.element($window.frameElement).scope(); | 349 | var parentScope = $window.parent.angular.element($window.frameElement).scope(); |
346 | parentScope.$root.$broadcast('widgetEditModeInited'); | 350 | parentScope.$root.$broadcast('widgetEditModeInited'); |
347 | parentScope.$root.$apply(); | 351 | parentScope.$root.$apply(); |
@@ -349,7 +353,13 @@ export default function DashboardController(types, dashboardUtils, widgetService | @@ -349,7 +353,13 @@ export default function DashboardController(types, dashboardUtils, widgetService | ||
349 | dashboardService.getDashboard($stateParams.dashboardId) | 353 | dashboardService.getDashboard($stateParams.dashboardId) |
350 | .then(function success(dashboard) { | 354 | .then(function success(dashboard) { |
351 | vm.dashboard = dashboardUtils.validateAndUpdateDashboard(dashboard); | 355 | vm.dashboard = dashboardUtils.validateAndUpdateDashboard(dashboard); |
352 | - entityService.processEntityAliases(vm.dashboard.configuration.entityAliases) | 356 | + vm.dashboardConfiguration = vm.dashboard.configuration; |
357 | + vm.dashboardCtx.dashboard = vm.dashboard; | ||
358 | + vm.dashboardCtx.dashboardTimewindow = vm.dashboardConfiguration.timewindow; | ||
359 | + vm.dashboardCtx.aliasController = new AliasController($scope, $q, $filter, utils, | ||
360 | + types, entityService, vm.dashboardCtx.stateController, vm.dashboardConfiguration.entityAliases); | ||
361 | + | ||
362 | + /* entityService.processEntityAliases(vm.dashboard.configuration.entityAliases) | ||
353 | .then( | 363 | .then( |
354 | function(resolution) { | 364 | function(resolution) { |
355 | if (resolution.error && !isTenantAdmin()) { | 365 | if (resolution.error && !isTenantAdmin()) { |
@@ -362,7 +372,7 @@ export default function DashboardController(types, dashboardUtils, widgetService | @@ -362,7 +372,7 @@ export default function DashboardController(types, dashboardUtils, widgetService | ||
362 | vm.dashboardCtx.dashboardTimewindow = vm.dashboardConfiguration.timewindow; | 372 | vm.dashboardCtx.dashboardTimewindow = vm.dashboardConfiguration.timewindow; |
363 | } | 373 | } |
364 | } | 374 | } |
365 | - ); | 375 | + );*/ |
366 | }, function fail() { | 376 | }, function fail() { |
367 | vm.configurationError = true; | 377 | vm.configurationError = true; |
368 | }); | 378 | }); |
@@ -373,6 +383,7 @@ export default function DashboardController(types, dashboardUtils, widgetService | @@ -373,6 +383,7 @@ export default function DashboardController(types, dashboardUtils, widgetService | ||
373 | var layoutsData = dashboardUtils.getStateLayoutsData(vm.dashboard, state); | 383 | var layoutsData = dashboardUtils.getStateLayoutsData(vm.dashboard, state); |
374 | if (layoutsData) { | 384 | if (layoutsData) { |
375 | vm.dashboardCtx.state = state; | 385 | vm.dashboardCtx.state = state; |
386 | + vm.dashboardCtx.aliasController.dashboardStateChanged(); | ||
376 | var layoutVisibilityChanged = false; | 387 | var layoutVisibilityChanged = false; |
377 | for (var l in vm.layouts) { | 388 | for (var l in vm.layouts) { |
378 | var layout = vm.layouts[l]; | 389 | var layout = vm.layouts[l]; |
@@ -916,7 +927,7 @@ export default function DashboardController(types, dashboardUtils, widgetService | @@ -916,7 +927,7 @@ export default function DashboardController(types, dashboardUtils, widgetService | ||
916 | templateUrl: addWidgetTemplate, | 927 | templateUrl: addWidgetTemplate, |
917 | locals: { | 928 | locals: { |
918 | dashboard: vm.dashboard, | 929 | dashboard: vm.dashboard, |
919 | - aliasesInfo: vm.dashboardCtx.aliasesInfo, | 930 | + aliasController: vm.dashboardCtx.aliasController, |
920 | widget: newWidget, | 931 | widget: newWidget, |
921 | widgetInfo: widgetTypeInfo | 932 | widgetInfo: widgetTypeInfo |
922 | }, | 933 | }, |
@@ -930,10 +941,8 @@ export default function DashboardController(types, dashboardUtils, widgetService | @@ -930,10 +941,8 @@ export default function DashboardController(types, dashboardUtils, widgetService | ||
930 | } | 941 | } |
931 | }).then(function (result) { | 942 | }).then(function (result) { |
932 | var widget = result.widget; | 943 | var widget = result.widget; |
933 | - vm.dashboardCtx.aliasesInfo = result.aliasesInfo; | ||
934 | addWidget(widget); | 944 | addWidget(widget); |
935 | - }, function (rejection) { | ||
936 | - vm.dashboardCtx.aliasesInfo = rejection.aliasesInfo; | 945 | + }, function () { |
937 | }); | 946 | }); |
938 | } | 947 | } |
939 | } | 948 | } |
@@ -1025,7 +1034,7 @@ export default function DashboardController(types, dashboardUtils, widgetService | @@ -1025,7 +1034,7 @@ export default function DashboardController(types, dashboardUtils, widgetService | ||
1025 | notifyDashboardUpdated(); | 1034 | notifyDashboardUpdated(); |
1026 | } | 1035 | } |
1027 | 1036 | ||
1028 | - function showAliasesResolutionError(error) { | 1037 | +/* function showAliasesResolutionError(error) { |
1029 | var alert = $mdDialog.alert() | 1038 | var alert = $mdDialog.alert() |
1030 | .parent(angular.element($document[0].body)) | 1039 | .parent(angular.element($document[0].body)) |
1031 | .clickOutsideToClose(true) | 1040 | .clickOutsideToClose(true) |
@@ -1037,20 +1046,10 @@ export default function DashboardController(types, dashboardUtils, widgetService | @@ -1037,20 +1046,10 @@ export default function DashboardController(types, dashboardUtils, widgetService | ||
1037 | alert._options.fullscreen = true; | 1046 | alert._options.fullscreen = true; |
1038 | 1047 | ||
1039 | $mdDialog.show(alert); | 1048 | $mdDialog.show(alert); |
1040 | - } | 1049 | + }*/ |
1041 | 1050 | ||
1042 | function entityAliasesUpdated() { | 1051 | function entityAliasesUpdated() { |
1043 | - var deferred = $q.defer(); | ||
1044 | - entityService.processEntityAliases(vm.dashboard.configuration.entityAliases) | ||
1045 | - .then( | ||
1046 | - function(resolution) { | ||
1047 | - if (resolution.aliasesInfo) { | ||
1048 | - vm.dashboardCtx.aliasesInfo = resolution.aliasesInfo; | ||
1049 | - } | ||
1050 | - deferred.resolve(); | ||
1051 | - } | ||
1052 | - ); | ||
1053 | - return deferred.promise; | 1052 | + vm.dashboardCtx.aliasController.updateEntityAliases(vm.dashboard.configuration.entityAliases); |
1054 | } | 1053 | } |
1055 | 1054 | ||
1056 | function notifyDashboardUpdated() { | 1055 | function notifyDashboardUpdated() { |
@@ -57,8 +57,7 @@ | @@ -57,8 +57,7 @@ | ||
57 | </tb-timewindow> | 57 | </tb-timewindow> |
58 | <tb-aliases-entity-select ng-show="!vm.isEdit && vm.displayEntitiesSelect()" | 58 | <tb-aliases-entity-select ng-show="!vm.isEdit && vm.displayEntitiesSelect()" |
59 | tooltip-direction="bottom" | 59 | tooltip-direction="bottom" |
60 | - ng-model="vm.dashboardCtx.aliasesInfo.entityAliases" | ||
61 | - entity-aliases-info="vm.dashboardCtx.aliasesInfo.entityAliasesInfo"> | 60 | + alias-controller="vm.dashboardCtx.aliasController"> |
62 | </tb-aliases-entity-select> | 61 | </tb-aliases-entity-select> |
63 | <md-button ng-show="vm.isEdit" aria-label="{{ 'entity.aliases' | translate }}" class="md-icon-button" | 62 | <md-button ng-show="vm.isEdit" aria-label="{{ 'entity.aliases' | translate }}" class="md-icon-button" |
64 | ng-click="vm.openEntityAliases($event)"> | 63 | ng-click="vm.openEntityAliases($event)"> |
@@ -179,7 +178,7 @@ | @@ -179,7 +178,7 @@ | ||
179 | <form name="vm.widgetForm" ng-if="vm.isEditingWidget"> | 178 | <form name="vm.widgetForm" ng-if="vm.isEditingWidget"> |
180 | <tb-edit-widget | 179 | <tb-edit-widget |
181 | dashboard="vm.dashboard" | 180 | dashboard="vm.dashboard" |
182 | - aliases-info="vm.dashboardCtx.aliasesInfo" | 181 | + alias-controller="vm.dashboardCtx.aliasController" |
183 | widget="vm.editingWidget" | 182 | widget="vm.editingWidget" |
184 | widget-layout="vm.editingWidgetLayout" | 183 | widget-layout="vm.editingWidgetLayout" |
185 | the-form="vm.widgetForm"> | 184 | the-form="vm.widgetForm"> |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | /* eslint-disable import/no-unresolved, import/default */ | 16 | /* eslint-disable import/no-unresolved, import/default */ |
17 | 17 | ||
18 | -import entityAliasesTemplate from '../entity/entity-aliases.tpl.html'; | 18 | +import entityAliasDialogTemplate from '../entity/alias/entity-alias-dialog.tpl.html'; |
19 | import editWidgetTemplate from './edit-widget.tpl.html'; | 19 | import editWidgetTemplate from './edit-widget.tpl.html'; |
20 | 20 | ||
21 | /* eslint-enable import/no-unresolved, import/default */ | 21 | /* eslint-enable import/no-unresolved, import/default */ |
@@ -68,47 +68,53 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid | @@ -68,47 +68,53 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid | ||
68 | }); | 68 | }); |
69 | 69 | ||
70 | scope.fetchEntityKeys = function (entityAliasId, query, type) { | 70 | scope.fetchEntityKeys = function (entityAliasId, query, type) { |
71 | - var entityAlias = scope.aliasesInfo.entityAliases[entityAliasId]; | ||
72 | - if (entityAlias && entityAlias.entityId) { | ||
73 | - return entityService.getEntityKeys(entityAlias.entityType, entityAlias.entityId, query, type); | ||
74 | - } else { | ||
75 | - return $q.when([]); | ||
76 | - } | 71 | + var deferred = $q.defer(); |
72 | + scope.aliasController.getAliasInfo(entityAliasId).then( | ||
73 | + function success(aliasInfo) { | ||
74 | + var entity = aliasInfo.currentEntity; | ||
75 | + if (entity) { | ||
76 | + entityService.getEntityKeys(entity.entityType, entity.id, query, type).then( | ||
77 | + function success(keys) { | ||
78 | + deferred.resolve(keys); | ||
79 | + }, | ||
80 | + function fail() { | ||
81 | + deferred.resolve([]); | ||
82 | + } | ||
83 | + ); | ||
84 | + } else { | ||
85 | + deferred.resolve([]); | ||
86 | + } | ||
87 | + }, | ||
88 | + function fail() { | ||
89 | + deferred.resolve([]); | ||
90 | + } | ||
91 | + ); | ||
92 | + return deferred.promise; | ||
77 | }; | 93 | }; |
78 | 94 | ||
79 | scope.createEntityAlias = function (event, alias, allowedEntityTypes) { | 95 | scope.createEntityAlias = function (event, alias, allowedEntityTypes) { |
80 | 96 | ||
81 | var deferred = $q.defer(); | 97 | var deferred = $q.defer(); |
82 | - var singleEntityAlias = {id: null, alias: alias, entityType: types.entityType.device, entityFilter: null}; | 98 | + var singleEntityAlias = {id: null, alias: alias, filter: {}}; |
83 | 99 | ||
84 | $mdDialog.show({ | 100 | $mdDialog.show({ |
85 | - controller: 'EntityAliasesController', | 101 | + controller: 'EntityAliasDialogController', |
86 | controllerAs: 'vm', | 102 | controllerAs: 'vm', |
87 | - templateUrl: entityAliasesTemplate, | 103 | + templateUrl: entityAliasDialogTemplate, |
88 | locals: { | 104 | locals: { |
89 | - config: { | ||
90 | - entityAliases: angular.copy(scope.dashboard.configuration.entityAliases), | ||
91 | - widgets: null, | ||
92 | - isSingleEntityAlias: true, | ||
93 | - singleEntityAlias: singleEntityAlias, | ||
94 | - allowedEntityTypes: allowedEntityTypes | ||
95 | - } | 105 | + isAdd: true, |
106 | + allowedEntityTypes: allowedEntityTypes, | ||
107 | + entityAliases: scope.dashboard.configuration.entityAliases, | ||
108 | + alias: singleEntityAlias | ||
96 | }, | 109 | }, |
97 | parent: angular.element($document[0].body), | 110 | parent: angular.element($document[0].body), |
98 | fullscreen: true, | 111 | fullscreen: true, |
99 | skipHide: true, | 112 | skipHide: true, |
100 | targetEvent: event | 113 | targetEvent: event |
101 | }).then(function (singleEntityAlias) { | 114 | }).then(function (singleEntityAlias) { |
102 | - scope.dashboard.configuration.entityAliases[singleEntityAlias.id] = | ||
103 | - { alias: singleEntityAlias.alias, entityType: singleEntityAlias.entityType, entityFilter: singleEntityAlias.entityFilter }; | ||
104 | - entityService.processEntityAliases(scope.dashboard.configuration.entityAliases).then( | ||
105 | - function(resolution) { | ||
106 | - if (!resolution.error) { | ||
107 | - scope.aliasesInfo = resolution.aliasesInfo; | ||
108 | - } | ||
109 | - deferred.resolve(singleEntityAlias); | ||
110 | - } | ||
111 | - ); | 115 | + scope.dashboard.configuration.entityAliases[singleEntityAlias.id] = singleEntityAlias; |
116 | + scope.aliasController.updateEntityAliases(scope.dashboard.configuration.entityAliases); | ||
117 | + deferred.resolve(singleEntityAlias); | ||
112 | }, function () { | 118 | }, function () { |
113 | deferred.reject(); | 119 | deferred.reject(); |
114 | }); | 120 | }); |
@@ -124,7 +130,7 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid | @@ -124,7 +130,7 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid | ||
124 | link: linker, | 130 | link: linker, |
125 | scope: { | 131 | scope: { |
126 | dashboard: '=', | 132 | dashboard: '=', |
127 | - aliasesInfo: '=', | 133 | + aliasController: '=', |
128 | widget: '=', | 134 | widget: '=', |
129 | widgetLayout: '=', | 135 | widgetLayout: '=', |
130 | theForm: '=' | 136 | theForm: '=' |
@@ -21,7 +21,7 @@ | @@ -21,7 +21,7 @@ | ||
21 | is-data-enabled="isDataEnabled" | 21 | is-data-enabled="isDataEnabled" |
22 | widget-settings-schema="settingsSchema" | 22 | widget-settings-schema="settingsSchema" |
23 | datakey-settings-schema="dataKeySettingsSchema" | 23 | datakey-settings-schema="dataKeySettingsSchema" |
24 | - entity-aliases="aliasesInfo.entityAliases" | 24 | + alias-controller="aliasController" |
25 | functions-only="functionsOnly" | 25 | functions-only="functionsOnly" |
26 | fetch-entity-keys="fetchEntityKeys(entityAliasId, query, type)" | 26 | fetch-entity-keys="fetchEntityKeys(entityAliasId, query, type)" |
27 | on-create-entity-alias="createEntityAlias(event, alias, allowedEntityTypes)" | 27 | on-create-entity-alias="createEntityAlias(event, alias, allowedEntityTypes)" |
@@ -45,7 +45,7 @@ | @@ -45,7 +45,7 @@ | ||
45 | widget-layouts="vm.layoutCtx.widgetLayouts" | 45 | widget-layouts="vm.layoutCtx.widgetLayouts" |
46 | columns="vm.layoutCtx.gridSettings.columns" | 46 | columns="vm.layoutCtx.gridSettings.columns" |
47 | margins="vm.layoutCtx.gridSettings.margins" | 47 | margins="vm.layoutCtx.gridSettings.margins" |
48 | - aliases-info="vm.dashboardCtx.aliasesInfo" | 48 | + alias-controller="vm.dashboardCtx.aliasController" |
49 | state-controller="vm.dashboardCtx.stateController" | 49 | state-controller="vm.dashboardCtx.stateController" |
50 | dashboard-timewindow="vm.dashboardCtx.dashboardTimewindow" | 50 | dashboard-timewindow="vm.dashboardCtx.dashboardTimewindow" |
51 | is-edit="vm.isEdit" | 51 | is-edit="vm.isEdit" |
@@ -49,11 +49,16 @@ | @@ -49,11 +49,16 @@ | ||
49 | disable-attribute-scope-selection="true"> | 49 | disable-attribute-scope-selection="true"> |
50 | </tb-attribute-table> | 50 | </tb-attribute-table> |
51 | </md-tab> | 51 | </md-tab> |
52 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}"> | ||
53 | + <tb-alarm-table flex entity-type="vm.types.entityType.device" | ||
54 | + entity-id="vm.grid.operatingItem().id.id"> | ||
55 | + </tb-alarm-table> | ||
56 | + </md-tab> | ||
52 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'device.events' | translate }}"> | 57 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'device.events' | translate }}"> |
53 | <tb-event-table flex entity-type="vm.types.entityType.device" | 58 | <tb-event-table flex entity-type="vm.types.entityType.device" |
54 | entity-id="vm.grid.operatingItem().id.id" | 59 | entity-id="vm.grid.operatingItem().id.id" |
55 | tenant-id="vm.grid.operatingItem().tenantId.id" | 60 | tenant-id="vm.grid.operatingItem().tenantId.id" |
56 | - default-event-type="{{vm.types.eventType.alarm.value}}"> | 61 | + default-event-type="{{vm.types.eventType.error.value}}"> |
57 | </tb-event-table> | 62 | </tb-event-table> |
58 | </md-tab> | 63 | </md-tab> |
59 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> | 64 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> |
@@ -15,7 +15,6 @@ | @@ -15,7 +15,6 @@ | ||
15 | */ | 15 | */ |
16 | import uiRouter from 'angular-ui-router'; | 16 | import uiRouter from 'angular-ui-router'; |
17 | import thingsboardGrid from '../components/grid.directive'; | 17 | import thingsboardGrid from '../components/grid.directive'; |
18 | -import thingsboardEvent from '../event'; | ||
19 | import thingsboardApiUser from '../api/user.service'; | 18 | import thingsboardApiUser from '../api/user.service'; |
20 | import thingsboardApiDevice from '../api/device.service'; | 19 | import thingsboardApiDevice from '../api/device.service'; |
21 | import thingsboardApiCustomer from '../api/customer.service'; | 20 | import thingsboardApiCustomer from '../api/customer.service'; |
@@ -30,7 +29,6 @@ import DeviceDirective from './device.directive'; | @@ -30,7 +29,6 @@ import DeviceDirective from './device.directive'; | ||
30 | export default angular.module('thingsboard.device', [ | 29 | export default angular.module('thingsboard.device', [ |
31 | uiRouter, | 30 | uiRouter, |
32 | thingsboardGrid, | 31 | thingsboardGrid, |
33 | - thingsboardEvent, | ||
34 | thingsboardApiUser, | 32 | thingsboardApiUser, |
35 | thingsboardApiDevice, | 33 | thingsboardApiDevice, |
36 | thingsboardApiCustomer | 34 | thingsboardApiCustomer |
ui/src/app/entity/alias/aliases-entity-select-button.tpl.html
renamed from
ui/src/app/entity/aliases-entity-select-button.tpl.html
1 | +/* | ||
2 | + * Copyright © 2016-2017 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 | +/*@ngInject*/ | ||
18 | +export default function AliasesEntitySelectPanelController(mdPanelRef, $scope, $filter, types, aliasController, onEntityAliasesUpdate) { | ||
19 | + | ||
20 | + var vm = this; | ||
21 | + vm._mdPanelRef = mdPanelRef; | ||
22 | + vm.aliasController = aliasController; | ||
23 | + vm.onEntityAliasesUpdate = onEntityAliasesUpdate; | ||
24 | + vm.entityAliases = {}; | ||
25 | + vm.entityAliasesInfo = {}; | ||
26 | + | ||
27 | + vm.currentAliasEntityChanged = currentAliasEntityChanged; | ||
28 | + | ||
29 | + var allEntityAliases = vm.aliasController.getEntityAliases(); | ||
30 | + for (var aliasId in allEntityAliases) { | ||
31 | + var aliasInfo = vm.aliasController.getInstantAliasInfo(aliasId); | ||
32 | + if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity) { | ||
33 | + vm.entityAliasesInfo[aliasId] = angular.copy(aliasInfo); | ||
34 | + vm.entityAliasesInfo[aliasId].selectedId = aliasInfo.currentEntity.id; | ||
35 | + } | ||
36 | + } | ||
37 | + | ||
38 | + function currentAliasEntityChanged(aliasId, selectedId) { | ||
39 | + var resolvedEntities = vm.entityAliasesInfo[aliasId].resolvedEntities; | ||
40 | + var selected = $filter('filter')(resolvedEntities, {id: selectedId}); | ||
41 | + if (selected && selected.length) { | ||
42 | + vm.aliasController.updateCurrentAliasEntity(aliasId, selected[0]); | ||
43 | + if (onEntityAliasesUpdate) { | ||
44 | + onEntityAliasesUpdate(); | ||
45 | + } | ||
46 | + } | ||
47 | + } | ||
48 | + | ||
49 | +} |
ui/src/app/entity/alias/aliases-entity-select-panel.tpl.html
renamed from
ui/src/app/entity/aliases-entity-select-panel.tpl.html
@@ -18,12 +18,12 @@ | @@ -18,12 +18,12 @@ | ||
18 | <md-content flex layout="column"> | 18 | <md-content flex layout="column"> |
19 | <section flex layout="column"> | 19 | <section flex layout="column"> |
20 | <md-content flex class="md-padding" layout="column"> | 20 | <md-content flex class="md-padding" layout="column"> |
21 | - <div flex layout="row" ng-repeat="(aliasId, entityAlias) in vm.entityAliases"> | 21 | + <div flex layout="row" ng-repeat="(aliasId, entityAliasInfo) in vm.entityAliasesInfo"> |
22 | <md-input-container flex> | 22 | <md-input-container flex> |
23 | - <label>{{entityAlias.alias}}</label> | ||
24 | - <md-select ng-model="vm.entityAliases[aliasId].entityId"> | ||
25 | - <md-option ng-repeat="entityInfo in vm.entityAliasesInfo[aliasId]" ng-value="entityInfo.id"> | ||
26 | - {{entityInfo.name}} | 23 | + <label>{{entityAliasInfo.alias}}</label> |
24 | + <md-select ng-model="entityAliasInfo.selectedId" ng-change="vm.currentAliasEntityChanged(aliasId, entityAliasInfo.selectedId)"> | ||
25 | + <md-option ng-repeat="resolvedEntity in entityAliasInfo.resolvedEntities" ng-value="resolvedEntity.id"> | ||
26 | + {{resolvedEntity.name}} | ||
27 | </md-option> | 27 | </md-option> |
28 | </md-select> | 28 | </md-select> |
29 | </md-input-container> | 29 | </md-input-container> |
ui/src/app/entity/alias/aliases-entity-select.directive.js
renamed from
ui/src/app/entity/aliases-entity-select.directive.js
@@ -29,7 +29,7 @@ import aliasesEntitySelectPanelTemplate from './aliases-entity-select-panel.tpl. | @@ -29,7 +29,7 @@ import aliasesEntitySelectPanelTemplate from './aliases-entity-select-panel.tpl. | ||
29 | /*@ngInject*/ | 29 | /*@ngInject*/ |
30 | export default function AliasesEntitySelectDirective($compile, $templateCache, $mdMedia, types, $mdPanel, $document, $translate) { | 30 | export default function AliasesEntitySelectDirective($compile, $templateCache, $mdMedia, types, $mdPanel, $document, $translate) { |
31 | 31 | ||
32 | - var linker = function (scope, element, attrs, ngModelCtrl) { | 32 | + var linker = function (scope, element, attrs) { |
33 | 33 | ||
34 | /* tbAliasesEntitySelect (ng-model) | 34 | /* tbAliasesEntitySelect (ng-model) |
35 | * { | 35 | * { |
@@ -81,10 +81,8 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $ | @@ -81,10 +81,8 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $ | ||
81 | position: position, | 81 | position: position, |
82 | fullscreen: false, | 82 | fullscreen: false, |
83 | locals: { | 83 | locals: { |
84 | - 'entityAliases': angular.copy(scope.model), | ||
85 | - 'entityAliasesInfo': scope.entityAliasesInfo, | ||
86 | - 'onEntityAliasesUpdate': function (entityAliases) { | ||
87 | - scope.model = entityAliases; | 84 | + 'aliasController': scope.aliasController, |
85 | + 'onEntityAliasesUpdate': function () { | ||
88 | scope.updateView(); | 86 | scope.updateView(); |
89 | } | 87 | } |
90 | }, | 88 | }, |
@@ -96,41 +94,40 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $ | @@ -96,41 +94,40 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $ | ||
96 | $mdPanel.open(config); | 94 | $mdPanel.open(config); |
97 | } | 95 | } |
98 | 96 | ||
97 | + scope.$on('entityAliasesChanged', function() { | ||
98 | + scope.updateView(); | ||
99 | + }); | ||
100 | + | ||
101 | + scope.$on('entityAliasResolved', function() { | ||
102 | + scope.updateView(); | ||
103 | + }); | ||
104 | + | ||
99 | scope.updateView = function () { | 105 | scope.updateView = function () { |
100 | - var value = angular.copy(scope.model); | ||
101 | - ngModelCtrl.$setViewValue(value); | ||
102 | updateDisplayValue(); | 106 | updateDisplayValue(); |
103 | } | 107 | } |
104 | 108 | ||
105 | - ngModelCtrl.$render = function () { | ||
106 | - if (ngModelCtrl.$viewValue) { | ||
107 | - var value = ngModelCtrl.$viewValue; | ||
108 | - scope.model = angular.copy(value); | ||
109 | - updateDisplayValue(); | ||
110 | - } | ||
111 | - } | ||
112 | - | ||
113 | function updateDisplayValue() { | 109 | function updateDisplayValue() { |
114 | var displayValue; | 110 | var displayValue; |
115 | var singleValue = true; | 111 | var singleValue = true; |
116 | var currentAliasId; | 112 | var currentAliasId; |
117 | - for (var aliasId in scope.model) { | ||
118 | - if (!currentAliasId) { | ||
119 | - currentAliasId = aliasId; | ||
120 | - } else { | ||
121 | - singleValue = false; | ||
122 | - break; | 113 | + var entityAliases = scope.aliasController.getEntityAliases(); |
114 | + for (var aliasId in entityAliases) { | ||
115 | + var entityAlias = entityAliases[aliasId]; | ||
116 | + if (!entityAlias.filter.resolveMultiple) { | ||
117 | + var resolvedAlias = scope.aliasController.getInstantAliasInfo(aliasId); | ||
118 | + if (resolvedAlias && resolvedAlias.currentEntity) { | ||
119 | + if (!currentAliasId) { | ||
120 | + currentAliasId = aliasId; | ||
121 | + } else { | ||
122 | + singleValue = false; | ||
123 | + break; | ||
124 | + } | ||
125 | + } | ||
123 | } | 126 | } |
124 | } | 127 | } |
125 | if (singleValue && currentAliasId) { | 128 | if (singleValue && currentAliasId) { |
126 | - var entityId = scope.model[currentAliasId].entityId; | ||
127 | - var entitiesInfo = scope.entityAliasesInfo[currentAliasId]; | ||
128 | - for (var i=0;i<entitiesInfo.length;i++) { | ||
129 | - if (entitiesInfo[i].id === entityId) { | ||
130 | - displayValue = entitiesInfo[i].name; | ||
131 | - break; | ||
132 | - } | ||
133 | - } | 129 | + var aliasInfo = scope.aliasController.getInstantAliasInfo(currentAliasId); |
130 | + displayValue = aliasInfo.currentEntity.name; | ||
134 | } else { | 131 | } else { |
135 | displayValue = $translate.instant('entity.entities'); | 132 | displayValue = $translate.instant('entity.entities'); |
136 | } | 133 | } |
@@ -142,9 +139,8 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $ | @@ -142,9 +139,8 @@ export default function AliasesEntitySelectDirective($compile, $templateCache, $ | ||
142 | 139 | ||
143 | return { | 140 | return { |
144 | restrict: "E", | 141 | restrict: "E", |
145 | - require: "^ngModel", | ||
146 | scope: { | 142 | scope: { |
147 | - entityAliasesInfo:'=' | 143 | + aliasController:'=' |
148 | }, | 144 | }, |
149 | link: linker | 145 | link: linker |
150 | }; | 146 | }; |
ui/src/app/entity/alias/aliases-entity-select.scss
renamed from
ui/src/app/entity/aliases-entity-select.scss