Showing
18 changed files
with
771 additions
and
217 deletions
@@ -256,7 +256,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | @@ -256,7 +256,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | ||
256 | } else { | 256 | } else { |
257 | entitiesSortOrder = sortOrder; | 257 | entitiesSortOrder = sortOrder; |
258 | } | 258 | } |
259 | - EntityDataPageLink edpl = new EntityDataPageLink(0, maxEntitiesPerAlarmSubscription, null, entitiesSortOrder); | 259 | + EntityDataPageLink edpl = new EntityDataPageLink(maxEntitiesPerAlarmSubscription, 0, null, entitiesSortOrder); |
260 | EntityDataQuery edq = new EntityDataQuery(adq.getEntityFilter(), edpl, adq.getEntityFields(), adq.getLatestValues(), adq.getKeyFilters()); | 260 | EntityDataQuery edq = new EntityDataQuery(adq.getEntityFilter(), edpl, adq.getEntityFields(), adq.getLatestValues(), adq.getKeyFilters()); |
261 | PageData<EntityData> entitiesData = entityService.findEntityDataByQuery(ctx.getTenantId(), ctx.getCustomerId(), edq); | 261 | PageData<EntityData> entitiesData = entityService.findEntityDataByQuery(ctx.getTenantId(), ctx.getCustomerId(), edq); |
262 | List<EntityData> entities = entitiesData.getData(); | 262 | List<EntityData> entities = entitiesData.getData(); |
@@ -35,6 +35,11 @@ public class AlarmDataUpdate extends DataUpdate<AlarmData> { | @@ -35,6 +35,11 @@ public class AlarmDataUpdate extends DataUpdate<AlarmData> { | ||
35 | super(cmdId, null, null, errorCode, errorMsg); | 35 | super(cmdId, null, null, errorCode, errorMsg); |
36 | } | 36 | } |
37 | 37 | ||
38 | + @Override | ||
39 | + public DataUpdateType getDataUpdateType() { | ||
40 | + return DataUpdateType.ALARM_DATA; | ||
41 | + } | ||
42 | + | ||
38 | @JsonCreator | 43 | @JsonCreator |
39 | public AlarmDataUpdate(@JsonProperty("cmdId") int cmdId, | 44 | public AlarmDataUpdate(@JsonProperty("cmdId") int cmdId, |
40 | @JsonProperty("data") PageData<AlarmData> data, | 45 | @JsonProperty("data") PageData<AlarmData> data, |
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/DataUpdateType.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 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.service.telemetry.cmd.v2; | ||
17 | + | ||
18 | +public enum DataUpdateType { | ||
19 | + ENTITY_DATA, | ||
20 | + ALARM_DATA | ||
21 | +} |
@@ -33,6 +33,11 @@ public class EntityDataUpdate extends DataUpdate<EntityData> { | @@ -33,6 +33,11 @@ public class EntityDataUpdate extends DataUpdate<EntityData> { | ||
33 | super(cmdId, null, null, errorCode, errorMsg); | 33 | super(cmdId, null, null, errorCode, errorMsg); |
34 | } | 34 | } |
35 | 35 | ||
36 | + @Override | ||
37 | + public DataUpdateType getDataUpdateType() { | ||
38 | + return DataUpdateType.ENTITY_DATA; | ||
39 | + } | ||
40 | + | ||
36 | @JsonCreator | 41 | @JsonCreator |
37 | public EntityDataUpdate(@JsonProperty("cmdId") int cmdId, | 42 | public EntityDataUpdate(@JsonProperty("cmdId") int cmdId, |
38 | @JsonProperty("data") PageData<EntityData> data, | 43 | @JsonProperty("data") PageData<EntityData> data, |
1 | +/// | ||
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { | ||
18 | + AlarmDataCmd, | ||
19 | + DataKeyType, | ||
20 | + TelemetryService, | ||
21 | + TelemetrySubscriber | ||
22 | +} from '@shared/models/telemetry/telemetry.models'; | ||
23 | +import { DatasourceType } from '@shared/models/widget.models'; | ||
24 | +import { | ||
25 | + AlarmData, | ||
26 | + AlarmDataPageLink, | ||
27 | + EntityFilter, | ||
28 | + EntityKey, | ||
29 | + EntityKeyType, | ||
30 | + KeyFilter | ||
31 | +} from '@shared/models/query/query.models'; | ||
32 | +import { SubscriptionTimewindow } from '@shared/models/time/time.models'; | ||
33 | +import { AlarmDataListener } from '@core/api/alarm-data.service'; | ||
34 | +import { UtilsService } from '@core/services/utils.service'; | ||
35 | +import { PageData } from '@shared/models/page/page-data'; | ||
36 | +import { deepClone, isDefined, isDefinedAndNotNull, isObject } from '@core/utils'; | ||
37 | +import { simulatedAlarm } from '@shared/models/alarm.models'; | ||
38 | + | ||
39 | +export interface AlarmSubscriptionDataKey { | ||
40 | + name: string; | ||
41 | + type: DataKeyType; | ||
42 | +} | ||
43 | + | ||
44 | +export interface AlarmDataSubscriptionOptions { | ||
45 | + datasourceType: DatasourceType; | ||
46 | + dataKeys: Array<AlarmSubscriptionDataKey>; | ||
47 | + entityFilter?: EntityFilter; | ||
48 | + pageLink?: AlarmDataPageLink; | ||
49 | + keyFilters?: Array<KeyFilter>; | ||
50 | + additionalKeyFilters?: Array<KeyFilter>; | ||
51 | + subscriptionTimewindow?: SubscriptionTimewindow; | ||
52 | +} | ||
53 | + | ||
54 | +export class AlarmDataSubscription { | ||
55 | + | ||
56 | + private datasourceType: DatasourceType = this.alarmDataSubscriptionOptions.datasourceType; | ||
57 | + | ||
58 | + private history: boolean; | ||
59 | + private realtime: boolean; | ||
60 | + | ||
61 | + private subscriber: TelemetrySubscriber; | ||
62 | + private alarmDataCommand: AlarmDataCmd; | ||
63 | + | ||
64 | + private pageData: PageData<AlarmData>; | ||
65 | + private alarmIdToDataIndex: {[id: string]: number}; | ||
66 | + | ||
67 | + private subsTw: SubscriptionTimewindow; | ||
68 | + | ||
69 | + constructor(public alarmDataSubscriptionOptions: AlarmDataSubscriptionOptions, | ||
70 | + private listener: AlarmDataListener, | ||
71 | + private telemetryService: TelemetryService, | ||
72 | + private utils: UtilsService) { | ||
73 | + } | ||
74 | + | ||
75 | + public unsubscribe() { | ||
76 | + if (this.datasourceType === DatasourceType.entity) { | ||
77 | + if (this.subscriber) { | ||
78 | + this.subscriber.unsubscribe(); | ||
79 | + this.subscriber = null; | ||
80 | + } | ||
81 | + } | ||
82 | + } | ||
83 | + | ||
84 | + public subscribe() { | ||
85 | + this.subsTw = this.alarmDataSubscriptionOptions.subscriptionTimewindow; | ||
86 | + this.history = this.alarmDataSubscriptionOptions.subscriptionTimewindow && | ||
87 | + isObject(this.alarmDataSubscriptionOptions.subscriptionTimewindow.fixedWindow); | ||
88 | + this.realtime = this.alarmDataSubscriptionOptions.subscriptionTimewindow && | ||
89 | + isDefinedAndNotNull(this.alarmDataSubscriptionOptions.subscriptionTimewindow.realtimeWindowMs); | ||
90 | + if (this.datasourceType === DatasourceType.entity) { | ||
91 | + this.subscriber = new TelemetrySubscriber(this.telemetryService); | ||
92 | + this.alarmDataCommand = new AlarmDataCmd(); | ||
93 | + | ||
94 | + const entityFields: Array<EntityKey> = | ||
95 | + this.alarmDataSubscriptionOptions.dataKeys.filter(dataKey => dataKey.type === DataKeyType.entityField).map( | ||
96 | + dataKey => ({ type: EntityKeyType.ENTITY_FIELD, key: dataKey.name }) | ||
97 | + ); | ||
98 | + | ||
99 | + const attrFields = this.alarmDataSubscriptionOptions.dataKeys.filter(dataKey => dataKey.type === DataKeyType.attribute).map( | ||
100 | + dataKey => ({ type: EntityKeyType.ATTRIBUTE, key: dataKey.name }) | ||
101 | + ); | ||
102 | + const tsFields = this.alarmDataSubscriptionOptions.dataKeys.filter(dataKey => dataKey.type === DataKeyType.timeseries).map( | ||
103 | + dataKey => ({ type: EntityKeyType.TIME_SERIES, key: dataKey.name }) | ||
104 | + ); | ||
105 | + const latestValues = attrFields.concat(tsFields); | ||
106 | + | ||
107 | + let keyFilters = this.alarmDataSubscriptionOptions.keyFilters; | ||
108 | + if (this.alarmDataSubscriptionOptions.additionalKeyFilters) { | ||
109 | + if (keyFilters) { | ||
110 | + keyFilters = keyFilters.concat(this.alarmDataSubscriptionOptions.additionalKeyFilters); | ||
111 | + } else { | ||
112 | + keyFilters = this.alarmDataSubscriptionOptions.additionalKeyFilters; | ||
113 | + } | ||
114 | + } | ||
115 | + this.alarmDataCommand.query = { | ||
116 | + entityFilter: this.alarmDataSubscriptionOptions.entityFilter, | ||
117 | + pageLink: deepClone(this.alarmDataSubscriptionOptions.pageLink), | ||
118 | + keyFilters, | ||
119 | + entityFields, | ||
120 | + latestValues | ||
121 | + }; | ||
122 | + if (this.history) { | ||
123 | + this.alarmDataCommand.query.pageLink.startTs = this.subsTw.fixedWindow.startTimeMs; | ||
124 | + this.alarmDataCommand.query.pageLink.endTs = this.subsTw.fixedWindow.endTimeMs; | ||
125 | + } else { | ||
126 | + this.alarmDataCommand.query.pageLink.timeWindow = this.subsTw.realtimeWindowMs; | ||
127 | + } | ||
128 | + | ||
129 | + this.subscriber.subscriptionCommands.push(this.alarmDataCommand); | ||
130 | + | ||
131 | + this.subscriber.alarmData$.subscribe((alarmDataUpdate) => { | ||
132 | + if (alarmDataUpdate.data) { | ||
133 | + this.onPageData(alarmDataUpdate.data); | ||
134 | + } else if (alarmDataUpdate.update) { | ||
135 | + this.onDataUpdate(alarmDataUpdate.update); | ||
136 | + } | ||
137 | + }); | ||
138 | + | ||
139 | + this.subscriber.subscribe(); | ||
140 | + | ||
141 | + } else if (this.datasourceType === DatasourceType.function) { | ||
142 | + const pageData: PageData<AlarmData> = { | ||
143 | + data: [{...simulatedAlarm, entityId: '1', latest: {}}], | ||
144 | + hasNext: false, | ||
145 | + totalElements: 1, | ||
146 | + totalPages: 1 | ||
147 | + }; | ||
148 | + this.onPageData(pageData); | ||
149 | + } | ||
150 | + } | ||
151 | + | ||
152 | + private resetData() { | ||
153 | + this.alarmIdToDataIndex = {}; | ||
154 | + for (let dataIndex = 0; dataIndex < this.pageData.data.length; dataIndex++) { | ||
155 | + const alarmData = this.pageData.data[dataIndex]; | ||
156 | + this.alarmIdToDataIndex[alarmData.id.id] = dataIndex; | ||
157 | + } | ||
158 | + } | ||
159 | + | ||
160 | + private onPageData(pageData: PageData<AlarmData>) { | ||
161 | + this.pageData = pageData; | ||
162 | + this.resetData(); | ||
163 | + this.listener.alarmsLoaded(pageData, this.alarmDataSubscriptionOptions.pageLink); | ||
164 | + } | ||
165 | + | ||
166 | + private onDataUpdate(update: Array<AlarmData>) { | ||
167 | + for (const alarmData of update) { | ||
168 | + const dataIndex = this.alarmIdToDataIndex[alarmData.id.id]; | ||
169 | + if (isDefined(dataIndex) && dataIndex >= 0) { | ||
170 | + this.pageData.data[dataIndex] = alarmData; | ||
171 | + } | ||
172 | + } | ||
173 | + this.listener.alarmsUpdated(update, this.pageData); | ||
174 | + } | ||
175 | + | ||
176 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2020 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 { SubscriptionTimewindow } from '@shared/models/time/time.models'; | ||
18 | +import { Datasource, DatasourceType } from '@shared/models/widget.models'; | ||
19 | +import { PageData } from '@shared/models/page/page-data'; | ||
20 | +import { AlarmData, AlarmDataPageLink, KeyFilter } from '@shared/models/query/query.models'; | ||
21 | +import { Injectable } from '@angular/core'; | ||
22 | +import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; | ||
23 | +import { UtilsService } from '@core/services/utils.service'; | ||
24 | +import { | ||
25 | + AlarmDataSubscription, | ||
26 | + AlarmDataSubscriptionOptions, | ||
27 | + AlarmSubscriptionDataKey | ||
28 | +} from '@core/api/alarm-data-subscription'; | ||
29 | +import { deepClone } from '@core/utils'; | ||
30 | + | ||
31 | +export interface AlarmDataListener { | ||
32 | + subscriptionTimewindow?: SubscriptionTimewindow; | ||
33 | + alarmSource: Datasource; | ||
34 | + alarmsLoaded: (pageData: PageData<AlarmData>, pageLink: AlarmDataPageLink) => void; | ||
35 | + alarmsUpdated: (update: Array<AlarmData>, pageData: PageData<AlarmData>) => void; | ||
36 | + subscription?: AlarmDataSubscription; | ||
37 | +} | ||
38 | + | ||
39 | +@Injectable({ | ||
40 | + providedIn: 'root' | ||
41 | +}) | ||
42 | +export class AlarmDataService { | ||
43 | + | ||
44 | + constructor(private telemetryService: TelemetryWebsocketService, | ||
45 | + private utils: UtilsService) {} | ||
46 | + | ||
47 | + | ||
48 | + public subscribeForAlarms(listener: AlarmDataListener, | ||
49 | + pageLink: AlarmDataPageLink, | ||
50 | + keyFilters: KeyFilter[]) { | ||
51 | + const alarmSource = listener.alarmSource; | ||
52 | + if (alarmSource.type === DatasourceType.entity && (!alarmSource.entityFilter || !pageLink)) { | ||
53 | + return; | ||
54 | + } | ||
55 | + listener.subscription = this.createSubscription(listener, | ||
56 | + pageLink, alarmSource.keyFilters, keyFilters); | ||
57 | + return listener.subscription.subscribe(); | ||
58 | + } | ||
59 | + | ||
60 | + public stopSubscription(listener: AlarmDataListener) { | ||
61 | + if (listener.subscription) { | ||
62 | + listener.subscription.unsubscribe(); | ||
63 | + } | ||
64 | + } | ||
65 | + | ||
66 | + private createSubscription(listener: AlarmDataListener, | ||
67 | + pageLink: AlarmDataPageLink, | ||
68 | + keyFilters: KeyFilter[], | ||
69 | + additionalKeyFilters: KeyFilter[]): AlarmDataSubscription { | ||
70 | + const alarmSource = listener.alarmSource; | ||
71 | + const alarmSubscriptionDataKeys: Array<AlarmSubscriptionDataKey> = []; | ||
72 | + alarmSource.dataKeys.forEach((dataKey) => { | ||
73 | + const alarmSubscriptionDataKey: AlarmSubscriptionDataKey = { | ||
74 | + name: dataKey.name, | ||
75 | + type: dataKey.type | ||
76 | + }; | ||
77 | + alarmSubscriptionDataKeys.push(alarmSubscriptionDataKey); | ||
78 | + }); | ||
79 | + const alarmDataSubscriptionOptions: AlarmDataSubscriptionOptions = { | ||
80 | + datasourceType: alarmSource.type, | ||
81 | + dataKeys: alarmSubscriptionDataKeys, | ||
82 | + subscriptionTimewindow: deepClone(listener.subscriptionTimewindow) | ||
83 | + }; | ||
84 | + if (alarmDataSubscriptionOptions.datasourceType === DatasourceType.entity) { | ||
85 | + alarmDataSubscriptionOptions.entityFilter = alarmSource.entityFilter; | ||
86 | + alarmDataSubscriptionOptions.pageLink = pageLink; | ||
87 | + alarmDataSubscriptionOptions.keyFilters = keyFilters; | ||
88 | + alarmDataSubscriptionOptions.additionalKeyFilters = additionalKeyFilters; | ||
89 | + } | ||
90 | + return new AlarmDataSubscription(alarmDataSubscriptionOptions, | ||
91 | + listener, this.telemetryService, this.utils); | ||
92 | + } | ||
93 | + | ||
94 | +} |
@@ -41,6 +41,7 @@ import { EntityInfo } from '@app/shared/models/entity.models'; | @@ -41,6 +41,7 @@ import { EntityInfo } from '@app/shared/models/entity.models'; | ||
41 | import { IDashboardComponent } from '@home/models/dashboard-component.models'; | 41 | import { IDashboardComponent } from '@home/models/dashboard-component.models'; |
42 | import * as moment_ from 'moment'; | 42 | import * as moment_ from 'moment'; |
43 | import { | 43 | import { |
44 | + AlarmData, AlarmDataPageLink, | ||
44 | EntityData, | 45 | EntityData, |
45 | EntityDataPageLink, | 46 | EntityDataPageLink, |
46 | EntityFilter, | 47 | EntityFilter, |
@@ -51,6 +52,7 @@ import { | @@ -51,6 +52,7 @@ import { | ||
51 | import { EntityDataService } from '@core/api/entity-data.service'; | 52 | import { EntityDataService } from '@core/api/entity-data.service'; |
52 | import { PageData } from '@shared/models/page/page-data'; | 53 | import { PageData } from '@shared/models/page/page-data'; |
53 | import { TranslateService } from '@ngx-translate/core'; | 54 | import { TranslateService } from '@ngx-translate/core'; |
55 | +import { AlarmDataService } from '@core/api/alarm-data.service'; | ||
54 | 56 | ||
55 | export interface TimewindowFunctions { | 57 | export interface TimewindowFunctions { |
56 | onUpdateTimewindow: (startTimeMs: number, endTimeMs: number, interval?: number) => void; | 58 | onUpdateTimewindow: (startTimeMs: number, endTimeMs: number, interval?: number) => void; |
@@ -184,9 +186,9 @@ export class WidgetSubscriptionContext { | @@ -184,9 +186,9 @@ export class WidgetSubscriptionContext { | ||
184 | 186 | ||
185 | timeService: TimeService; | 187 | timeService: TimeService; |
186 | deviceService: DeviceService; | 188 | deviceService: DeviceService; |
187 | - alarmService: AlarmService; | ||
188 | translate: TranslateService; | 189 | translate: TranslateService; |
189 | entityDataService: EntityDataService; | 190 | entityDataService: EntityDataService; |
191 | + alarmDataService: AlarmDataService; | ||
190 | utils: UtilsService; | 192 | utils: UtilsService; |
191 | raf: RafService; | 193 | raf: RafService; |
192 | widgetUtils: IWidgetUtils; | 194 | widgetUtils: IWidgetUtils; |
@@ -218,10 +220,10 @@ export interface WidgetSubscriptionOptions { | @@ -218,10 +220,10 @@ export interface WidgetSubscriptionOptions { | ||
218 | type?: widgetType; | 220 | type?: widgetType; |
219 | stateData?: boolean; | 221 | stateData?: boolean; |
220 | alarmSource?: Datasource; | 222 | alarmSource?: Datasource; |
221 | - alarmSearchStatus?: AlarmSearchStatus; | 223 | +/* alarmSearchStatus?: AlarmSearchStatus; |
222 | alarmsPollingInterval?: number; | 224 | alarmsPollingInterval?: number; |
223 | alarmsMaxCountLoad?: number; | 225 | alarmsMaxCountLoad?: number; |
224 | - alarmsFetchSize?: number; | 226 | + alarmsFetchSize?: number; */ |
225 | datasources?: Array<Datasource>; | 227 | datasources?: Array<Datasource>; |
226 | hasDataPageLink?: boolean; | 228 | hasDataPageLink?: boolean; |
227 | singleEntity?: boolean; | 229 | singleEntity?: boolean; |
@@ -269,10 +271,10 @@ export interface IWidgetSubscription { | @@ -269,10 +271,10 @@ export interface IWidgetSubscription { | ||
269 | timeWindow?: WidgetTimewindow; | 271 | timeWindow?: WidgetTimewindow; |
270 | comparisonTimeWindow?: WidgetTimewindow; | 272 | comparisonTimeWindow?: WidgetTimewindow; |
271 | 273 | ||
272 | - alarms?: Array<AlarmInfo>; | 274 | + alarms?: PageData<AlarmData>; |
273 | alarmSource?: Datasource; | 275 | alarmSource?: Datasource; |
274 | - alarmSearchStatus?: AlarmSearchStatus; | ||
275 | - alarmsPollingInterval?: number; | 276 | + /* alarmSearchStatus?: AlarmSearchStatus; |
277 | + alarmsPollingInterval?: number; */ | ||
276 | 278 | ||
277 | targetDeviceAliasIds?: Array<string>; | 279 | targetDeviceAliasIds?: Array<string>; |
278 | targetDeviceIds?: Array<string>; | 280 | targetDeviceIds?: Array<string>; |
@@ -309,6 +311,9 @@ export interface IWidgetSubscription { | @@ -309,6 +311,9 @@ export interface IWidgetSubscription { | ||
309 | pageLink: EntityDataPageLink, | 311 | pageLink: EntityDataPageLink, |
310 | keyFilters: KeyFilter[]): Observable<any>; | 312 | keyFilters: KeyFilter[]): Observable<any>; |
311 | 313 | ||
314 | + subscribeForAlarms(pageLink: AlarmDataPageLink, | ||
315 | + keyFilters: KeyFilter[]): void; | ||
316 | + | ||
312 | isDataResolved(): boolean; | 317 | isDataResolved(): boolean; |
313 | 318 | ||
314 | destroy(): void; | 319 | destroy(): void; |
@@ -47,21 +47,23 @@ import { | @@ -47,21 +47,23 @@ import { | ||
47 | import { forkJoin, Observable, of, ReplaySubject, Subject, throwError } from 'rxjs'; | 47 | import { forkJoin, Observable, of, ReplaySubject, Subject, throwError } from 'rxjs'; |
48 | import { CancelAnimationFrame } from '@core/services/raf.service'; | 48 | import { CancelAnimationFrame } from '@core/services/raf.service'; |
49 | import { EntityType } from '@shared/models/entity-type.models'; | 49 | import { EntityType } from '@shared/models/entity-type.models'; |
50 | -import { AlarmInfo, AlarmSearchStatus } from '@shared/models/alarm.models'; | ||
51 | import { createLabelFromDatasource, deepClone, isDefined, isEqual } from '@core/utils'; | 50 | import { createLabelFromDatasource, deepClone, isDefined, isEqual } from '@core/utils'; |
52 | -import { AlarmSourceListener } from '@core/http/alarm.service'; | ||
53 | import { EntityId } from '@app/shared/models/id/entity-id'; | 51 | import { EntityId } from '@app/shared/models/id/entity-id'; |
54 | import * as moment_ from 'moment'; | 52 | import * as moment_ from 'moment'; |
55 | -import { PageData } from '@shared/models/page/page-data'; | 53 | +import { emptyPageData, PageData } from '@shared/models/page/page-data'; |
56 | import { EntityDataListener } from '@core/api/entity-data.service'; | 54 | import { EntityDataListener } from '@core/api/entity-data.service'; |
57 | import { | 55 | import { |
56 | + AlarmData, | ||
57 | + AlarmDataPageLink, | ||
58 | EntityData, | 58 | EntityData, |
59 | EntityDataPageLink, | 59 | EntityDataPageLink, |
60 | entityDataToEntityInfo, | 60 | entityDataToEntityInfo, |
61 | + EntityKeyType, | ||
61 | KeyFilter, | 62 | KeyFilter, |
62 | updateDatasourceFromEntityInfo | 63 | updateDatasourceFromEntityInfo |
63 | } from '@shared/models/query/query.models'; | 64 | } from '@shared/models/query/query.models'; |
64 | import { map } from 'rxjs/operators'; | 65 | import { map } from 'rxjs/operators'; |
66 | +import { AlarmDataListener } from '@core/api/alarm-data.service'; | ||
65 | 67 | ||
66 | const moment = moment_; | 68 | const moment = moment_; |
67 | 69 | ||
@@ -102,10 +104,11 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -102,10 +104,11 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
102 | comparisonTimeWindow: WidgetTimewindow; | 104 | comparisonTimeWindow: WidgetTimewindow; |
103 | timewindowForComparison: SubscriptionTimewindow; | 105 | timewindowForComparison: SubscriptionTimewindow; |
104 | 106 | ||
105 | - alarms: Array<AlarmInfo>; | 107 | + // alarms: Array<AlarmInfo>; |
108 | + alarms: PageData<AlarmData>; | ||
106 | alarmSource: Datasource; | 109 | alarmSource: Datasource; |
107 | 110 | ||
108 | - private alarmSearchStatusValue: AlarmSearchStatus; | 111 | + /* private alarmSearchStatusValue: AlarmSearchStatus; |
109 | 112 | ||
110 | set alarmSearchStatus(value: AlarmSearchStatus) { | 113 | set alarmSearchStatus(value: AlarmSearchStatus) { |
111 | if (this.alarmSearchStatusValue !== value) { | 114 | if (this.alarmSearchStatusValue !== value) { |
@@ -116,12 +119,14 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -116,12 +119,14 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
116 | 119 | ||
117 | get alarmSearchStatus(): AlarmSearchStatus { | 120 | get alarmSearchStatus(): AlarmSearchStatus { |
118 | return this.alarmSearchStatusValue; | 121 | return this.alarmSearchStatusValue; |
119 | - } | 122 | + }*/ |
123 | + | ||
124 | + alarmDataListener: AlarmDataListener; | ||
120 | 125 | ||
121 | - alarmsPollingInterval: number; | 126 | +/* alarmsPollingInterval: number; |
122 | alarmsMaxCountLoad: number; | 127 | alarmsMaxCountLoad: number; |
123 | alarmsFetchSize: number; | 128 | alarmsFetchSize: number; |
124 | - alarmSourceListener: AlarmSourceListener; | 129 | + alarmSourceListener: AlarmSourceListener;*/ |
125 | 130 | ||
126 | loadingData: boolean; | 131 | loadingData: boolean; |
127 | 132 | ||
@@ -181,7 +186,7 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -181,7 +186,7 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
181 | this.callbacks.dataLoading = this.callbacks.dataLoading || (() => {}); | 186 | this.callbacks.dataLoading = this.callbacks.dataLoading || (() => {}); |
182 | this.callbacks.timeWindowUpdated = this.callbacks.timeWindowUpdated || (() => {}); | 187 | this.callbacks.timeWindowUpdated = this.callbacks.timeWindowUpdated || (() => {}); |
183 | this.alarmSource = options.alarmSource; | 188 | this.alarmSource = options.alarmSource; |
184 | - this.alarmSearchStatusValue = isDefined(options.alarmSearchStatus) ? | 189 | + /*this.alarmSearchStatusValue = isDefined(options.alarmSearchStatus) ? |
185 | options.alarmSearchStatus : AlarmSearchStatus.ANY; | 190 | options.alarmSearchStatus : AlarmSearchStatus.ANY; |
186 | this.alarmsPollingInterval = isDefined(options.alarmsPollingInterval) ? | 191 | this.alarmsPollingInterval = isDefined(options.alarmsPollingInterval) ? |
187 | options.alarmsPollingInterval : 5000; | 192 | options.alarmsPollingInterval : 5000; |
@@ -189,8 +194,10 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -189,8 +194,10 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
189 | options.alarmsMaxCountLoad : 0; | 194 | options.alarmsMaxCountLoad : 0; |
190 | this.alarmsFetchSize = isDefined(options.alarmsFetchSize) ? | 195 | this.alarmsFetchSize = isDefined(options.alarmsFetchSize) ? |
191 | options.alarmsFetchSize : 100; | 196 | options.alarmsFetchSize : 100; |
192 | - this.alarmSourceListener = null; | ||
193 | - this.alarms = []; | 197 | + this.alarmSourceListener = null;*/ |
198 | + this.alarmDataListener = null; | ||
199 | + // this.alarms = []; | ||
200 | + this.alarms = emptyPageData(); | ||
194 | this.originalTimewindow = null; | 201 | this.originalTimewindow = null; |
195 | this.timeWindow = {}; | 202 | this.timeWindow = {}; |
196 | this.useDashboardTimewindow = options.useDashboardTimewindow; | 203 | this.useDashboardTimewindow = options.useDashboardTimewindow; |
@@ -290,7 +297,7 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -290,7 +297,7 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
290 | if (this.targetDeviceId) { | 297 | if (this.targetDeviceId) { |
291 | this.rpcEnabled = true; | 298 | this.rpcEnabled = true; |
292 | } else { | 299 | } else { |
293 | - this.rpcEnabled = this.ctx.utils.widgetEditMode ? true : false; | 300 | + this.rpcEnabled = this.ctx.utils.widgetEditMode; |
294 | } | 301 | } |
295 | this.hasResolvedData = this.rpcEnabled; | 302 | this.hasResolvedData = this.rpcEnabled; |
296 | this.callbacks.rpcStateChanged(this); | 303 | this.callbacks.rpcStateChanged(this); |
@@ -317,7 +324,7 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -317,7 +324,7 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
317 | if (this.targetDeviceId) { | 324 | if (this.targetDeviceId) { |
318 | this.rpcEnabled = true; | 325 | this.rpcEnabled = true; |
319 | } else { | 326 | } else { |
320 | - this.rpcEnabled = this.ctx.utils.widgetEditMode ? true : false; | 327 | + this.rpcEnabled = this.ctx.utils.widgetEditMode; |
321 | } | 328 | } |
322 | this.hasResolvedData = true; | 329 | this.hasResolvedData = true; |
323 | this.callbacks.rpcStateChanged(this); | 330 | this.callbacks.rpcStateChanged(this); |
@@ -356,6 +363,7 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -356,6 +363,7 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
356 | } | 363 | } |
357 | 364 | ||
358 | private configureAlarmsData() { | 365 | private configureAlarmsData() { |
366 | + this.notifyDataLoaded(); | ||
359 | } | 367 | } |
360 | 368 | ||
361 | private initDataSubscription(): Observable<any> { | 369 | private initDataSubscription(): Observable<any> { |
@@ -482,13 +490,17 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -482,13 +490,17 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
482 | entityName = this.targetDeviceName; | 490 | entityName = this.targetDeviceName; |
483 | } | 491 | } |
484 | } else if (this.type === widgetType.alarm) { | 492 | } else if (this.type === widgetType.alarm) { |
485 | - if (this.alarmSource && this.alarmSource.entityType && this.alarmSource.entityId) { | ||
486 | - entityId = { | ||
487 | - entityType: this.alarmSource.entityType, | ||
488 | - id: this.alarmSource.entityId | ||
489 | - }; | ||
490 | - entityName = this.alarmSource.entityName; | ||
491 | - entityLabel = this.alarmSource.entityLabel; | 493 | + if (this.alarms && this.alarms.data.length) { |
494 | + const data = this.alarms.data[0]; | ||
495 | + entityId = data.originator; | ||
496 | + entityName = data.originatorName; | ||
497 | + if (data.latest && data.latest[EntityKeyType.ENTITY_FIELD]) { | ||
498 | + const entityFields = data.latest[EntityKeyType.ENTITY_FIELD]; | ||
499 | + const labelValue = entityFields.label; | ||
500 | + if (labelValue) { | ||
501 | + entityLabel = labelValue.value; | ||
502 | + } | ||
503 | + } | ||
492 | } | 504 | } |
493 | } else { | 505 | } else { |
494 | for (const datasource of this.datasources) { | 506 | for (const datasource of this.datasources) { |
@@ -522,7 +534,6 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -522,7 +534,6 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
522 | } else { | 534 | } else { |
523 | return this.checkSubscriptions(aliasIds); | 535 | return this.checkSubscriptions(aliasIds); |
524 | } | 536 | } |
525 | - return false; | ||
526 | } | 537 | } |
527 | 538 | ||
528 | onFiltersChanged(filterIds: Array<string>): boolean { | 539 | onFiltersChanged(filterIds: Array<string>): boolean { |
@@ -573,12 +584,6 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -573,12 +584,6 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
573 | return false; | 584 | return false; |
574 | } | 585 | } |
575 | 586 | ||
576 | - private onAlarmSearchStatusChanged() { | ||
577 | - if (this.type === widgetType.alarm) { | ||
578 | - this.update(); | ||
579 | - } | ||
580 | - } | ||
581 | - | ||
582 | updateDataVisibility(index: number): void { | 587 | updateDataVisibility(index: number): void { |
583 | if (this.displayLegend) { | 588 | if (this.displayLegend) { |
584 | const hidden = this.legendData.keys[index].dataKey.hidden; | 589 | const hidden = this.legendData.keys[index].dataKey.hidden; |
@@ -752,11 +757,12 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -752,11 +757,12 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
752 | } | 757 | } |
753 | 758 | ||
754 | update() { | 759 | update() { |
755 | - if (this.type === widgetType.rpc || this.type === widgetType.alarm) { | ||
756 | - this.unsubscribe(); | ||
757 | - this.subscribe(); | ||
758 | - } else { | ||
759 | - this.dataSubscribe(); | 760 | + if (this.type !== widgetType.rpc) { |
761 | + if (this.type === widgetType.alarm) { | ||
762 | + this.updateAlarmDataSubscription(); | ||
763 | + } else { | ||
764 | + this.dataSubscribe(); | ||
765 | + } | ||
760 | } | 766 | } |
761 | } | 767 | } |
762 | 768 | ||
@@ -821,13 +827,41 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -821,13 +827,41 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
821 | } | 827 | } |
822 | } | 828 | } |
823 | 829 | ||
824 | - private doSubscribe() { | ||
825 | - if (this.type === widgetType.rpc) { | ||
826 | - return; | 830 | + subscribeForAlarms(pageLink: AlarmDataPageLink, |
831 | + keyFilters: KeyFilter[]) { | ||
832 | + if (this.alarmDataListener) { | ||
833 | + this.ctx.alarmDataService.stopSubscription(this.alarmDataListener); | ||
827 | } | 834 | } |
828 | - if (this.type === widgetType.alarm) { | ||
829 | - this.alarmsSubscribe(); | ||
830 | - } else { | 835 | + if (this.timeWindowConfig) { |
836 | + this.updateRealtimeSubscription(); | ||
837 | + if (this.subscriptionTimewindow.fixedWindow) { | ||
838 | + this.onDataUpdated(); | ||
839 | + } | ||
840 | + } | ||
841 | + this.alarmDataListener = { | ||
842 | + subscriptionTimewindow: this.subscriptionTimewindow, | ||
843 | + alarmSource: this.alarmSource, | ||
844 | + alarmsLoaded: this.alarmsLoaded.bind(this), | ||
845 | + alarmsUpdated: this.alarmsUpdated.bind(this) | ||
846 | + }; | ||
847 | + | ||
848 | + this.alarms = emptyPageData(); | ||
849 | + | ||
850 | + this.ctx.alarmDataService.subscribeForAlarms(this.alarmDataListener, pageLink, keyFilters); | ||
851 | + | ||
852 | + let forceUpdate = false; | ||
853 | + if (this.alarmSource.unresolvedStateEntity || | ||
854 | + (this.alarmSource.type === DatasourceType.entity && !this.alarmSource.entityId) | ||
855 | + ) { | ||
856 | + forceUpdate = true; | ||
857 | + } | ||
858 | + if (forceUpdate) { | ||
859 | + this.onDataUpdated(); | ||
860 | + } | ||
861 | + } | ||
862 | + | ||
863 | + private doSubscribe() { | ||
864 | + if (this.type !== widgetType.rpc && this.type !== widgetType.alarm) { | ||
831 | this.dataSubscribe(); | 865 | this.dataSubscribe(); |
832 | } | 866 | } |
833 | } | 867 | } |
@@ -858,7 +892,7 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -858,7 +892,7 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
858 | } | 892 | } |
859 | } | 893 | } |
860 | 894 | ||
861 | - private alarmsSubscribe() { | 895 | + /* private alarmsSubscribe() { |
862 | this.notifyDataLoading(); | 896 | this.notifyDataLoading(); |
863 | if (this.timeWindowConfig) { | 897 | if (this.timeWindowConfig) { |
864 | this.updateRealtimeSubscription(); | 898 | this.updateRealtimeSubscription(); |
@@ -875,9 +909,10 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -875,9 +909,10 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
875 | alarmsFetchSize: this.alarmsFetchSize, | 909 | alarmsFetchSize: this.alarmsFetchSize, |
876 | alarmsUpdated: alarms => this.alarmsUpdated(alarms) | 910 | alarmsUpdated: alarms => this.alarmsUpdated(alarms) |
877 | }; | 911 | }; |
878 | - this.alarms = null; | ||
879 | 912 | ||
880 | - this.ctx.alarmService.subscribeForAlarms(this.alarmSourceListener); | 913 | + this.alarms = emptyPageData(); |
914 | + | ||
915 | + this.ctx.alarmDataService.subscribeForAlarms(this.alarmDataListener); | ||
881 | 916 | ||
882 | let forceUpdate = false; | 917 | let forceUpdate = false; |
883 | if (this.alarmSource.unresolvedStateEntity || | 918 | if (this.alarmSource.unresolvedStateEntity || |
@@ -889,7 +924,7 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -889,7 +924,7 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
889 | this.notifyDataLoaded(); | 924 | this.notifyDataLoaded(); |
890 | this.onDataUpdated(); | 925 | this.onDataUpdated(); |
891 | } | 926 | } |
892 | - } | 927 | + } */ |
893 | 928 | ||
894 | 929 | ||
895 | unsubscribe() { | 930 | unsubscribe() { |
@@ -910,33 +945,62 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -910,33 +945,62 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
910 | } | 945 | } |
911 | 946 | ||
912 | private alarmsUnsubscribe() { | 947 | private alarmsUnsubscribe() { |
913 | - if (this.alarmSourceListener) { | ||
914 | - this.ctx.alarmService.unsubscribeFromAlarms(this.alarmSourceListener); | ||
915 | - this.alarmSourceListener = null; | 948 | + if (this.alarmDataListener) { |
949 | + this.ctx.alarmDataService.stopSubscription(this.alarmDataListener); | ||
950 | + this.alarmDataListener = null; | ||
916 | } | 951 | } |
917 | } | 952 | } |
918 | 953 | ||
919 | private checkRpcTarget(aliasIds: Array<string>): boolean { | 954 | private checkRpcTarget(aliasIds: Array<string>): boolean { |
920 | - if (aliasIds.indexOf(this.targetDeviceAliasId) > -1) { | ||
921 | - return true; | ||
922 | - } else { | ||
923 | - return false; | ||
924 | - } | 955 | + return aliasIds.indexOf(this.targetDeviceAliasId) > -1; |
925 | } | 956 | } |
926 | 957 | ||
927 | private checkAlarmSource(aliasIds: Array<string>): boolean { | 958 | private checkAlarmSource(aliasIds: Array<string>): boolean { |
928 | if (this.options.alarmSource && this.options.alarmSource.entityAliasId) { | 959 | if (this.options.alarmSource && this.options.alarmSource.entityAliasId) { |
929 | - return aliasIds.indexOf(this.options.alarmSource.entityAliasId) > -1; | ||
930 | - } else { | ||
931 | - return false; | 960 | + if (aliasIds.indexOf(this.options.alarmSource.entityAliasId) > -1) { |
961 | + this.updateAlarmSubscription(); | ||
962 | + } | ||
932 | } | 963 | } |
964 | + return false; | ||
933 | } | 965 | } |
934 | 966 | ||
935 | private checkAlarmSourceFilters(filterIds: Array<string>): boolean { | 967 | private checkAlarmSourceFilters(filterIds: Array<string>): boolean { |
936 | if (this.options.alarmSource && this.options.alarmSource.filterId) { | 968 | if (this.options.alarmSource && this.options.alarmSource.filterId) { |
937 | - return filterIds.indexOf(this.options.alarmSource.filterId) > -1; | 969 | + if (filterIds.indexOf(this.options.alarmSource.filterId) > -1) { |
970 | + this.updateAlarmSubscription(); | ||
971 | + } | ||
972 | + } | ||
973 | + return false; | ||
974 | + } | ||
975 | + | ||
976 | + private updateAlarmSubscription() { | ||
977 | + this.alarmSource = this.options.alarmSource; | ||
978 | + if (!this.ctx.aliasController) { | ||
979 | + this.hasResolvedData = true; | ||
980 | + this.configureAlarmsData(); | ||
981 | + this.updateAlarmDataSubscription(); | ||
938 | } else { | 982 | } else { |
939 | - return false; | 983 | + this.ctx.aliasController.resolveAlarmSource(this.alarmSource).subscribe( |
984 | + (alarmSource) => { | ||
985 | + this.alarmSource = alarmSource; | ||
986 | + if (alarmSource) { | ||
987 | + this.hasResolvedData = true; | ||
988 | + } | ||
989 | + this.configureAlarmsData(); | ||
990 | + this.updateAlarmDataSubscription(); | ||
991 | + }, | ||
992 | + () => { | ||
993 | + this.notifyDataLoaded(); | ||
994 | + } | ||
995 | + ); | ||
996 | + } | ||
997 | + } | ||
998 | + | ||
999 | + private updateAlarmDataSubscription() { | ||
1000 | + if (this.alarmDataListener) { | ||
1001 | + const pageLink = this.alarmDataListener.subscription.alarmDataSubscriptionOptions.pageLink; | ||
1002 | + const keyFilters = this.alarmDataListener.subscription.alarmDataSubscriptionOptions.additionalKeyFilters; | ||
1003 | + this.subscribeForAlarms(pageLink, keyFilters); | ||
940 | } | 1004 | } |
941 | } | 1005 | } |
942 | 1006 | ||
@@ -999,7 +1063,7 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -999,7 +1063,7 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
999 | } | 1063 | } |
1000 | ); | 1064 | ); |
1001 | }, | 1065 | }, |
1002 | - (err) => { | 1066 | + () => { |
1003 | this.notifyDataLoaded(); | 1067 | this.notifyDataLoaded(); |
1004 | } | 1068 | } |
1005 | ); | 1069 | ); |
@@ -1031,11 +1095,6 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -1031,11 +1095,6 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
1031 | } | 1095 | } |
1032 | } | 1096 | } |
1033 | 1097 | ||
1034 | - private notifyDataLoading() { | ||
1035 | - this.loadingData = true; | ||
1036 | - this.callbacks.dataLoading(this); | ||
1037 | - } | ||
1038 | - | ||
1039 | private notifyDataLoaded() { | 1098 | private notifyDataLoaded() { |
1040 | this.loadingData = false; | 1099 | this.loadingData = false; |
1041 | this.callbacks.dataLoading(this); | 1100 | this.callbacks.dataLoading(this); |
@@ -1100,23 +1159,21 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -1100,23 +1159,21 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
1100 | const datasources = pageData.data.map((entityData, index) => | 1159 | const datasources = pageData.data.map((entityData, index) => |
1101 | this.entityDataToDatasource(datasource, entityData, index) | 1160 | this.entityDataToDatasource(datasource, entityData, index) |
1102 | ); | 1161 | ); |
1103 | - const datasourcesPage: PageData<Datasource> = { | 1162 | + this.datasourcePages[datasourceIndex] = { |
1104 | data: datasources, | 1163 | data: datasources, |
1105 | hasNext: pageData.hasNext, | 1164 | hasNext: pageData.hasNext, |
1106 | totalElements: pageData.totalElements, | 1165 | totalElements: pageData.totalElements, |
1107 | totalPages: pageData.totalPages | 1166 | totalPages: pageData.totalPages |
1108 | }; | 1167 | }; |
1109 | - this.datasourcePages[datasourceIndex] = datasourcesPage; | ||
1110 | const datasourceData = datasources.map((datasourceElement, index) => | 1168 | const datasourceData = datasources.map((datasourceElement, index) => |
1111 | this.entityDataToDatasourceData(datasourceElement, data[index]) | 1169 | this.entityDataToDatasourceData(datasourceElement, data[index]) |
1112 | ); | 1170 | ); |
1113 | - const datasourceDataPage: PageData<Array<DatasourceData>> = { | 1171 | + this.dataPages[datasourceIndex] = { |
1114 | data: datasourceData, | 1172 | data: datasourceData, |
1115 | hasNext: pageData.hasNext, | 1173 | hasNext: pageData.hasNext, |
1116 | totalElements: pageData.totalElements, | 1174 | totalElements: pageData.totalElements, |
1117 | totalPages: pageData.totalPages | 1175 | totalPages: pageData.totalPages |
1118 | }; | 1176 | }; |
1119 | - this.dataPages[datasourceIndex] = datasourceDataPage; | ||
1120 | if (datasource.type === DatasourceType.entity && | 1177 | if (datasource.type === DatasourceType.entity && |
1121 | pageData.hasNext && pageLink.pageSize > 1) { | 1178 | pageData.hasNext && pageLink.pageSize > 1) { |
1122 | if (this.warnOnPageDataOverflow) { | 1179 | if (this.warnOnPageDataOverflow) { |
@@ -1215,8 +1272,8 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -1215,8 +1272,8 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
1215 | 1272 | ||
1216 | private entityDataToDatasourceData(datasource: Datasource, data: Array<DataSetHolder>): Array<DatasourceData> { | 1273 | private entityDataToDatasourceData(datasource: Datasource, data: Array<DataSetHolder>): Array<DatasourceData> { |
1217 | return datasource.dataKeys.map((dataKey, keyIndex) => { | 1274 | return datasource.dataKeys.map((dataKey, keyIndex) => { |
1218 | - dataKey.hidden = dataKey.settings.hideDataByDefault ? true : false; | ||
1219 | - dataKey.inLegend = dataKey.settings.removeFromLegend ? false : true; | 1275 | + dataKey.hidden = !!dataKey.settings.hideDataByDefault; |
1276 | + dataKey.inLegend = !dataKey.settings.removeFromLegend; | ||
1220 | if (this.comparisonEnabled && dataKey.isAdditional && dataKey.settings.comparisonSettings.comparisonValuesLabel) { | 1277 | if (this.comparisonEnabled && dataKey.isAdditional && dataKey.settings.comparisonSettings.comparisonValuesLabel) { |
1221 | dataKey.label = createLabelFromDatasource(datasource, dataKey.settings.comparisonSettings.comparisonValuesLabel); | 1278 | dataKey.label = createLabelFromDatasource(datasource, dataKey.settings.comparisonSettings.comparisonValuesLabel); |
1222 | } else { | 1279 | } else { |
@@ -1242,7 +1299,7 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -1242,7 +1299,7 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
1242 | const newDatasource = deepClone(configDatasource); | 1299 | const newDatasource = deepClone(configDatasource); |
1243 | const entityInfo = entityDataToEntityInfo(entityData); | 1300 | const entityInfo = entityDataToEntityInfo(entityData); |
1244 | updateDatasourceFromEntityInfo(newDatasource, entityInfo); | 1301 | updateDatasourceFromEntityInfo(newDatasource, entityInfo); |
1245 | - newDatasource.generated = index > 0 ? true : false; | 1302 | + newDatasource.generated = index > 0; |
1246 | return newDatasource; | 1303 | return newDatasource; |
1247 | } | 1304 | } |
1248 | 1305 | ||
@@ -1285,16 +1342,16 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -1285,16 +1342,16 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
1285 | } | 1342 | } |
1286 | } | 1343 | } |
1287 | 1344 | ||
1288 | - private alarmsUpdated(alarms: Array<AlarmInfo>) { | ||
1289 | - this.notifyDataLoaded(); | ||
1290 | - const updated = !this.alarms || !isEqual(this.alarms, alarms); | 1345 | + private alarmsLoaded(alarms: PageData<AlarmData>) { |
1291 | this.alarms = alarms; | 1346 | this.alarms = alarms; |
1292 | if (this.subscriptionTimewindow && this.subscriptionTimewindow.realtimeWindowMs) { | 1347 | if (this.subscriptionTimewindow && this.subscriptionTimewindow.realtimeWindowMs) { |
1293 | this.updateTimewindow(); | 1348 | this.updateTimewindow(); |
1294 | } | 1349 | } |
1295 | - if (updated) { | ||
1296 | - this.onDataUpdated(); | ||
1297 | - } | 1350 | + this.onDataUpdated(); |
1351 | + } | ||
1352 | + | ||
1353 | + private alarmsUpdated(_updated: Array<AlarmData>, alarms: PageData<AlarmData>) { | ||
1354 | + this.alarmsLoaded(alarms); | ||
1298 | } | 1355 | } |
1299 | 1356 | ||
1300 | private updateLegend(dataIndex: number, data: DataSet, detectChanges: boolean) { | 1357 | private updateLegend(dataIndex: number, data: DataSet, detectChanges: boolean) { |
@@ -16,8 +16,10 @@ | @@ -16,8 +16,10 @@ | ||
16 | 16 | ||
17 | import { Inject, Injectable, NgZone } from '@angular/core'; | 17 | import { Inject, Injectable, NgZone } from '@angular/core'; |
18 | import { | 18 | import { |
19 | + AlarmDataCmd, AlarmDataUnsubscribeCmd, | ||
20 | + AlarmDataUpdate, | ||
19 | AttributesSubscriptionCmd, EntityDataCmd, EntityDataUnsubscribeCmd, EntityDataUpdate, | 21 | AttributesSubscriptionCmd, EntityDataCmd, EntityDataUnsubscribeCmd, EntityDataUpdate, |
20 | - GetHistoryCmd, isEntityDataUpdateMsg, | 22 | + GetHistoryCmd, isAlarmDataUpdateMsg, isEntityDataUpdateMsg, |
21 | SubscriptionCmd, | 23 | SubscriptionCmd, |
22 | SubscriptionUpdate, | 24 | SubscriptionUpdate, |
23 | SubscriptionUpdateMsg, | 25 | SubscriptionUpdateMsg, |
@@ -107,6 +109,8 @@ export class TelemetryWebsocketService implements TelemetryService { | @@ -107,6 +109,8 @@ export class TelemetryWebsocketService implements TelemetryService { | ||
107 | this.cmdsWrapper.historyCmds.push(subscriptionCommand); | 109 | this.cmdsWrapper.historyCmds.push(subscriptionCommand); |
108 | } else if (subscriptionCommand instanceof EntityDataCmd) { | 110 | } else if (subscriptionCommand instanceof EntityDataCmd) { |
109 | this.cmdsWrapper.entityDataCmds.push(subscriptionCommand); | 111 | this.cmdsWrapper.entityDataCmds.push(subscriptionCommand); |
112 | + } else if (subscriptionCommand instanceof AlarmDataCmd) { | ||
113 | + this.cmdsWrapper.alarmDataCmds.push(subscriptionCommand); | ||
110 | } | 114 | } |
111 | } | 115 | } |
112 | ); | 116 | ); |
@@ -142,6 +146,10 @@ export class TelemetryWebsocketService implements TelemetryService { | @@ -142,6 +146,10 @@ export class TelemetryWebsocketService implements TelemetryService { | ||
142 | const entityDataUnsubscribeCmd = new EntityDataUnsubscribeCmd(); | 146 | const entityDataUnsubscribeCmd = new EntityDataUnsubscribeCmd(); |
143 | entityDataUnsubscribeCmd.cmdId = subscriptionCommand.cmdId; | 147 | entityDataUnsubscribeCmd.cmdId = subscriptionCommand.cmdId; |
144 | this.cmdsWrapper.entityDataUnsubscribeCmds.push(entityDataUnsubscribeCmd); | 148 | this.cmdsWrapper.entityDataUnsubscribeCmds.push(entityDataUnsubscribeCmd); |
149 | + } else if (subscriptionCommand instanceof AlarmDataCmd) { | ||
150 | + const alarmDataUnsubscribeCmd = new AlarmDataUnsubscribeCmd(); | ||
151 | + alarmDataUnsubscribeCmd.cmdId = subscriptionCommand.cmdId; | ||
152 | + this.cmdsWrapper.alarmDataUnsubscribeCmds.push(alarmDataUnsubscribeCmd); | ||
145 | } | 153 | } |
146 | const cmdId = subscriptionCommand.cmdId; | 154 | const cmdId = subscriptionCommand.cmdId; |
147 | if (cmdId) { | 155 | if (cmdId) { |
@@ -281,6 +289,11 @@ export class TelemetryWebsocketService implements TelemetryService { | @@ -281,6 +289,11 @@ export class TelemetryWebsocketService implements TelemetryService { | ||
281 | if (subscriber) { | 289 | if (subscriber) { |
282 | subscriber.onEntityData(new EntityDataUpdate(message)); | 290 | subscriber.onEntityData(new EntityDataUpdate(message)); |
283 | } | 291 | } |
292 | + } else if (isAlarmDataUpdateMsg(message)) { | ||
293 | + subscriber = this.subscribersMap.get(message.cmdId); | ||
294 | + if (subscriber) { | ||
295 | + subscriber.onAlarmData(new AlarmDataUpdate(message)); | ||
296 | + } | ||
284 | } else if (message.subscriptionId) { | 297 | } else if (message.subscriptionId) { |
285 | subscriber = this.subscribersMap.get(message.subscriptionId); | 298 | subscriber = this.subscribersMap.get(message.subscriptionId); |
286 | if (subscriber) { | 299 | if (subscriber) { |
@@ -62,7 +62,7 @@ | @@ -62,7 +62,7 @@ | ||
62 | </mat-toolbar> | 62 | </mat-toolbar> |
63 | <div fxFlex class="table-container"> | 63 | <div fxFlex class="table-container"> |
64 | <table mat-table [dataSource]="alarmsDatasource" | 64 | <table mat-table [dataSource]="alarmsDatasource" |
65 | - matSort [matSortActive]="sortOrderProperty" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear> | 65 | + matSort [matSortActive]="sortOrderProperty" [matSortDirection]="pageLinkSortDirection()" matSortDisableClear> |
66 | <ng-container matColumnDef="select" sticky> | 66 | <ng-container matColumnDef="select" sticky> |
67 | <mat-header-cell *matHeaderCellDef style="width: 30px;"> | 67 | <mat-header-cell *matHeaderCellDef style="width: 30px;"> |
68 | <mat-checkbox (change)="$event ? alarmsDatasource.masterToggle() : null" | 68 | <mat-checkbox (change)="$event ? alarmsDatasource.masterToggle() : null" |
@@ -125,9 +125,12 @@ | @@ -125,9 +125,12 @@ | ||
125 | *matRowDef="let alarm; columns: displayedColumns;" | 125 | *matRowDef="let alarm; columns: displayedColumns;" |
126 | (click)="onRowClick($event, alarm)"></mat-row> | 126 | (click)="onRowClick($event, alarm)"></mat-row> |
127 | </table> | 127 | </table> |
128 | - <span [fxShow]="alarmsDatasource.isEmpty() | async" | 128 | + <span [fxShow]="(alarmsDatasource.isEmpty() | async) && !alarmsDatasource.dataLoading" |
129 | fxLayoutAlign="center center" | 129 | fxLayoutAlign="center center" |
130 | class="no-data-found" translate>alarm.no-alarms-prompt</span> | 130 | class="no-data-found" translate>alarm.no-alarms-prompt</span> |
131 | + <span [fxShow]="alarmsDatasource.dataLoading" | ||
132 | + fxLayoutAlign="center center" | ||
133 | + class="no-data-found">{{ 'common.loading' | translate }}</span> | ||
131 | </div> | 134 | </div> |
132 | <mat-divider *ngIf="displayPagination"></mat-divider> | 135 | <mat-divider *ngIf="displayPagination"></mat-divider> |
133 | <mat-paginator *ngIf="displayPagination" | 136 | <mat-paginator *ngIf="displayPagination" |
@@ -29,21 +29,21 @@ import { PageComponent } from '@shared/components/page.component'; | @@ -29,21 +29,21 @@ import { PageComponent } from '@shared/components/page.component'; | ||
29 | import { Store } from '@ngrx/store'; | 29 | import { Store } from '@ngrx/store'; |
30 | import { AppState } from '@core/core.state'; | 30 | import { AppState } from '@core/core.state'; |
31 | import { WidgetAction, WidgetContext } from '@home/models/widget-component.models'; | 31 | import { WidgetAction, WidgetContext } from '@home/models/widget-component.models'; |
32 | -import { Datasource, WidgetActionDescriptor, WidgetConfig } from '@shared/models/widget.models'; | 32 | +import { DataKey, Datasource, WidgetActionDescriptor, WidgetConfig } from '@shared/models/widget.models'; |
33 | import { IWidgetSubscription } from '@core/api/widget-api.models'; | 33 | import { IWidgetSubscription } from '@core/api/widget-api.models'; |
34 | import { UtilsService } from '@core/services/utils.service'; | 34 | import { UtilsService } from '@core/services/utils.service'; |
35 | import { TranslateService } from '@ngx-translate/core'; | 35 | import { TranslateService } from '@ngx-translate/core'; |
36 | -import { deepClone, isDefined, isNumber, createLabelFromDatasource, hashCode } from '@core/utils'; | 36 | +import { createLabelFromDatasource, deepClone, hashCode, isDefined, isNumber } from '@core/utils'; |
37 | import cssjs from '@core/css/css'; | 37 | import cssjs from '@core/css/css'; |
38 | -import { PageLink } from '@shared/models/page/page-link'; | ||
39 | -import { Direction, SortOrder, sortOrderFromString } from '@shared/models/page/sort-order'; | 38 | +import { sortItems } from '@shared/models/page/page-link'; |
39 | +import { Direction } from '@shared/models/page/sort-order'; | ||
40 | import { CollectionViewer, DataSource, SelectionModel } from '@angular/cdk/collections'; | 40 | import { CollectionViewer, DataSource, SelectionModel } from '@angular/cdk/collections'; |
41 | -import { BehaviorSubject, forkJoin, fromEvent, merge, Observable, of } from 'rxjs'; | 41 | +import { BehaviorSubject, forkJoin, fromEvent, merge, Observable } from 'rxjs'; |
42 | import { emptyPageData, PageData } from '@shared/models/page/page-data'; | 42 | import { emptyPageData, PageData } from '@shared/models/page/page-data'; |
43 | import { entityTypeTranslations } from '@shared/models/entity-type.models'; | 43 | import { entityTypeTranslations } from '@shared/models/entity-type.models'; |
44 | -import { catchError, debounceTime, distinctUntilChanged, map, take, tap } from 'rxjs/operators'; | 44 | +import { debounceTime, distinctUntilChanged, map, take, tap } from 'rxjs/operators'; |
45 | import { MatPaginator } from '@angular/material/paginator'; | 45 | import { MatPaginator } from '@angular/material/paginator'; |
46 | -import { MatSort } from '@angular/material/sort'; | 46 | +import { MatSort, SortDirection } from '@angular/material/sort'; |
47 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; | 47 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; |
48 | import { | 48 | import { |
49 | CellContentInfo, | 49 | CellContentInfo, |
@@ -51,14 +51,16 @@ import { | @@ -51,14 +51,16 @@ import { | ||
51 | constructTableCssString, | 51 | constructTableCssString, |
52 | DisplayColumn, | 52 | DisplayColumn, |
53 | EntityColumn, | 53 | EntityColumn, |
54 | - fromAlarmColumnDef, | 54 | + entityDataSortOrderFromString, |
55 | + findColumnByEntityKey, | ||
56 | + findEntityKeyByColumnDef, | ||
57 | + fromEntityColumnDef, | ||
55 | getAlarmValue, | 58 | getAlarmValue, |
56 | getCellContentInfo, | 59 | getCellContentInfo, |
57 | getCellStyleInfo, | 60 | getCellStyleInfo, |
58 | getColumnWidth, | 61 | getColumnWidth, |
59 | TableWidgetDataKeySettings, | 62 | TableWidgetDataKeySettings, |
60 | TableWidgetSettings, | 63 | TableWidgetSettings, |
61 | - toAlarmColumnDef, | ||
62 | widthStyle | 64 | widthStyle |
63 | } from '@home/components/widget/lib/table-widget.models'; | 65 | } from '@home/components/widget/lib/table-widget.models'; |
64 | import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; | 66 | import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; |
@@ -69,8 +71,8 @@ import { | @@ -69,8 +71,8 @@ import { | ||
69 | DisplayColumnsPanelData | 71 | DisplayColumnsPanelData |
70 | } from '@home/components/widget/lib/display-columns-panel.component'; | 72 | } from '@home/components/widget/lib/display-columns-panel.component'; |
71 | import { | 73 | import { |
74 | + AlarmDataInfo, | ||
72 | alarmFields, | 75 | alarmFields, |
73 | - AlarmInfo, | ||
74 | alarmSeverityColors, | 76 | alarmSeverityColors, |
75 | alarmSeverityTranslations, | 77 | alarmSeverityTranslations, |
76 | AlarmStatus, | 78 | AlarmStatus, |
@@ -90,6 +92,15 @@ import { MatDialog } from '@angular/material/dialog'; | @@ -90,6 +92,15 @@ import { MatDialog } from '@angular/material/dialog'; | ||
90 | import { NULL_UUID } from '@shared/models/id/has-uuid'; | 92 | import { NULL_UUID } from '@shared/models/id/has-uuid'; |
91 | import { DialogService } from '@core/services/dialog.service'; | 93 | import { DialogService } from '@core/services/dialog.service'; |
92 | import { AlarmService } from '@core/http/alarm.service'; | 94 | import { AlarmService } from '@core/http/alarm.service'; |
95 | +import { | ||
96 | + AlarmData, | ||
97 | + AlarmDataPageLink, | ||
98 | + dataKeyToEntityKey, | ||
99 | + dataKeyTypeToEntityKeyType, | ||
100 | + entityDataPageLinkSortDirection, | ||
101 | + KeyFilter | ||
102 | +} from '@app/shared/models/query/query.models'; | ||
103 | +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; | ||
93 | 104 | ||
94 | interface AlarmsTableWidgetSettings extends TableWidgetSettings { | 105 | interface AlarmsTableWidgetSettings extends TableWidgetSettings { |
95 | alarmsTitle: string; | 106 | alarmsTitle: string; |
@@ -125,7 +136,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -125,7 +136,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
125 | public displayPagination = true; | 136 | public displayPagination = true; |
126 | public enableStickyAction = false; | 137 | public enableStickyAction = false; |
127 | public pageSizeOptions; | 138 | public pageSizeOptions; |
128 | - public pageLink: PageLink; | 139 | + public pageLink: AlarmDataPageLink; |
129 | public sortOrderProperty: string; | 140 | public sortOrderProperty: string; |
130 | public textSearchMode = false; | 141 | public textSearchMode = false; |
131 | public columns: Array<EntityColumn> = []; | 142 | public columns: Array<EntityColumn> = []; |
@@ -189,9 +200,11 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -189,9 +200,11 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
189 | private dialogService: DialogService, | 200 | private dialogService: DialogService, |
190 | private alarmService: AlarmService) { | 201 | private alarmService: AlarmService) { |
191 | super(store); | 202 | super(store); |
192 | - | ||
193 | - const sortOrder: SortOrder = sortOrderFromString(this.defaultSortOrder); | ||
194 | - this.pageLink = new PageLink(this.defaultPageSize, 0, null, sortOrder); | 203 | + this.pageLink = { |
204 | + page: 0, | ||
205 | + pageSize: this.defaultPageSize, | ||
206 | + textSearch: null | ||
207 | + }; | ||
195 | } | 208 | } |
196 | 209 | ||
197 | ngOnInit(): void { | 210 | ngOnInit(): void { |
@@ -232,11 +245,15 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -232,11 +245,15 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
232 | 245 | ||
233 | public onDataUpdated() { | 246 | public onDataUpdated() { |
234 | this.ngZone.run(() => { | 247 | this.ngZone.run(() => { |
235 | - this.alarmsDatasource.updateAlarms(this.subscription.alarms); | 248 | + this.alarmsDatasource.updateAlarms(); |
236 | this.ctx.detectChanges(); | 249 | this.ctx.detectChanges(); |
237 | }); | 250 | }); |
238 | } | 251 | } |
239 | 252 | ||
253 | + public pageLinkSortDirection(): SortDirection { | ||
254 | + return entityDataPageLinkSortDirection(this.pageLink); | ||
255 | + } | ||
256 | + | ||
240 | private initializeConfig() { | 257 | private initializeConfig() { |
241 | this.ctx.widgetActions = [this.searchAction, this.statusFilterAction, this.columnDisplayAction]; | 258 | this.ctx.widgetActions = [this.searchAction, this.statusFilterAction, this.columnDisplayAction]; |
242 | 259 | ||
@@ -304,6 +321,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -304,6 +321,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
304 | this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; | 321 | this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; |
305 | this.pageLink.pageSize = this.displayPagination ? this.defaultPageSize : Number.POSITIVE_INFINITY; | 322 | this.pageLink.pageSize = this.displayPagination ? this.defaultPageSize : Number.POSITIVE_INFINITY; |
306 | 323 | ||
324 | + // TODO: search status, severity, types, searchPropagatedAlarms from widget config to pageLink | ||
325 | + this.pageLink.searchPropagatedAlarms = false; // true for old widget configs | ||
326 | + this.pageLink.severityList = []; | ||
327 | + this.pageLink.statusList = []; | ||
328 | + this.pageLink.typeList = []; | ||
329 | + | ||
307 | const cssString = constructTableCssString(this.widgetConfig); | 330 | const cssString = constructTableCssString(this.widgetConfig); |
308 | const cssParser = new cssjs(); | 331 | const cssParser = new cssjs(); |
309 | cssParser.testMode = false; | 332 | cssParser.testMode = false; |
@@ -319,9 +342,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -319,9 +342,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
319 | this.displayedColumns.push('select'); | 342 | this.displayedColumns.push('select'); |
320 | } | 343 | } |
321 | 344 | ||
345 | + const latestDataKeys: Array<DataKey> = []; | ||
346 | + | ||
322 | if (this.alarmSource) { | 347 | if (this.alarmSource) { |
323 | this.alarmSource.dataKeys.forEach((alarmDataKey) => { | 348 | this.alarmSource.dataKeys.forEach((alarmDataKey) => { |
324 | const dataKey: EntityColumn = deepClone(alarmDataKey) as EntityColumn; | 349 | const dataKey: EntityColumn = deepClone(alarmDataKey) as EntityColumn; |
350 | + dataKey.entityKey = dataKeyToEntityKey(alarmDataKey); | ||
325 | dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label); | 351 | dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label); |
326 | dataKey.def = 'def' + this.columns.length; | 352 | dataKey.def = 'def' + this.columns.length; |
327 | const keySettings: TableWidgetDataKeySettings = dataKey.settings; | 353 | const keySettings: TableWidgetDataKeySettings = dataKey.settings; |
@@ -330,19 +356,28 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -330,19 +356,28 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
330 | this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx'); | 356 | this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx'); |
331 | this.columnWidth[dataKey.def] = getColumnWidth(keySettings); | 357 | this.columnWidth[dataKey.def] = getColumnWidth(keySettings); |
332 | this.columns.push(dataKey); | 358 | this.columns.push(dataKey); |
359 | + | ||
360 | + if (dataKey.type !== DataKeyType.alarm) { | ||
361 | + latestDataKeys.push(dataKey); | ||
362 | + } | ||
333 | }); | 363 | }); |
334 | this.displayedColumns.push(...this.columns.map(column => column.def)); | 364 | this.displayedColumns.push(...this.columns.map(column => column.def)); |
335 | } | 365 | } |
336 | if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) { | 366 | if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) { |
337 | this.defaultSortOrder = this.settings.defaultSortOrder; | 367 | this.defaultSortOrder = this.settings.defaultSortOrder; |
338 | } | 368 | } |
339 | - this.pageLink.sortOrder = sortOrderFromString(this.defaultSortOrder); | ||
340 | - this.sortOrderProperty = toAlarmColumnDef(this.pageLink.sortOrder.property, this.columns); | 369 | + this.pageLink.sortOrder = entityDataSortOrderFromString(this.defaultSortOrder, this.columns); |
370 | + let sortColumn: EntityColumn; | ||
371 | + if (this.pageLink.sortOrder) { | ||
372 | + sortColumn = findColumnByEntityKey(this.pageLink.sortOrder.key, this.columns); | ||
373 | + } | ||
374 | + this.sortOrderProperty = sortColumn ? sortColumn.def : null; | ||
341 | 375 | ||
342 | if (this.actionCellDescriptors.length) { | 376 | if (this.actionCellDescriptors.length) { |
343 | this.displayedColumns.push('actions'); | 377 | this.displayedColumns.push('actions'); |
344 | } | 378 | } |
345 | - this.alarmsDatasource = new AlarmsDatasource(); | 379 | + |
380 | + this.alarmsDatasource = new AlarmsDatasource(this.subscription, latestDataKeys); | ||
346 | if (this.enableSelection) { | 381 | if (this.enableSelection) { |
347 | this.alarmsDatasource.selectionModeChanged$.subscribe((selectionMode) => { | 382 | this.alarmsDatasource.selectionModeChanged$.subscribe((selectionMode) => { |
348 | const hideTitlePanel = selectionMode || this.textSearchMode; | 383 | const hideTitlePanel = selectionMode || this.textSearchMode; |
@@ -467,9 +502,13 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -467,9 +502,13 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
467 | } else { | 502 | } else { |
468 | this.pageLink.page = 0; | 503 | this.pageLink.page = 0; |
469 | } | 504 | } |
470 | - this.pageLink.sortOrder.property = fromAlarmColumnDef(this.sort.active, this.columns); | ||
471 | - this.pageLink.sortOrder.direction = Direction[this.sort.direction.toUpperCase()]; | ||
472 | - this.alarmsDatasource.loadAlarms(this.pageLink); | 505 | + this.pageLink.sortOrder = { |
506 | + key: findEntityKeyByColumnDef(this.sort.active, this.columns), | ||
507 | + direction: Direction[this.sort.direction.toUpperCase()] | ||
508 | + }; | ||
509 | + const sortOrderLabel = fromEntityColumnDef(this.sort.active, this.columns); | ||
510 | + const keyFilters: KeyFilter[] = null; // TODO: | ||
511 | + this.alarmsDatasource.loadAlarms(this.pageLink, sortOrderLabel, keyFilters); | ||
473 | this.ctx.detectChanges(); | 512 | this.ctx.detectChanges(); |
474 | } | 513 | } |
475 | 514 | ||
@@ -482,7 +521,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -482,7 +521,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
482 | return widthStyle(columnWidth); | 521 | return widthStyle(columnWidth); |
483 | } | 522 | } |
484 | 523 | ||
485 | - public cellStyle(alarm: AlarmInfo, key: EntityColumn): any { | 524 | + public cellStyle(alarm: AlarmDataInfo, key: EntityColumn): any { |
486 | let style: any = {}; | 525 | let style: any = {}; |
487 | if (alarm && key) { | 526 | if (alarm && key) { |
488 | const styleInfo = this.stylesInfo[key.def]; | 527 | const styleInfo = this.stylesInfo[key.def]; |
@@ -504,7 +543,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -504,7 +543,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
504 | return style; | 543 | return style; |
505 | } | 544 | } |
506 | 545 | ||
507 | - public cellContent(alarm: AlarmInfo, key: EntityColumn): SafeHtml { | 546 | + public cellContent(alarm: AlarmDataInfo, key: EntityColumn): SafeHtml { |
508 | if (alarm && key) { | 547 | if (alarm && key) { |
509 | const contentInfo = this.contentsInfo[key.def]; | 548 | const contentInfo = this.contentsInfo[key.def]; |
510 | const value = getAlarmValue(alarm, key); | 549 | const value = getAlarmValue(alarm, key); |
@@ -524,7 +563,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -524,7 +563,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
524 | } | 563 | } |
525 | } | 564 | } |
526 | 565 | ||
527 | - public onRowClick($event: Event, alarm: AlarmInfo) { | 566 | + public onRowClick($event: Event, alarm: AlarmDataInfo) { |
528 | if ($event) { | 567 | if ($event) { |
529 | $event.stopPropagation(); | 568 | $event.stopPropagation(); |
530 | } | 569 | } |
@@ -541,7 +580,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -541,7 +580,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
541 | } | 580 | } |
542 | } | 581 | } |
543 | 582 | ||
544 | - public onActionButtonClick($event: Event, alarm: AlarmInfo, actionDescriptor: AlarmWidgetActionDescriptor) { | 583 | + public onActionButtonClick($event: Event, alarm: AlarmDataInfo, actionDescriptor: AlarmWidgetActionDescriptor) { |
545 | if (actionDescriptor.details) { | 584 | if (actionDescriptor.details) { |
546 | this.openAlarmDetails($event, alarm); | 585 | this.openAlarmDetails($event, alarm); |
547 | } else if (actionDescriptor.acknowledge) { | 586 | } else if (actionDescriptor.acknowledge) { |
@@ -562,7 +601,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -562,7 +601,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
562 | } | 601 | } |
563 | } | 602 | } |
564 | 603 | ||
565 | - public actionEnabled(alarm: AlarmInfo, actionDescriptor: AlarmWidgetActionDescriptor): boolean { | 604 | + public actionEnabled(alarm: AlarmDataInfo, actionDescriptor: AlarmWidgetActionDescriptor): boolean { |
566 | if (actionDescriptor.acknowledge) { | 605 | if (actionDescriptor.acknowledge) { |
567 | return (alarm.status === AlarmStatus.ACTIVE_UNACK || | 606 | return (alarm.status === AlarmStatus.ACTIVE_UNACK || |
568 | alarm.status === AlarmStatus.CLEARED_UNACK); | 607 | alarm.status === AlarmStatus.CLEARED_UNACK); |
@@ -573,7 +612,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -573,7 +612,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
573 | return true; | 612 | return true; |
574 | } | 613 | } |
575 | 614 | ||
576 | - private openAlarmDetails($event: Event, alarm: AlarmInfo) { | 615 | + private openAlarmDetails($event: Event, alarm: AlarmDataInfo) { |
577 | if ($event) { | 616 | if ($event) { |
578 | $event.stopPropagation(); | 617 | $event.stopPropagation(); |
579 | } | 618 | } |
@@ -599,7 +638,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -599,7 +638,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
599 | } | 638 | } |
600 | } | 639 | } |
601 | 640 | ||
602 | - private ackAlarm($event: Event, alarm: AlarmInfo) { | 641 | + private ackAlarm($event: Event, alarm: AlarmDataInfo) { |
603 | if ($event) { | 642 | if ($event) { |
604 | $event.stopPropagation(); | 643 | $event.stopPropagation(); |
605 | } | 644 | } |
@@ -626,12 +665,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -626,12 +665,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
626 | $event.stopPropagation(); | 665 | $event.stopPropagation(); |
627 | } | 666 | } |
628 | if (this.alarmsDatasource.selection.hasValue()) { | 667 | if (this.alarmsDatasource.selection.hasValue()) { |
629 | - const alarms = this.alarmsDatasource.selection.selected.filter( | ||
630 | - (alarm) => alarm.id.id !== NULL_UUID | 668 | + const alarmIds = this.alarmsDatasource.selection.selected.filter( |
669 | + (alarmId) => alarmId !== NULL_UUID | ||
631 | ); | 670 | ); |
632 | - if (alarms.length) { | ||
633 | - const title = this.translate.instant('alarm.aknowledge-alarms-title', {count: alarms.length}); | ||
634 | - const content = this.translate.instant('alarm.aknowledge-alarms-text', {count: alarms.length}); | 671 | + if (alarmIds.length) { |
672 | + const title = this.translate.instant('alarm.aknowledge-alarms-title', {count: alarmIds.length}); | ||
673 | + const content = this.translate.instant('alarm.aknowledge-alarms-text', {count: alarmIds.length}); | ||
635 | this.dialogService.confirm( | 674 | this.dialogService.confirm( |
636 | title, | 675 | title, |
637 | content, | 676 | content, |
@@ -641,8 +680,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -641,8 +680,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
641 | if (res) { | 680 | if (res) { |
642 | if (res) { | 681 | if (res) { |
643 | const tasks: Observable<void>[] = []; | 682 | const tasks: Observable<void>[] = []; |
644 | - for (const alarm of alarms) { | ||
645 | - tasks.push(this.alarmService.ackAlarm(alarm.id.id)); | 683 | + for (const alarmId of alarmIds) { |
684 | + tasks.push(this.alarmService.ackAlarm(alarmId)); | ||
646 | } | 685 | } |
647 | forkJoin(tasks).subscribe(() => { | 686 | forkJoin(tasks).subscribe(() => { |
648 | this.alarmsDatasource.clearSelection(); | 687 | this.alarmsDatasource.clearSelection(); |
@@ -655,7 +694,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -655,7 +694,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
655 | } | 694 | } |
656 | } | 695 | } |
657 | 696 | ||
658 | - private clearAlarm($event: Event, alarm: AlarmInfo) { | 697 | + private clearAlarm($event: Event, alarm: AlarmDataInfo) { |
659 | if ($event) { | 698 | if ($event) { |
660 | $event.stopPropagation(); | 699 | $event.stopPropagation(); |
661 | } | 700 | } |
@@ -682,12 +721,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -682,12 +721,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
682 | $event.stopPropagation(); | 721 | $event.stopPropagation(); |
683 | } | 722 | } |
684 | if (this.alarmsDatasource.selection.hasValue()) { | 723 | if (this.alarmsDatasource.selection.hasValue()) { |
685 | - const alarms = this.alarmsDatasource.selection.selected.filter( | ||
686 | - (alarm) => alarm.id.id !== NULL_UUID | 724 | + const alarmIds = this.alarmsDatasource.selection.selected.filter( |
725 | + (alarmId) => alarmId !== NULL_UUID | ||
687 | ); | 726 | ); |
688 | - if (alarms.length) { | ||
689 | - const title = this.translate.instant('alarm.clear-alarms-title', {count: alarms.length}); | ||
690 | - const content = this.translate.instant('alarm.clear-alarms-text', {count: alarms.length}); | 727 | + if (alarmIds.length) { |
728 | + const title = this.translate.instant('alarm.clear-alarms-title', {count: alarmIds.length}); | ||
729 | + const content = this.translate.instant('alarm.clear-alarms-text', {count: alarmIds.length}); | ||
691 | this.dialogService.confirm( | 730 | this.dialogService.confirm( |
692 | title, | 731 | title, |
693 | content, | 732 | content, |
@@ -697,8 +736,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -697,8 +736,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
697 | if (res) { | 736 | if (res) { |
698 | if (res) { | 737 | if (res) { |
699 | const tasks: Observable<void>[] = []; | 738 | const tasks: Observable<void>[] = []; |
700 | - for (const alarm of alarms) { | ||
701 | - tasks.push(this.alarmService.clearAlarm(alarm.id.id)); | 739 | + for (const alarmId of alarmIds) { |
740 | + tasks.push(this.alarmService.clearAlarm(alarmId)); | ||
702 | } | 741 | } |
703 | forkJoin(tasks).subscribe(() => { | 742 | forkJoin(tasks).subscribe(() => { |
704 | this.alarmsDatasource.clearSelection(); | 743 | this.alarmsDatasource.clearSelection(); |
@@ -756,27 +795,29 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | @@ -756,27 +795,29 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, | ||
756 | 795 | ||
757 | } | 796 | } |
758 | 797 | ||
759 | -class AlarmsDatasource implements DataSource<AlarmInfo> { | 798 | +class AlarmsDatasource implements DataSource<AlarmDataInfo> { |
760 | 799 | ||
761 | - private alarmsSubject = new BehaviorSubject<AlarmInfo[]>([]); | ||
762 | - private pageDataSubject = new BehaviorSubject<PageData<AlarmInfo>>(emptyPageData<AlarmInfo>()); | 800 | + private alarmsSubject = new BehaviorSubject<AlarmDataInfo[]>([]); |
801 | + private pageDataSubject = new BehaviorSubject<PageData<AlarmDataInfo>>(emptyPageData<AlarmDataInfo>()); | ||
763 | 802 | ||
764 | - public selection = new SelectionModel<AlarmInfo>(true, [], false); | 803 | + public selection = new SelectionModel<string>(true, [], false); |
765 | 804 | ||
766 | private selectionModeChanged = new EventEmitter<boolean>(); | 805 | private selectionModeChanged = new EventEmitter<boolean>(); |
767 | 806 | ||
768 | - public selectionModeChanged$ = this.selectionModeChanged.asObservable(); | 807 | + public selectionModeChanged$ = this.selectionModeChanged.asObservable(); |
808 | + | ||
809 | + private currentAlarm: AlarmDataInfo = null; | ||
769 | 810 | ||
770 | - private allAlarms: Array<AlarmInfo> = []; | ||
771 | - private allAlarmsSubject = new BehaviorSubject<AlarmInfo[]>([]); | ||
772 | - private allAlarms$: Observable<Array<AlarmInfo>> = this.allAlarmsSubject.asObservable(); | 811 | + public dataLoading = true; |
773 | 812 | ||
774 | - private currentAlarm: AlarmInfo = null; | 813 | + private appliedPageLink: AlarmDataPageLink; |
814 | + private appliedSortOrderLabel: string; | ||
775 | 815 | ||
776 | - constructor() { | 816 | + constructor(private subscription: IWidgetSubscription, |
817 | + private dataKeys: Array<DataKey>) { | ||
777 | } | 818 | } |
778 | 819 | ||
779 | - connect(collectionViewer: CollectionViewer): Observable<AlarmInfo[] | ReadonlyArray<AlarmInfo>> { | 820 | + connect(collectionViewer: CollectionViewer): Observable<AlarmDataInfo[] | ReadonlyArray<AlarmDataInfo>> { |
780 | return this.alarmsSubject.asObservable(); | 821 | return this.alarmsSubject.asObservable(); |
781 | } | 822 | } |
782 | 823 | ||
@@ -785,51 +826,63 @@ class AlarmsDatasource implements DataSource<AlarmInfo> { | @@ -785,51 +826,63 @@ class AlarmsDatasource implements DataSource<AlarmInfo> { | ||
785 | this.pageDataSubject.complete(); | 826 | this.pageDataSubject.complete(); |
786 | } | 827 | } |
787 | 828 | ||
788 | - loadAlarms(pageLink: PageLink) { | 829 | + loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { |
789 | if (this.selection.hasValue()) { | 830 | if (this.selection.hasValue()) { |
790 | this.selection.clear(); | 831 | this.selection.clear(); |
791 | this.onSelectionModeChanged(false); | 832 | this.onSelectionModeChanged(false); |
792 | } | 833 | } |
793 | - this.fetchAlarms(pageLink).pipe( | ||
794 | - catchError(() => of(emptyPageData<AlarmInfo>())), | ||
795 | - ).subscribe( | ||
796 | - (pageData) => { | ||
797 | - this.alarmsSubject.next(pageData.data); | ||
798 | - this.pageDataSubject.next(pageData); | ||
799 | - } | ||
800 | - ); | 834 | + this.appliedPageLink = pageLink; |
835 | + this.appliedSortOrderLabel = sortOrderLabel; | ||
836 | + this.subscription.subscribeForAlarms(pageLink, keyFilters); | ||
801 | } | 837 | } |
802 | 838 | ||
803 | - updateAlarms(alarms: AlarmInfo[]) { | ||
804 | - alarms.forEach((newAlarm) => { | ||
805 | - const existingAlarmIndex = this.allAlarms.findIndex(alarm => alarm.id.id === newAlarm.id.id); | ||
806 | - if (existingAlarmIndex > -1) { | ||
807 | - Object.assign(this.allAlarms[existingAlarmIndex], newAlarm); | ||
808 | - } else { | ||
809 | - this.allAlarms.push(newAlarm); | ||
810 | - } | 839 | + updateAlarms() { |
840 | + const subscriptionAlarms = this.subscription.alarms; | ||
841 | + let alarms = new Array<AlarmDataInfo>(); | ||
842 | + subscriptionAlarms.data.forEach((alarmData) => { | ||
843 | + alarms.push(this.alarmDataToInfo(alarmData)); | ||
811 | }); | 844 | }); |
812 | - for (let i = this.allAlarms.length - 1; i >= 0; i--) { | ||
813 | - const oldAlarm = this.allAlarms[i]; | ||
814 | - const newAlarmIndex = alarms.findIndex(alarm => alarm.id.id === oldAlarm.id.id); | ||
815 | - if (newAlarmIndex === -1) { | ||
816 | - this.allAlarms.splice(i, 1); | ||
817 | - } | 845 | + if (this.appliedSortOrderLabel && this.appliedSortOrderLabel.length) { |
846 | + const asc = this.appliedPageLink.sortOrder.direction === Direction.ASC; | ||
847 | + alarms = alarms.sort((a, b) => sortItems(a, b, this.appliedSortOrderLabel, asc)); | ||
818 | } | 848 | } |
819 | if (this.selection.hasValue()) { | 849 | if (this.selection.hasValue()) { |
820 | - const toRemove: AlarmInfo[] = []; | ||
821 | - this.selection.selected.forEach((selectedAlarm) => { | ||
822 | - const existingAlarm = this.allAlarms.find(alarm => alarm.id.id === selectedAlarm.id.id); | ||
823 | - if (!existingAlarm) { | ||
824 | - toRemove.push(selectedAlarm); | ||
825 | - } | ||
826 | - }); | 850 | + const alarmIds = alarms.map((alarm) => alarm.id.id); |
851 | + const toRemove = this.selection.selected.filter(alarmId => alarmIds.indexOf(alarmId) === -1); | ||
827 | this.selection.deselect(...toRemove); | 852 | this.selection.deselect(...toRemove); |
828 | if (this.selection.isEmpty()) { | 853 | if (this.selection.isEmpty()) { |
829 | this.onSelectionModeChanged(false); | 854 | this.onSelectionModeChanged(false); |
830 | } | 855 | } |
831 | } | 856 | } |
832 | - this.allAlarmsSubject.next(this.allAlarms); | 857 | + const alarmsPageData: PageData<AlarmDataInfo> = { |
858 | + data: alarms, | ||
859 | + totalPages: subscriptionAlarms.totalPages, | ||
860 | + totalElements: subscriptionAlarms.totalElements, | ||
861 | + hasNext: subscriptionAlarms.hasNext | ||
862 | + }; | ||
863 | + this.alarmsSubject.next(alarms); | ||
864 | + this.pageDataSubject.next(alarmsPageData); | ||
865 | + this.dataLoading = false; | ||
866 | + } | ||
867 | + | ||
868 | + private alarmDataToInfo(alarmData: AlarmData): AlarmDataInfo { | ||
869 | + const alarm: AlarmDataInfo = deepClone(alarmData); | ||
870 | + delete alarm.latest; | ||
871 | + const latest = alarmData.latest; | ||
872 | + this.dataKeys.forEach((dataKey, index) => { | ||
873 | + const type = dataKeyTypeToEntityKeyType(dataKey.type); | ||
874 | + let value = ''; | ||
875 | + if (type) { | ||
876 | + if (latest && latest[type]) { | ||
877 | + const tsVal = latest[type][dataKey.name]; | ||
878 | + if (tsVal) { | ||
879 | + value = tsVal.value; | ||
880 | + } | ||
881 | + } | ||
882 | + } | ||
883 | + alarm[dataKey.label] = value; | ||
884 | + }); | ||
885 | + return alarm; | ||
833 | } | 886 | } |
834 | 887 | ||
835 | isAllSelected(): Observable<boolean> { | 888 | isAllSelected(): Observable<boolean> { |
@@ -851,16 +904,16 @@ class AlarmsDatasource implements DataSource<AlarmInfo> { | @@ -851,16 +904,16 @@ class AlarmsDatasource implements DataSource<AlarmInfo> { | ||
851 | ); | 904 | ); |
852 | } | 905 | } |
853 | 906 | ||
854 | - toggleSelection(alarm: AlarmInfo) { | 907 | + toggleSelection(alarm: AlarmDataInfo) { |
855 | const hasValue = this.selection.hasValue(); | 908 | const hasValue = this.selection.hasValue(); |
856 | - this.selection.toggle(alarm); | 909 | + this.selection.toggle(alarm.id.id); |
857 | if (hasValue !== this.selection.hasValue()) { | 910 | if (hasValue !== this.selection.hasValue()) { |
858 | this.onSelectionModeChanged(this.selection.hasValue()); | 911 | this.onSelectionModeChanged(this.selection.hasValue()); |
859 | } | 912 | } |
860 | } | 913 | } |
861 | 914 | ||
862 | - isSelected(alarm: AlarmInfo): boolean { | ||
863 | - return this.selection.isSelected(alarm); | 915 | + isSelected(alarm: AlarmDataInfo): boolean { |
916 | + return this.selection.isSelected(alarm.id.id); | ||
864 | } | 917 | } |
865 | 918 | ||
866 | clearSelection() { | 919 | clearSelection() { |
@@ -881,7 +934,7 @@ class AlarmsDatasource implements DataSource<AlarmInfo> { | @@ -881,7 +934,7 @@ class AlarmsDatasource implements DataSource<AlarmInfo> { | ||
881 | } | 934 | } |
882 | } else { | 935 | } else { |
883 | alarms.forEach(row => { | 936 | alarms.forEach(row => { |
884 | - this.selection.select(row); | 937 | + this.selection.select(row.id.id); |
885 | }); | 938 | }); |
886 | if (numSelected === 0) { | 939 | if (numSelected === 0) { |
887 | this.onSelectionModeChanged(true); | 940 | this.onSelectionModeChanged(true); |
@@ -892,7 +945,7 @@ class AlarmsDatasource implements DataSource<AlarmInfo> { | @@ -892,7 +945,7 @@ class AlarmsDatasource implements DataSource<AlarmInfo> { | ||
892 | ).subscribe(); | 945 | ).subscribe(); |
893 | } | 946 | } |
894 | 947 | ||
895 | - public toggleCurrentAlarm(alarm: AlarmInfo): boolean { | 948 | + public toggleCurrentAlarm(alarm: AlarmDataInfo): boolean { |
896 | if (this.currentAlarm !== alarm) { | 949 | if (this.currentAlarm !== alarm) { |
897 | this.currentAlarm = alarm; | 950 | this.currentAlarm = alarm; |
898 | return true; | 951 | return true; |
@@ -901,7 +954,7 @@ class AlarmsDatasource implements DataSource<AlarmInfo> { | @@ -901,7 +954,7 @@ class AlarmsDatasource implements DataSource<AlarmInfo> { | ||
901 | } | 954 | } |
902 | } | 955 | } |
903 | 956 | ||
904 | - public isCurrentAlarm(alarm: AlarmInfo): boolean { | 957 | + public isCurrentAlarm(alarm: AlarmDataInfo): boolean { |
905 | return (this.currentAlarm && alarm && this.currentAlarm.id && alarm.id) && | 958 | return (this.currentAlarm && alarm && this.currentAlarm.id && alarm.id) && |
906 | (this.currentAlarm.id.id === alarm.id.id); | 959 | (this.currentAlarm.id.id === alarm.id.id); |
907 | } | 960 | } |
@@ -909,10 +962,4 @@ class AlarmsDatasource implements DataSource<AlarmInfo> { | @@ -909,10 +962,4 @@ class AlarmsDatasource implements DataSource<AlarmInfo> { | ||
909 | private onSelectionModeChanged(selectionMode: boolean) { | 962 | private onSelectionModeChanged(selectionMode: boolean) { |
910 | this.selectionModeChanged.emit(selectionMode); | 963 | this.selectionModeChanged.emit(selectionMode); |
911 | } | 964 | } |
912 | - | ||
913 | - private fetchAlarms(pageLink: PageLink): Observable<PageData<AlarmInfo>> { | ||
914 | - return this.allAlarms$.pipe( | ||
915 | - map((data) => pageLink.filterData(data)) | ||
916 | - ); | ||
917 | - } | ||
918 | } | 965 | } |
@@ -77,6 +77,7 @@ import { | @@ -77,6 +77,7 @@ import { | ||
77 | DisplayColumnsPanelData | 77 | DisplayColumnsPanelData |
78 | } from '@home/components/widget/lib/display-columns-panel.component'; | 78 | } from '@home/components/widget/lib/display-columns-panel.component'; |
79 | import { | 79 | import { |
80 | + dataKeyToEntityKey, | ||
80 | Direction, | 81 | Direction, |
81 | EntityDataPageLink, | 82 | EntityDataPageLink, |
82 | entityDataPageLinkSortDirection, | 83 | entityDataPageLinkSortDirection, |
@@ -205,7 +206,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni | @@ -205,7 +206,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni | ||
205 | 206 | ||
206 | public onDataUpdated() { | 207 | public onDataUpdated() { |
207 | this.ngZone.run(() => { | 208 | this.ngZone.run(() => { |
208 | - this.entityDatasource.dataUpdated(); // .updateEntitiesData(this.subscription.data); | 209 | + this.entityDatasource.dataUpdated(); |
209 | this.ctx.detectChanges(); | 210 | this.ctx.detectChanges(); |
210 | }); | 211 | }); |
211 | } | 212 | } |
@@ -339,19 +340,9 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni | @@ -339,19 +340,9 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni | ||
339 | if (datasource && datasource.dataKeys) { | 340 | if (datasource && datasource.dataKeys) { |
340 | datasource.dataKeys.forEach((entityDataKey) => { | 341 | datasource.dataKeys.forEach((entityDataKey) => { |
341 | const dataKey: EntityColumn = deepClone(entityDataKey) as EntityColumn; | 342 | const dataKey: EntityColumn = deepClone(entityDataKey) as EntityColumn; |
342 | - dataKey.entityKey = { | ||
343 | - key: dataKey.name, | ||
344 | - type: null | ||
345 | - }; | 343 | + dataKey.entityKey = dataKeyToEntityKey(entityDataKey); |
346 | if (dataKey.type === DataKeyType.function) { | 344 | if (dataKey.type === DataKeyType.function) { |
347 | dataKey.name = dataKey.label; | 345 | dataKey.name = dataKey.label; |
348 | - dataKey.entityKey.type = EntityKeyType.ENTITY_FIELD; | ||
349 | - } else if (dataKey.type === DataKeyType.entityField) { | ||
350 | - dataKey.entityKey.type = EntityKeyType.ENTITY_FIELD; | ||
351 | - } else if (dataKey.type === DataKeyType.attribute) { | ||
352 | - dataKey.entityKey.type = EntityKeyType.ATTRIBUTE; | ||
353 | - } else if (dataKey.type === DataKeyType.timeseries) { | ||
354 | - dataKey.entityKey.type = EntityKeyType.TIME_SERIES; | ||
355 | } | 346 | } |
356 | dataKeys.push(dataKey); | 347 | dataKeys.push(dataKey); |
357 | 348 |
@@ -17,9 +17,10 @@ | @@ -17,9 +17,10 @@ | ||
17 | import { EntityId } from '@shared/models/id/entity-id'; | 17 | import { EntityId } from '@shared/models/id/entity-id'; |
18 | import { DataKey, WidgetConfig } from '@shared/models/widget.models'; | 18 | import { DataKey, WidgetConfig } from '@shared/models/widget.models'; |
19 | import { getDescendantProp, isDefined } from '@core/utils'; | 19 | import { getDescendantProp, isDefined } from '@core/utils'; |
20 | -import { alarmFields, AlarmInfo } from '@shared/models/alarm.models'; | 20 | +import { AlarmDataInfo, alarmFields } from '@shared/models/alarm.models'; |
21 | import * as tinycolor_ from 'tinycolor2'; | 21 | import * as tinycolor_ from 'tinycolor2'; |
22 | import { Direction, EntityDataSortOrder, EntityKey } from '@shared/models/query/query.models'; | 22 | import { Direction, EntityDataSortOrder, EntityKey } from '@shared/models/query/query.models'; |
23 | +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; | ||
23 | 24 | ||
24 | const tinycolor = tinycolor_; | 25 | const tinycolor = tinycolor_; |
25 | 26 | ||
@@ -120,7 +121,15 @@ export function findColumn(searchProperty: string, searchValue: string, columns: | @@ -120,7 +121,15 @@ export function findColumn(searchProperty: string, searchValue: string, columns: | ||
120 | } | 121 | } |
121 | 122 | ||
122 | export function findColumnByLabel(label: string, columns: EntityColumn[]): EntityColumn { | 123 | export function findColumnByLabel(label: string, columns: EntityColumn[]): EntityColumn { |
123 | - return findColumn('label', label, columns); | 124 | + let column: EntityColumn; |
125 | + const alarmColumns = columns.filter(c => c.type === DataKeyType.alarm); | ||
126 | + if (alarmColumns.length) { | ||
127 | + column = findColumn('name', label, alarmColumns); | ||
128 | + } | ||
129 | + if (!column) { | ||
130 | + column = findColumn('label', label, columns); | ||
131 | + } | ||
132 | + return column; | ||
124 | } | 133 | } |
125 | 134 | ||
126 | export function findColumnByDef(def: string, columns: EntityColumn[]): EntityColumn { | 135 | export function findColumnByDef(def: string, columns: EntityColumn[]): EntityColumn { |
@@ -160,12 +169,12 @@ export function getEntityValue(entity: any, key: DataKey): any { | @@ -160,12 +169,12 @@ export function getEntityValue(entity: any, key: DataKey): any { | ||
160 | return getDescendantProp(entity, key.label); | 169 | return getDescendantProp(entity, key.label); |
161 | } | 170 | } |
162 | 171 | ||
163 | -export function getAlarmValue(alarm: AlarmInfo, key: EntityColumn) { | 172 | +export function getAlarmValue(alarm: AlarmDataInfo, key: EntityColumn) { |
164 | const alarmField = alarmFields[key.name]; | 173 | const alarmField = alarmFields[key.name]; |
165 | if (alarmField) { | 174 | if (alarmField) { |
166 | return getDescendantProp(alarm, alarmField.value); | 175 | return getDescendantProp(alarm, alarmField.value); |
167 | } else { | 176 | } else { |
168 | - return getDescendantProp(alarm, key.name); | 177 | + return getDescendantProp(alarm, key.label); |
169 | } | 178 | } |
170 | } | 179 | } |
171 | 180 |
@@ -94,6 +94,7 @@ import { ResizeObserver } from '@juggle/resize-observer'; | @@ -94,6 +94,7 @@ import { ResizeObserver } from '@juggle/resize-observer'; | ||
94 | import { EntityDataService } from '@core/api/entity-data.service'; | 94 | import { EntityDataService } from '@core/api/entity-data.service'; |
95 | import { TranslateService } from '@ngx-translate/core'; | 95 | import { TranslateService } from '@ngx-translate/core'; |
96 | import { NotificationType } from '@core/notification/notification.models'; | 96 | import { NotificationType } from '@core/notification/notification.models'; |
97 | +import { AlarmDataService } from '@core/api/alarm-data.service'; | ||
97 | 98 | ||
98 | @Component({ | 99 | @Component({ |
99 | selector: 'tb-widget', | 100 | selector: 'tb-widget', |
@@ -167,9 +168,9 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI | @@ -167,9 +168,9 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI | ||
167 | private timeService: TimeService, | 168 | private timeService: TimeService, |
168 | private deviceService: DeviceService, | 169 | private deviceService: DeviceService, |
169 | private entityService: EntityService, | 170 | private entityService: EntityService, |
170 | - private alarmService: AlarmService, | ||
171 | private dashboardService: DashboardService, | 171 | private dashboardService: DashboardService, |
172 | private entityDataService: EntityDataService, | 172 | private entityDataService: EntityDataService, |
173 | + private alarmDataService: AlarmDataService, | ||
173 | private translate: TranslateService, | 174 | private translate: TranslateService, |
174 | private utils: UtilsService, | 175 | private utils: UtilsService, |
175 | private raf: RafService, | 176 | private raf: RafService, |
@@ -300,9 +301,9 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI | @@ -300,9 +301,9 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI | ||
300 | this.subscriptionContext = new WidgetSubscriptionContext(this.widgetContext.dashboard); | 301 | this.subscriptionContext = new WidgetSubscriptionContext(this.widgetContext.dashboard); |
301 | this.subscriptionContext.timeService = this.timeService; | 302 | this.subscriptionContext.timeService = this.timeService; |
302 | this.subscriptionContext.deviceService = this.deviceService; | 303 | this.subscriptionContext.deviceService = this.deviceService; |
303 | - this.subscriptionContext.alarmService = this.alarmService; | ||
304 | this.subscriptionContext.translate = this.translate; | 304 | this.subscriptionContext.translate = this.translate; |
305 | this.subscriptionContext.entityDataService = this.entityDataService; | 305 | this.subscriptionContext.entityDataService = this.entityDataService; |
306 | + this.subscriptionContext.alarmDataService = this.alarmDataService; | ||
306 | this.subscriptionContext.utils = this.utils; | 307 | this.subscriptionContext.utils = this.utils; |
307 | this.subscriptionContext.raf = this.raf; | 308 | this.subscriptionContext.raf = this.raf; |
308 | this.subscriptionContext.widgetUtils = this.widgetContext.utils; | 309 | this.subscriptionContext.widgetUtils = this.widgetContext.utils; |
@@ -901,14 +902,14 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI | @@ -901,14 +902,14 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI | ||
901 | }; | 902 | }; |
902 | if (this.widget.type === widgetType.alarm) { | 903 | if (this.widget.type === widgetType.alarm) { |
903 | options.alarmSource = deepClone(this.widget.config.alarmSource); | 904 | options.alarmSource = deepClone(this.widget.config.alarmSource); |
904 | - options.alarmSearchStatus = isDefined(this.widget.config.alarmSearchStatus) ? | 905 | + /*options.alarmSearchStatus = isDefined(this.widget.config.alarmSearchStatus) ? |
905 | this.widget.config.alarmSearchStatus : AlarmSearchStatus.ANY; | 906 | this.widget.config.alarmSearchStatus : AlarmSearchStatus.ANY; |
906 | options.alarmsPollingInterval = isDefined(this.widget.config.alarmsPollingInterval) ? | 907 | options.alarmsPollingInterval = isDefined(this.widget.config.alarmsPollingInterval) ? |
907 | this.widget.config.alarmsPollingInterval * 1000 : 5000; | 908 | this.widget.config.alarmsPollingInterval * 1000 : 5000; |
908 | options.alarmsMaxCountLoad = isDefined(this.widget.config.alarmsMaxCountLoad) ? | 909 | options.alarmsMaxCountLoad = isDefined(this.widget.config.alarmsMaxCountLoad) ? |
909 | this.widget.config.alarmsMaxCountLoad : 0; | 910 | this.widget.config.alarmsMaxCountLoad : 0; |
910 | options.alarmsFetchSize = isDefined(this.widget.config.alarmsFetchSize) ? | 911 | options.alarmsFetchSize = isDefined(this.widget.config.alarmsFetchSize) ? |
911 | - this.widget.config.alarmsFetchSize : 100; | 912 | + this.widget.config.alarmsFetchSize : 100;*/ |
912 | } else { | 913 | } else { |
913 | options.datasources = deepClone(this.widget.config.datasources); | 914 | options.datasources = deepClone(this.widget.config.datasources); |
914 | } | 915 | } |
@@ -103,6 +103,10 @@ export interface AlarmInfo extends Alarm { | @@ -103,6 +103,10 @@ export interface AlarmInfo extends Alarm { | ||
103 | originatorName: string; | 103 | originatorName: string; |
104 | } | 104 | } |
105 | 105 | ||
106 | +export interface AlarmDataInfo extends AlarmInfo { | ||
107 | + [key: string]: any; | ||
108 | +} | ||
109 | + | ||
106 | export const simulatedAlarm: AlarmInfo = { | 110 | export const simulatedAlarm: AlarmInfo = { |
107 | id: new AlarmId(NULL_UUID), | 111 | id: new AlarmId(NULL_UUID), |
108 | tenantId: new TenantId(NULL_UUID), | 112 | tenantId: new TenantId(NULL_UUID), |
@@ -20,10 +20,11 @@ import { SortDirection } from '@angular/material/sort'; | @@ -20,10 +20,11 @@ import { SortDirection } from '@angular/material/sort'; | ||
20 | import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; | 20 | import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
21 | import { EntityInfo } from '@shared/models/entity.models'; | 21 | import { EntityInfo } from '@shared/models/entity.models'; |
22 | import { EntityType } from '@shared/models/entity-type.models'; | 22 | import { EntityType } from '@shared/models/entity-type.models'; |
23 | -import { Datasource, DatasourceType } from '@shared/models/widget.models'; | 23 | +import { DataKey, Datasource, DatasourceType } from '@shared/models/widget.models'; |
24 | import { PageData } from '@shared/models/page/page-data'; | 24 | import { PageData } from '@shared/models/page/page-data'; |
25 | import { isDefined, isEqual } from '@core/utils'; | 25 | import { isDefined, isEqual } from '@core/utils'; |
26 | import { TranslateService } from '@ngx-translate/core'; | 26 | import { TranslateService } from '@ngx-translate/core'; |
27 | +import { AlarmInfo, AlarmSearchStatus, AlarmSeverity } from '../alarm.models'; | ||
27 | 28 | ||
28 | export enum EntityKeyType { | 29 | export enum EntityKeyType { |
29 | ATTRIBUTE = 'ATTRIBUTE', | 30 | ATTRIBUTE = 'ATTRIBUTE', |
@@ -31,7 +32,8 @@ export enum EntityKeyType { | @@ -31,7 +32,8 @@ export enum EntityKeyType { | ||
31 | SHARED_ATTRIBUTE = 'SHARED_ATTRIBUTE', | 32 | SHARED_ATTRIBUTE = 'SHARED_ATTRIBUTE', |
32 | SERVER_ATTRIBUTE = 'SERVER_ATTRIBUTE', | 33 | SERVER_ATTRIBUTE = 'SERVER_ATTRIBUTE', |
33 | TIME_SERIES = 'TIME_SERIES', | 34 | TIME_SERIES = 'TIME_SERIES', |
34 | - ENTITY_FIELD = 'ENTITY_FIELD' | 35 | + ENTITY_FIELD = 'ENTITY_FIELD', |
36 | + ALARM_FIELD = 'ENTITY_FIELD' | ||
35 | } | 37 | } |
36 | 38 | ||
37 | export const entityKeyTypeTranslationMap = new Map<EntityKeyType, string>( | 39 | export const entityKeyTypeTranslationMap = new Map<EntityKeyType, string>( |
@@ -53,6 +55,23 @@ export function entityKeyTypeToDataKeyType(entityKeyType: EntityKeyType): DataKe | @@ -53,6 +55,23 @@ export function entityKeyTypeToDataKeyType(entityKeyType: EntityKeyType): DataKe | ||
53 | return DataKeyType.timeseries; | 55 | return DataKeyType.timeseries; |
54 | case EntityKeyType.ENTITY_FIELD: | 56 | case EntityKeyType.ENTITY_FIELD: |
55 | return DataKeyType.entityField; | 57 | return DataKeyType.entityField; |
58 | + case EntityKeyType.ALARM_FIELD: | ||
59 | + return DataKeyType.alarm; | ||
60 | + } | ||
61 | +} | ||
62 | + | ||
63 | +export function dataKeyTypeToEntityKeyType(dataKeyType: DataKeyType): EntityKeyType { | ||
64 | + switch (dataKeyType) { | ||
65 | + case DataKeyType.timeseries: | ||
66 | + return EntityKeyType.TIME_SERIES; | ||
67 | + case DataKeyType.attribute: | ||
68 | + return EntityKeyType.ATTRIBUTE; | ||
69 | + case DataKeyType.function: | ||
70 | + return EntityKeyType.ENTITY_FIELD; | ||
71 | + case DataKeyType.alarm: | ||
72 | + return EntityKeyType.ALARM_FIELD; | ||
73 | + case DataKeyType.entityField: | ||
74 | + return EntityKeyType.ENTITY_FIELD; | ||
56 | } | 75 | } |
57 | } | 76 | } |
58 | 77 | ||
@@ -61,6 +80,14 @@ export interface EntityKey { | @@ -61,6 +80,14 @@ export interface EntityKey { | ||
61 | key: string; | 80 | key: string; |
62 | } | 81 | } |
63 | 82 | ||
83 | +export function dataKeyToEntityKey(dataKey: DataKey): EntityKey { | ||
84 | + const entityKey: EntityKey = { | ||
85 | + key: dataKey.name, | ||
86 | + type: dataKeyTypeToEntityKeyType(dataKey.type) | ||
87 | + }; | ||
88 | + return entityKey; | ||
89 | +} | ||
90 | + | ||
64 | export enum EntityKeyValueType { | 91 | export enum EntityKeyValueType { |
65 | STRING = 'STRING', | 92 | STRING = 'STRING', |
66 | NUMERIC = 'NUMERIC', | 93 | NUMERIC = 'NUMERIC', |
@@ -479,6 +506,16 @@ export interface EntityDataPageLink { | @@ -479,6 +506,16 @@ export interface EntityDataPageLink { | ||
479 | dynamic?: boolean; | 506 | dynamic?: boolean; |
480 | } | 507 | } |
481 | 508 | ||
509 | +export interface AlarmDataPageLink extends EntityDataPageLink { | ||
510 | + startTs?: number; | ||
511 | + endTs?: number; | ||
512 | + timeWindow?: number; | ||
513 | + typeList?: Array<string>; | ||
514 | + statusList?: Array<AlarmSearchStatus>; | ||
515 | + severityList?: Array<AlarmSeverity>; | ||
516 | + searchPropagatedAlarms?: boolean; | ||
517 | +} | ||
518 | + | ||
482 | export function entityDataPageLinkSortDirection(pageLink: EntityDataPageLink): SortDirection { | 519 | export function entityDataPageLinkSortDirection(pageLink: EntityDataPageLink): SortDirection { |
483 | if (pageLink.sortOrder) { | 520 | if (pageLink.sortOrder) { |
484 | return (pageLink.sortOrder.direction + '').toLowerCase() as SortDirection; | 521 | return (pageLink.sortOrder.direction + '').toLowerCase() as SortDirection; |
@@ -508,13 +545,19 @@ export interface EntityCountQuery { | @@ -508,13 +545,19 @@ export interface EntityCountQuery { | ||
508 | entityFilter: EntityFilter; | 545 | entityFilter: EntityFilter; |
509 | } | 546 | } |
510 | 547 | ||
511 | -export interface EntityDataQuery extends EntityCountQuery { | ||
512 | - pageLink: EntityDataPageLink; | 548 | +export interface AbstractDataQuery<T extends EntityDataPageLink> extends EntityCountQuery { |
549 | + pageLink: T; | ||
513 | entityFields?: Array<EntityKey>; | 550 | entityFields?: Array<EntityKey>; |
514 | latestValues?: Array<EntityKey>; | 551 | latestValues?: Array<EntityKey>; |
515 | keyFilters?: Array<KeyFilter>; | 552 | keyFilters?: Array<KeyFilter>; |
516 | } | 553 | } |
517 | 554 | ||
555 | +export interface EntityDataQuery extends AbstractDataQuery<EntityDataPageLink> { | ||
556 | +} | ||
557 | + | ||
558 | +export interface AlarmDataQuery extends AbstractDataQuery<AlarmDataPageLink> { | ||
559 | +} | ||
560 | + | ||
518 | export interface TsValue { | 561 | export interface TsValue { |
519 | ts: number; | 562 | ts: number; |
520 | value: string; | 563 | value: string; |
@@ -526,6 +569,11 @@ export interface EntityData { | @@ -526,6 +569,11 @@ export interface EntityData { | ||
526 | timeseries: {[key: string]: Array<TsValue>}; | 569 | timeseries: {[key: string]: Array<TsValue>}; |
527 | } | 570 | } |
528 | 571 | ||
572 | +export interface AlarmData extends AlarmInfo { | ||
573 | + entityId: string; | ||
574 | + latest: {[entityKeyType: string]: {[key: string]: TsValue}}; | ||
575 | +} | ||
576 | + | ||
529 | export function entityPageDataChanged(prevPageData: PageData<EntityData>, nextPageData: PageData<EntityData>): boolean { | 577 | export function entityPageDataChanged(prevPageData: PageData<EntityData>, nextPageData: PageData<EntityData>): boolean { |
530 | const prevIds = prevPageData.data.map((entityData) => entityData.entityId.id); | 578 | const prevIds = prevPageData.data.map((entityData) => entityData.entityId.id); |
531 | const nextIds = nextPageData.data.map((entityData) => entityData.entityId.id); | 579 | const nextIds = nextPageData.data.map((entityData) => entityData.entityId.id); |
@@ -21,7 +21,7 @@ import { Observable, ReplaySubject, Subject } from 'rxjs'; | @@ -21,7 +21,7 @@ import { Observable, ReplaySubject, Subject } from 'rxjs'; | ||
21 | import { EntityId } from '@shared/models/id/entity-id'; | 21 | import { EntityId } from '@shared/models/id/entity-id'; |
22 | import { map } from 'rxjs/operators'; | 22 | import { map } from 'rxjs/operators'; |
23 | import { NgZone } from '@angular/core'; | 23 | import { NgZone } from '@angular/core'; |
24 | -import { EntityData, EntityDataQuery, EntityKey } from '@shared/models/query/query.models'; | 24 | +import { AlarmData, AlarmDataQuery, EntityData, EntityDataQuery, EntityKey } from '@shared/models/query/query.models'; |
25 | import { PageData } from '@shared/models/page/page-data'; | 25 | import { PageData } from '@shared/models/page/page-data'; |
26 | 26 | ||
27 | export enum DataKeyType { | 27 | export enum DataKeyType { |
@@ -165,16 +165,31 @@ export class EntityDataCmd implements WebsocketCmd { | @@ -165,16 +165,31 @@ export class EntityDataCmd implements WebsocketCmd { | ||
165 | } | 165 | } |
166 | } | 166 | } |
167 | 167 | ||
168 | +export class AlarmDataCmd implements WebsocketCmd { | ||
169 | + cmdId: number; | ||
170 | + query?: AlarmDataQuery; | ||
171 | + | ||
172 | + public isEmpty(): boolean { | ||
173 | + return !this.query; | ||
174 | + } | ||
175 | +} | ||
176 | + | ||
168 | export class EntityDataUnsubscribeCmd implements WebsocketCmd { | 177 | export class EntityDataUnsubscribeCmd implements WebsocketCmd { |
169 | cmdId: number; | 178 | cmdId: number; |
170 | } | 179 | } |
171 | 180 | ||
181 | +export class AlarmDataUnsubscribeCmd implements WebsocketCmd { | ||
182 | + cmdId: number; | ||
183 | +} | ||
184 | + | ||
172 | export class TelemetryPluginCmdsWrapper { | 185 | export class TelemetryPluginCmdsWrapper { |
173 | attrSubCmds: Array<AttributesSubscriptionCmd>; | 186 | attrSubCmds: Array<AttributesSubscriptionCmd>; |
174 | tsSubCmds: Array<TimeseriesSubscriptionCmd>; | 187 | tsSubCmds: Array<TimeseriesSubscriptionCmd>; |
175 | historyCmds: Array<GetHistoryCmd>; | 188 | historyCmds: Array<GetHistoryCmd>; |
176 | entityDataCmds: Array<EntityDataCmd>; | 189 | entityDataCmds: Array<EntityDataCmd>; |
177 | entityDataUnsubscribeCmds: Array<EntityDataUnsubscribeCmd>; | 190 | entityDataUnsubscribeCmds: Array<EntityDataUnsubscribeCmd>; |
191 | + alarmDataCmds: Array<AlarmDataCmd>; | ||
192 | + alarmDataUnsubscribeCmds: Array<AlarmDataUnsubscribeCmd>; | ||
178 | 193 | ||
179 | constructor() { | 194 | constructor() { |
180 | this.attrSubCmds = []; | 195 | this.attrSubCmds = []; |
@@ -182,6 +197,8 @@ export class TelemetryPluginCmdsWrapper { | @@ -182,6 +197,8 @@ export class TelemetryPluginCmdsWrapper { | ||
182 | this.historyCmds = []; | 197 | this.historyCmds = []; |
183 | this.entityDataCmds = []; | 198 | this.entityDataCmds = []; |
184 | this.entityDataUnsubscribeCmds = []; | 199 | this.entityDataUnsubscribeCmds = []; |
200 | + this.alarmDataCmds = []; | ||
201 | + this.alarmDataUnsubscribeCmds = []; | ||
185 | } | 202 | } |
186 | 203 | ||
187 | public hasCommands(): boolean { | 204 | public hasCommands(): boolean { |
@@ -189,7 +206,9 @@ export class TelemetryPluginCmdsWrapper { | @@ -189,7 +206,9 @@ export class TelemetryPluginCmdsWrapper { | ||
189 | this.historyCmds.length > 0 || | 206 | this.historyCmds.length > 0 || |
190 | this.attrSubCmds.length > 0 || | 207 | this.attrSubCmds.length > 0 || |
191 | this.entityDataCmds.length > 0 || | 208 | this.entityDataCmds.length > 0 || |
192 | - this.entityDataUnsubscribeCmds.length > 0; | 209 | + this.entityDataUnsubscribeCmds.length > 0 || |
210 | + this.alarmDataCmds.length > 0 || | ||
211 | + this.alarmDataUnsubscribeCmds.length > 0; | ||
193 | } | 212 | } |
194 | 213 | ||
195 | public clear() { | 214 | public clear() { |
@@ -198,6 +217,8 @@ export class TelemetryPluginCmdsWrapper { | @@ -198,6 +217,8 @@ export class TelemetryPluginCmdsWrapper { | ||
198 | this.historyCmds.length = 0; | 217 | this.historyCmds.length = 0; |
199 | this.entityDataCmds.length = 0; | 218 | this.entityDataCmds.length = 0; |
200 | this.entityDataUnsubscribeCmds.length = 0; | 219 | this.entityDataUnsubscribeCmds.length = 0; |
220 | + this.alarmDataCmds.length = 0; | ||
221 | + this.alarmDataUnsubscribeCmds.length = 0; | ||
201 | } | 222 | } |
202 | 223 | ||
203 | public preparePublishCommands(maxCommands: number): TelemetryPluginCmdsWrapper { | 224 | public preparePublishCommands(maxCommands: number): TelemetryPluginCmdsWrapper { |
@@ -212,6 +233,10 @@ export class TelemetryPluginCmdsWrapper { | @@ -212,6 +233,10 @@ export class TelemetryPluginCmdsWrapper { | ||
212 | preparedWrapper.entityDataCmds = this.popCmds(this.entityDataCmds, leftCount); | 233 | preparedWrapper.entityDataCmds = this.popCmds(this.entityDataCmds, leftCount); |
213 | leftCount -= preparedWrapper.entityDataCmds.length; | 234 | leftCount -= preparedWrapper.entityDataCmds.length; |
214 | preparedWrapper.entityDataUnsubscribeCmds = this.popCmds(this.entityDataUnsubscribeCmds, leftCount); | 235 | preparedWrapper.entityDataUnsubscribeCmds = this.popCmds(this.entityDataUnsubscribeCmds, leftCount); |
236 | + leftCount -= preparedWrapper.entityDataUnsubscribeCmds.length; | ||
237 | + preparedWrapper.alarmDataCmds = this.popCmds(this.alarmDataCmds, leftCount); | ||
238 | + leftCount -= preparedWrapper.alarmDataCmds.length; | ||
239 | + preparedWrapper.alarmDataUnsubscribeCmds = this.popCmds(this.alarmDataUnsubscribeCmds, leftCount); | ||
215 | return preparedWrapper; | 240 | return preparedWrapper; |
216 | } | 241 | } |
217 | 242 | ||
@@ -239,18 +264,38 @@ export interface SubscriptionUpdateMsg extends SubscriptionDataHolder { | @@ -239,18 +264,38 @@ export interface SubscriptionUpdateMsg extends SubscriptionDataHolder { | ||
239 | errorMsg: string; | 264 | errorMsg: string; |
240 | } | 265 | } |
241 | 266 | ||
242 | -export interface EntityDataUpdateMsg { | 267 | +export enum DataUpdateType { |
268 | + ENTITY_DATA = 'ENTITY_DATA', | ||
269 | + ALARM_DATA = 'ALARM_DATA' | ||
270 | +} | ||
271 | + | ||
272 | +export interface DataUpdateMsg<T> { | ||
243 | cmdId: number; | 273 | cmdId: number; |
244 | - data?: PageData<EntityData>; | ||
245 | - update?: Array<EntityData>; | 274 | + data?: PageData<T>; |
275 | + update?: Array<T>; | ||
246 | errorCode: number; | 276 | errorCode: number; |
247 | errorMsg: string; | 277 | errorMsg: string; |
278 | + dataUpdateType: DataUpdateType; | ||
279 | +} | ||
280 | + | ||
281 | +export interface EntityDataUpdateMsg extends DataUpdateMsg<EntityData> { | ||
282 | + dataUpdateType: DataUpdateType.ENTITY_DATA; | ||
248 | } | 283 | } |
249 | 284 | ||
250 | -export type WebsocketDataMsg = EntityDataUpdateMsg | SubscriptionUpdateMsg; | 285 | +export interface AlarmDataUpdateMsg extends DataUpdateMsg<AlarmData> { |
286 | + dataUpdateType: DataUpdateType.ALARM_DATA; | ||
287 | +} | ||
288 | + | ||
289 | +export type WebsocketDataMsg = AlarmDataUpdateMsg | EntityDataUpdateMsg | SubscriptionUpdateMsg; | ||
251 | 290 | ||
252 | export function isEntityDataUpdateMsg(message: WebsocketDataMsg): message is EntityDataUpdateMsg { | 291 | export function isEntityDataUpdateMsg(message: WebsocketDataMsg): message is EntityDataUpdateMsg { |
253 | - return (message as EntityDataUpdateMsg).cmdId !== undefined; | 292 | + const updateMsg = (message as DataUpdateMsg<any>); |
293 | + return updateMsg.cmdId !== undefined && updateMsg.dataUpdateType === DataUpdateType.ENTITY_DATA; | ||
294 | +} | ||
295 | + | ||
296 | +export function isAlarmDataUpdateMsg(message: WebsocketDataMsg): message is AlarmDataUpdateMsg { | ||
297 | + const updateMsg = (message as DataUpdateMsg<any>); | ||
298 | + return updateMsg.cmdId !== undefined && updateMsg.dataUpdateType === DataUpdateType.ALARM_DATA; | ||
254 | } | 299 | } |
255 | 300 | ||
256 | export class SubscriptionUpdate implements SubscriptionUpdateMsg { | 301 | export class SubscriptionUpdate implements SubscriptionUpdateMsg { |
@@ -302,19 +347,33 @@ export class SubscriptionUpdate implements SubscriptionUpdateMsg { | @@ -302,19 +347,33 @@ export class SubscriptionUpdate implements SubscriptionUpdateMsg { | ||
302 | } | 347 | } |
303 | } | 348 | } |
304 | 349 | ||
305 | -export class EntityDataUpdate implements EntityDataUpdateMsg { | 350 | +export class DataUpdate<T> implements DataUpdateMsg<T> { |
306 | cmdId: number; | 351 | cmdId: number; |
307 | errorCode: number; | 352 | errorCode: number; |
308 | errorMsg: string; | 353 | errorMsg: string; |
309 | - data?: PageData<EntityData>; | ||
310 | - update?: Array<EntityData>; | 354 | + data?: PageData<T>; |
355 | + update?: Array<T>; | ||
356 | + dataUpdateType: DataUpdateType; | ||
311 | 357 | ||
312 | - constructor(msg: EntityDataUpdateMsg) { | 358 | + constructor(msg: DataUpdateMsg<T>) { |
313 | this.cmdId = msg.cmdId; | 359 | this.cmdId = msg.cmdId; |
314 | this.errorCode = msg.errorCode; | 360 | this.errorCode = msg.errorCode; |
315 | this.errorMsg = msg.errorMsg; | 361 | this.errorMsg = msg.errorMsg; |
316 | this.data = msg.data; | 362 | this.data = msg.data; |
317 | this.update = msg.update; | 363 | this.update = msg.update; |
364 | + this.dataUpdateType = msg.dataUpdateType; | ||
365 | + } | ||
366 | +} | ||
367 | + | ||
368 | +export class EntityDataUpdate extends DataUpdate<EntityData> { | ||
369 | + constructor(msg: EntityDataUpdateMsg) { | ||
370 | + super(msg); | ||
371 | + } | ||
372 | +} | ||
373 | + | ||
374 | +export class AlarmDataUpdate extends DataUpdate<AlarmData> { | ||
375 | + constructor(msg: AlarmDataUpdateMsg) { | ||
376 | + super(msg); | ||
318 | } | 377 | } |
319 | } | 378 | } |
320 | 379 | ||
@@ -328,6 +387,7 @@ export class TelemetrySubscriber { | @@ -328,6 +387,7 @@ export class TelemetrySubscriber { | ||
328 | 387 | ||
329 | private dataSubject = new ReplaySubject<SubscriptionUpdate>(1); | 388 | private dataSubject = new ReplaySubject<SubscriptionUpdate>(1); |
330 | private entityDataSubject = new ReplaySubject<EntityDataUpdate>(1); | 389 | private entityDataSubject = new ReplaySubject<EntityDataUpdate>(1); |
390 | + private alarmDataSubject = new ReplaySubject<AlarmDataUpdate>(1); | ||
331 | private reconnectSubject = new Subject(); | 391 | private reconnectSubject = new Subject(); |
332 | 392 | ||
333 | private zone: NgZone; | 393 | private zone: NgZone; |
@@ -336,6 +396,7 @@ export class TelemetrySubscriber { | @@ -336,6 +396,7 @@ export class TelemetrySubscriber { | ||
336 | 396 | ||
337 | public data$ = this.dataSubject.asObservable(); | 397 | public data$ = this.dataSubject.asObservable(); |
338 | public entityData$ = this.entityDataSubject.asObservable(); | 398 | public entityData$ = this.entityDataSubject.asObservable(); |
399 | + public alarmData$ = this.alarmDataSubject.asObservable(); | ||
339 | public reconnect$ = this.reconnectSubject.asObservable(); | 400 | public reconnect$ = this.reconnectSubject.asObservable(); |
340 | 401 | ||
341 | public static createEntityAttributesSubscription(telemetryService: TelemetryService, | 402 | public static createEntityAttributesSubscription(telemetryService: TelemetryService, |
@@ -379,6 +440,7 @@ export class TelemetrySubscriber { | @@ -379,6 +440,7 @@ export class TelemetrySubscriber { | ||
379 | public complete() { | 440 | public complete() { |
380 | this.dataSubject.complete(); | 441 | this.dataSubject.complete(); |
381 | this.entityDataSubject.complete(); | 442 | this.entityDataSubject.complete(); |
443 | + this.alarmDataSubject.complete(); | ||
382 | this.reconnectSubject.complete(); | 444 | this.reconnectSubject.complete(); |
383 | } | 445 | } |
384 | 446 | ||
@@ -416,6 +478,18 @@ export class TelemetrySubscriber { | @@ -416,6 +478,18 @@ export class TelemetrySubscriber { | ||
416 | } | 478 | } |
417 | } | 479 | } |
418 | 480 | ||
481 | + public onAlarmData(message: AlarmDataUpdate) { | ||
482 | + if (this.zone) { | ||
483 | + this.zone.run( | ||
484 | + () => { | ||
485 | + this.alarmDataSubject.next(message); | ||
486 | + } | ||
487 | + ); | ||
488 | + } else { | ||
489 | + this.alarmDataSubject.next(message); | ||
490 | + } | ||
491 | + } | ||
492 | + | ||
419 | public onReconnected() { | 493 | public onReconnected() { |
420 | this.reconnectSubject.next(); | 494 | this.reconnectSubject.next(); |
421 | } | 495 | } |