diff --git a/application/src/main/data/json/system/widget_bundles/alarm_widgets.json b/application/src/main/data/json/system/widget_bundles/alarm_widgets.json index 123e8b748c..697ce4bbae 100644 --- a/application/src/main/data/json/system/widget_bundles/alarm_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/alarm_widgets.json @@ -16,10 +16,10 @@ "templateHtml": "\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.alarmsTableWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"AlarmTableSettings\",\n \"properties\": {\n \"alarmsTitle\": {\n \"title\": \"Alarms table title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"enableSelection\": {\n \"title\": \"Enable alarms selection\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSearch\": {\n \"title\": \"Enable alarms search\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSelectColumnDisplay\": {\n \"title\": \"Enable select columns to display\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStatusFilter\": {\n \"title\": \"Enable alarm status filter\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyAction\": {\n \"title\": \"Always display actions column\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"displayDetails\": {\n \"title\": \"Display alarm details\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowAcknowledgment\": {\n \"title\": \"Allow alarms acknowledgment\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowClear\": {\n \"title\": \"Allow alarms clear\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultSortOrder\": {\n \"title\": \"Default sort order\",\n \"type\": \"string\",\n \"default\": \"-createdTime\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"alarmsTitle\",\n \"enableSelection\",\n \"enableSearch\",\n \"enableSelectColumnDisplay\",\n \"enableStatusFilter\",\n \"enableStickyAction\",\n \"displayDetails\",\n \"allowAcknowledgment\",\n \"allowClear\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"defaultSortOrder\"\n ]\n}", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"AlarmTableSettings\",\n \"properties\": {\n \"alarmsTitle\": {\n \"title\": \"Alarms table title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"enableSelection\": {\n \"title\": \"Enable alarms selection\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSearch\": {\n \"title\": \"Enable alarms search\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSelectColumnDisplay\": {\n \"title\": \"Enable select columns to display\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableFilter\": {\n \"title\": \"Enable alarm filter\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyAction\": {\n \"title\": \"Always display actions column\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"displayDetails\": {\n \"title\": \"Display alarm details\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowAcknowledgment\": {\n \"title\": \"Allow alarms acknowledgment\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowClear\": {\n \"title\": \"Allow alarms clear\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultSortOrder\": {\n \"title\": \"Default sort order\",\n \"type\": \"string\",\n \"default\": \"-createdTime\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"alarmsTitle\",\n \"enableSelection\",\n \"enableSearch\",\n \"enableSelectColumnDisplay\",\n \"enableFilter\",\n \"enableStickyAction\",\n \"displayDetails\",\n \"allowAcknowledgment\",\n \"allowClear\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"defaultSortOrder\"\n ]\n}", "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"columnWidth\": {\n \"title\": \"Column width (px or %)\",\n \"type\": \"string\",\n \"default\": \"0px\"\n },\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, alarm, filter)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"columnWidth\",\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}", - "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStatusFilter\":true,\"enableStickyAction\":false},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{}}" + "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false}" } } ] -} +} \ No newline at end of file diff --git a/ui-ngx/src/app/core/api/alias-controller.ts b/ui-ngx/src/app/core/api/alias-controller.ts index ca29cceb0d..c38187afd3 100644 --- a/ui-ngx/src/app/core/api/alias-controller.ts +++ b/ui-ngx/src/app/core/api/alias-controller.ts @@ -293,7 +293,7 @@ export class AliasController implements IAliasController { } resolveAlarmSource(alarmSource: Datasource): Observable { - return this.resolveDatasource(alarmSource, true).pipe( + return this.resolveDatasource(alarmSource).pipe( map((datasource) => { if (datasource.type === DatasourceType.function) { let name: string; diff --git a/ui-ngx/src/app/core/api/widget-api.models.ts b/ui-ngx/src/app/core/api/widget-api.models.ts index f6bb4067ed..fe7bb82044 100644 --- a/ui-ngx/src/app/core/api/widget-api.models.ts +++ b/ui-ngx/src/app/core/api/widget-api.models.ts @@ -29,11 +29,9 @@ import { } from '@shared/models/widget.models'; import { TimeService } from '../services/time.service'; import { DeviceService } from '../http/device.service'; -import { AlarmService } from '../http/alarm.service'; import { UtilsService } from '@core/services/utils.service'; import { Timewindow, WidgetTimewindow } from '@shared/models/time/time.models'; import { EntityType } from '@shared/models/entity-type.models'; -import { AlarmInfo, AlarmSearchStatus } from '@shared/models/alarm.models'; import { HttpErrorResponse } from '@angular/common/http'; import { RafService } from '@core/services/raf.service'; import { EntityAliases } from '@shared/models/alias.models'; @@ -41,11 +39,13 @@ import { EntityInfo } from '@app/shared/models/entity.models'; import { IDashboardComponent } from '@home/models/dashboard-component.models'; import * as moment_ from 'moment'; import { - AlarmData, AlarmDataPageLink, + AlarmData, + AlarmDataPageLink, EntityData, EntityDataPageLink, EntityFilter, - Filter, FilterInfo, + Filter, + FilterInfo, Filters, KeyFilter } from '@shared/models/query/query.models'; @@ -220,10 +220,6 @@ export interface WidgetSubscriptionOptions { type?: widgetType; stateData?: boolean; alarmSource?: Datasource; -/* alarmSearchStatus?: AlarmSearchStatus; - alarmsPollingInterval?: number; - alarmsMaxCountLoad?: number; - alarmsFetchSize?: number; */ datasources?: Array; hasDataPageLink?: boolean; singleEntity?: boolean; @@ -273,8 +269,6 @@ export interface IWidgetSubscription { alarms?: PageData; alarmSource?: Datasource; - /* alarmSearchStatus?: AlarmSearchStatus; - alarmsPollingInterval?: number; */ targetDeviceAliasIds?: Array; targetDeviceIds?: Array; diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index 5b2a2d983b..51c0a0ee42 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -104,30 +104,10 @@ export class WidgetSubscription implements IWidgetSubscription { comparisonTimeWindow: WidgetTimewindow; timewindowForComparison: SubscriptionTimewindow; - // alarms: Array; alarms: PageData; alarmSource: Datasource; - - /* private alarmSearchStatusValue: AlarmSearchStatus; - - set alarmSearchStatus(value: AlarmSearchStatus) { - if (this.alarmSearchStatusValue !== value) { - this.alarmSearchStatusValue = value; - this.onAlarmSearchStatusChanged(); - } - } - - get alarmSearchStatus(): AlarmSearchStatus { - return this.alarmSearchStatusValue; - }*/ - alarmDataListener: AlarmDataListener; -/* alarmsPollingInterval: number; - alarmsMaxCountLoad: number; - alarmsFetchSize: number; - alarmSourceListener: AlarmSourceListener;*/ - loadingData: boolean; targetDeviceAliasIds?: Array; @@ -186,17 +166,7 @@ export class WidgetSubscription implements IWidgetSubscription { this.callbacks.dataLoading = this.callbacks.dataLoading || (() => {}); this.callbacks.timeWindowUpdated = this.callbacks.timeWindowUpdated || (() => {}); this.alarmSource = options.alarmSource; - /*this.alarmSearchStatusValue = isDefined(options.alarmSearchStatus) ? - options.alarmSearchStatus : AlarmSearchStatus.ANY; - this.alarmsPollingInterval = isDefined(options.alarmsPollingInterval) ? - options.alarmsPollingInterval : 5000; - this.alarmsMaxCountLoad = isDefined(options.alarmsMaxCountLoad) ? - options.alarmsMaxCountLoad : 0; - this.alarmsFetchSize = isDefined(options.alarmsFetchSize) ? - options.alarmsFetchSize : 100; - this.alarmSourceListener = null;*/ this.alarmDataListener = null; - // this.alarms = []; this.alarms = emptyPageData(); this.originalTimewindow = null; this.timeWindow = {}; @@ -834,9 +804,6 @@ export class WidgetSubscription implements IWidgetSubscription { } if (this.timeWindowConfig) { this.updateRealtimeSubscription(); - if (this.subscriptionTimewindow.fixedWindow) { - this.onDataUpdated(); - } } this.alarmDataListener = { subscriptionTimewindow: this.subscriptionTimewindow, @@ -850,9 +817,7 @@ export class WidgetSubscription implements IWidgetSubscription { this.ctx.alarmDataService.subscribeForAlarms(this.alarmDataListener, pageLink, keyFilters); let forceUpdate = false; - if (this.alarmSource.unresolvedStateEntity || - (this.alarmSource.type === DatasourceType.entity && !this.alarmSource.entityId) - ) { + if (this.alarmSource.unresolvedStateEntity) { forceUpdate = true; } if (forceUpdate) { @@ -892,41 +857,6 @@ export class WidgetSubscription implements IWidgetSubscription { } } - /* private alarmsSubscribe() { - this.notifyDataLoading(); - if (this.timeWindowConfig) { - this.updateRealtimeSubscription(); - if (this.subscriptionTimewindow.fixedWindow) { - this.onDataUpdated(); - } - } - this.alarmSourceListener = { - subscriptionTimewindow: this.subscriptionTimewindow, - alarmSource: this.alarmSource, - alarmSearchStatus: this.alarmSearchStatus, - alarmsPollingInterval: this.alarmsPollingInterval, - alarmsMaxCountLoad: this.alarmsMaxCountLoad, - alarmsFetchSize: this.alarmsFetchSize, - alarmsUpdated: alarms => this.alarmsUpdated(alarms) - }; - - this.alarms = emptyPageData(); - - this.ctx.alarmDataService.subscribeForAlarms(this.alarmDataListener); - - let forceUpdate = false; - if (this.alarmSource.unresolvedStateEntity || - (this.alarmSource.type === DatasourceType.entity && !this.alarmSource.entityId) - ) { - forceUpdate = true; - } - if (forceUpdate) { - this.notifyDataLoaded(); - this.onDataUpdated(); - } - } */ - - unsubscribe() { if (this.type !== widgetType.rpc) { if (this.type === widgetType.alarm) { diff --git a/ui-ngx/src/app/core/http/alarm.service.ts b/ui-ngx/src/app/core/http/alarm.service.ts index 57a5a3af00..211fc413e9 100644 --- a/ui-ngx/src/app/core/http/alarm.service.ts +++ b/ui-ngx/src/app/core/http/alarm.service.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; -import { EMPTY, Observable } from 'rxjs'; +import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { PageData } from '@shared/models/page/page-data'; import { EntityId } from '@shared/models/id/entity-id'; @@ -26,55 +26,15 @@ import { AlarmQuery, AlarmSearchStatus, AlarmSeverity, - AlarmStatus, - simulatedAlarm + AlarmStatus } from '@shared/models/alarm.models'; -import { EntityType } from '@shared/models/entity-type.models'; -import { Datasource, DatasourceType } from '@shared/models/widget.models'; -import { SubscriptionTimewindow } from '@shared/models/time/time.models'; import { UtilsService } from '@core/services/utils.service'; -import { TimePageLink } from '@shared/models/page/page-link'; -import { Direction, SortOrder } from '@shared/models/page/sort-order'; -import { concatMap, expand, map, toArray } from 'rxjs/operators'; -import { isDefined } from '@core/utils'; -import Timeout = NodeJS.Timeout; - -interface AlarmSourceListenerQuery { - entityType: EntityType; - entityId: string; - alarmSearchStatus: AlarmSearchStatus; - alarmStatus: AlarmStatus; - alarmsMaxCountLoad: number; - alarmsFetchSize: number; - fetchOriginator?: boolean; - limit?: number; - interval?: number; - startTime?: number; - endTime?: number; - onAlarms?: (alarms: Array) => void; -} - -export interface AlarmSourceListener { - id?: string; - subscriptionTimewindow: SubscriptionTimewindow; - alarmSource: Datasource; - alarmsPollingInterval: number; - alarmSearchStatus: AlarmSearchStatus; - alarmsMaxCountLoad: number; - alarmsFetchSize: number; - alarmsUpdated: (alarms: Array) => void; - lastUpdateTs?: number; - alarmsQuery?: AlarmSourceListenerQuery; - pollTimer?: Timeout; -} @Injectable({ providedIn: 'root' }) export class AlarmService { - private alarmSourceListeners: {[id: string]: AlarmSourceListener} = {}; - constructor( private http: HttpClient, private utils: UtilsService @@ -122,128 +82,4 @@ export class AlarmService { defaultHttpOptionsFromConfig(config)); } - public subscribeForAlarms(alarmSourceListener: AlarmSourceListener): void { - alarmSourceListener.id = this.utils.guid(); - this.alarmSourceListeners[alarmSourceListener.id] = alarmSourceListener; - const alarmSource = alarmSourceListener.alarmSource; - if (alarmSource.type === DatasourceType.function) { - setTimeout(() => { - alarmSourceListener.alarmsUpdated([simulatedAlarm]); - }, 0); - } else if (alarmSource.entityType && alarmSource.entityId) { - const pollingInterval = alarmSourceListener.alarmsPollingInterval; - alarmSourceListener.alarmsQuery = { - entityType: alarmSource.entityType, - entityId: alarmSource.entityId, - alarmSearchStatus: alarmSourceListener.alarmSearchStatus, - alarmStatus: null, - alarmsMaxCountLoad: alarmSourceListener.alarmsMaxCountLoad, - alarmsFetchSize: alarmSourceListener.alarmsFetchSize - }; - const originatorKeys = alarmSource.dataKeys.filter(dataKey => dataKey.name.toLocaleLowerCase().includes('originator')); - if (originatorKeys.length) { - alarmSourceListener.alarmsQuery.fetchOriginator = true; - } - const subscriptionTimewindow = alarmSourceListener.subscriptionTimewindow; - if (subscriptionTimewindow.realtimeWindowMs) { - alarmSourceListener.alarmsQuery.startTime = subscriptionTimewindow.startTs; - } else { - alarmSourceListener.alarmsQuery.startTime = subscriptionTimewindow.fixedWindow.startTimeMs; - alarmSourceListener.alarmsQuery.endTime = subscriptionTimewindow.fixedWindow.endTimeMs; - } - alarmSourceListener.alarmsQuery.onAlarms = (alarms) => { - if (subscriptionTimewindow.realtimeWindowMs) { - const now = Date.now(); - if (alarmSourceListener.lastUpdateTs) { - const interval = now - alarmSourceListener.lastUpdateTs; - alarmSourceListener.alarmsQuery.startTime += interval; - } - alarmSourceListener.lastUpdateTs = now; - } - alarmSourceListener.alarmsUpdated(alarms); - }; - this.onPollAlarms(alarmSourceListener.alarmsQuery); - alarmSourceListener.pollTimer = setInterval(this.onPollAlarms.bind(this), pollingInterval, alarmSourceListener.alarmsQuery); - } - } - - public unsubscribeFromAlarms(alarmSourceListener: AlarmSourceListener): void { - if (alarmSourceListener && alarmSourceListener.id) { - if (alarmSourceListener.pollTimer) { - clearInterval(alarmSourceListener.pollTimer); - alarmSourceListener.pollTimer = null; - } - delete this.alarmSourceListeners[alarmSourceListener.id]; - } - } - - private onPollAlarms(alarmsQuery: AlarmSourceListenerQuery): void { - this.getAlarmsByAlarmSourceQuery(alarmsQuery).subscribe((alarms) => { - alarmsQuery.onAlarms(alarms); - }); - } - - private getAlarmsByAlarmSourceQuery(alarmsQuery: AlarmSourceListenerQuery): Observable> { - const time = Date.now(); - let pageLink: TimePageLink; - const sortOrder: SortOrder = {property: 'createdTime', direction: Direction.DESC}; - if (alarmsQuery.limit) { - pageLink = new TimePageLink(alarmsQuery.limit, 0, - null, - sortOrder); - } else if (alarmsQuery.interval) { - pageLink = new TimePageLink(alarmsQuery.alarmsFetchSize || 100, 0, - null, - sortOrder, time - alarmsQuery.interval); - } else if (alarmsQuery.startTime) { - pageLink = new TimePageLink(alarmsQuery.alarmsFetchSize || 100, 0, - null, - sortOrder, Math.round(alarmsQuery.startTime)); - if (alarmsQuery.endTime) { - pageLink.endTime = Math.round(alarmsQuery.endTime); - } - } - let leftToLoad; - if (isDefined(alarmsQuery.alarmsMaxCountLoad) && alarmsQuery.alarmsMaxCountLoad !== 0) { - leftToLoad = alarmsQuery.alarmsMaxCountLoad; - if (leftToLoad < pageLink.pageSize) { - pageLink.pageSize = leftToLoad; - } - } - return this.fetchAlarms(alarmsQuery, pageLink, leftToLoad); - } - - private fetchAlarms(query: AlarmSourceListenerQuery, - pageLink: TimePageLink, leftToLoad?: number): Observable> { - const alarmQuery = new AlarmQuery( - {id: query.entityId, entityType: query.entityType}, - pageLink, - query.alarmSearchStatus, - query.alarmStatus, - query.fetchOriginator, - null); - return this.getAlarms(alarmQuery, {ignoreLoading: true, ignoreErrors: true}).pipe( - expand((data) => { - let continueLoad = data.hasNext && !query.limit; - if (continueLoad && isDefined(leftToLoad)) { - leftToLoad -= data.data.length; - if (leftToLoad === 0) { - continueLoad = false; - } else if (leftToLoad < alarmQuery.pageLink.pageSize) { - alarmQuery.pageLink.pageSize = leftToLoad; - } - } - if (continueLoad) { - alarmQuery.offset = data.data[data.data.length-1].id.id; - return this.getAlarms(alarmQuery, {ignoreLoading: true}); - } else { - return EMPTY; - } - }), - map((data) => data.data), - concatMap((data) => data), - toArray(), - map((data) => data.sort((a, b) => alarmQuery.pageLink.sort(a, b))), - ); - } } diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index 7e3c160344..1e8c54b7ae 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -61,9 +61,10 @@ import { EntityKey, EntityKeyType, FilterPredicateType, - singleEntityDataPageLink, StringFilterPredicate, + singleEntityDataPageLink, StringOperation } from '@shared/models/query/query.models'; +import { alarmFields } from '@shared/models/alarm.models'; @Injectable({ providedIn: 'root' @@ -622,10 +623,18 @@ export class EntityService { return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys; } + private getAlarmKeys(searchText: string): Array { + const alarmKeys: string[] = Object.keys(alarmFields); + const query = searchText.toLowerCase(); + return query ? alarmKeys.filter((alarmField) => alarmField.toLowerCase().indexOf(query) === 0) : alarmKeys; + } + public getEntityKeys(entityId: EntityId, query: string, type: DataKeyType, config?: RequestConfig): Observable> { if (type === DataKeyType.entityField) { return of(this.getEntityFieldKeys(entityId.entityType as EntityType, query)); + } else if (type === DataKeyType.alarm) { + return of(this.getAlarmKeys(query)); } let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/keys/`; if (type === DataKeyType.timeseries) { diff --git a/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html index f05c97f3d5..d09b7aae24 100644 --- a/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html @@ -32,6 +32,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.ts index 5e020a420a..c05f226fac 100644 --- a/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.ts @@ -30,6 +30,7 @@ export interface DataKeyConfigDialogData { dataKey: DataKey; dataKeySettingsSchema: any; entityAliasId?: string; + showPostProcessing?: boolean; callbacks?: DataKeysCallbacks; } diff --git a/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html b/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html index 4252c57679..a0a1850cff 100644 --- a/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html @@ -72,7 +72,7 @@ formControlName="funcBody"> -
+
{{ 'datakey.use-data-post-processing-func' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts index 738c2fee9c..7696b18c76 100644 --- a/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts @@ -71,6 +71,9 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con @Input() dataKeySettingsSchema: any; + @Input() + showPostProcessing = true; + @ViewChild('keyInput') keyInput: ElementRef; @ViewChild('funcBodyEdit') funcBodyEdit: JsFuncComponent; diff --git a/ui-ngx/src/app/modules/home/components/widget/data-keys.component.html b/ui-ngx/src/app/modules/home/components/widget/data-keys.component.html index 8b15c59196..50e5d5b6ac 100644 --- a/ui-ngx/src/app/modules/home/components/widget/data-keys.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/data-keys.component.html @@ -31,7 +31,12 @@
- + + + notifications + @@ -86,7 +91,12 @@ [displayWith]="displayKeyFn"> - + + + notifications + @@ -118,11 +128,17 @@ {{ translate.get('entity.no-key-matching', {key: truncate.transform(searchText, true, 6, '...')}) | async }} - + entity.create-new-key {{'entity.create-new-key' | translate }} + + notifications + diff --git a/ui-ngx/src/app/modules/home/components/widget/data-keys.component.ts b/ui-ngx/src/app/modules/home/components/widget/data-keys.component.ts index a08aeac886..6d57c88dd6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/data-keys.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/data-keys.component.ts @@ -16,26 +16,26 @@ import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; import { - AfterViewInit, - Component, - ElementRef, - forwardRef, - Input, - OnChanges, - OnInit, - SimpleChanges, - SkipSelf, - ViewChild + AfterViewInit, + Component, + ElementRef, + forwardRef, + Input, + OnChanges, + OnInit, + SimpleChanges, + SkipSelf, + ViewChild } from '@angular/core'; import { - ControlValueAccessor, - FormBuilder, - FormControl, - FormGroup, - FormGroupDirective, - NG_VALUE_ACCESSOR, - NgForm, - Validators + ControlValueAccessor, + FormBuilder, + FormControl, + FormGroup, + FormGroupDirective, + NG_VALUE_ACCESSOR, + NgForm, + Validators } from '@angular/forms'; import { Observable, of } from 'rxjs'; import { filter, map, mergeMap, share, tap } from 'rxjs/operators'; @@ -56,8 +56,8 @@ import { TruncatePipe } from '@shared/pipe/truncate.pipe'; import { DialogService } from '@core/services/dialog.service'; import { MatDialog } from '@angular/material/dialog'; import { - DataKeyConfigDialogComponent, - DataKeyConfigDialogData + DataKeyConfigDialogComponent, + DataKeyConfigDialogData } from '@home/components/widget/data-key-config-dialog.component'; import { deepClone } from '@core/utils'; import { MatChipDropEvent } from '@app/shared/components/mat-chip-draggable.directive'; @@ -218,11 +218,6 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie } private updateParams() { - if (this.widgetType === widgetType.alarm) { - this.dataKeyType = DataKeyType.alarm; - this.placeholder = this.translate.instant('datakey.alarm'); - this.requiredText = this.translate.instant('datakey.alarm-fields-required'); - } else { if (this.datasourceType === DatasourceType.function) { this.dataKeyType = DataKeyType.function; this.placeholder = this.translate.instant('datakey.function-types'); @@ -231,13 +226,15 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie if (this.widgetType === widgetType.latest) { this.dataKeyType = null; this.requiredText = this.translate.instant('datakey.timeseries-or-attributes-required'); + } else if (this.widgetType === widgetType.alarm) { + this.dataKeyType = null; + this.requiredText = this.translate.instant('datakey.alarm-fields-timeseries-or-attributes-required'); } else { this.dataKeyType = DataKeyType.timeseries; this.requiredText = this.translate.instant('datakey.timeseries-required'); } this.placeholder = ''; } - } } private reset() { @@ -387,6 +384,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie dataKey: deepClone(key), dataKeySettingsSchema: this.datakeySettingsSchema, entityAliasId: this.entityAliasId, + showPostProcessing: this.widgetType !== widgetType.alarm, callbacks: this.callbacks } }).afterClosed().subscribe((updatedDataKey) => { @@ -411,16 +409,19 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie if (this.latestSearchTextResult === null || this.searchText !== searchText) { this.searchText = searchText; let fetchObservable: Observable> = null; - if (this.datasourceType === DatasourceType.function || this.widgetType === widgetType.alarm) { + if (this.datasourceType === DatasourceType.function) { const dataKeyFilter = this.createDataKeyFilter(this.searchText); const targetKeysList = this.widgetType === widgetType.alarm ? this.alarmKeys : this.functionTypeKeys; fetchObservable = of(targetKeysList.filter(dataKeyFilter)); } else { if (this.entityAliasId) { const dataKeyTypes = [DataKeyType.timeseries]; - if (this.widgetType === widgetType.latest) { + if (this.widgetType === widgetType.latest || this.widgetType === widgetType.alarm) { dataKeyTypes.push(DataKeyType.attribute); dataKeyTypes.push(DataKeyType.entityField); + if (this.widgetType === widgetType.alarm) { + dataKeyTypes.push(DataKeyType.alarm); + } } fetchObservable = this.callbacks.fetchEntityKeys(this.entityAliasId, this.searchText, dataKeyTypes); } else { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.html new file mode 100644 index 0000000000..b150110fca --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.html @@ -0,0 +1,66 @@ + +
+ + alarm.alarm-status-list + + + {{ alarmSearchStatusTranslationMap.get(searchStatus) | translate }} + + + + + alarm.alarm-severity-list + + + {{ alarmSeverityTranslationMap.get(alarmSeverityEnum[alarmSeverity]) | translate }} + + + + + alarm.alarm-type-list + + + {{type}} + cancel + + + + +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.scss similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.scss diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.ts new file mode 100644 index 0000000000..6304c9fa0e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.ts @@ -0,0 +1,119 @@ +/// +/// Copyright © 2016-2020 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Inject, InjectionToken } from '@angular/core'; +import { + AlarmSearchStatus, + alarmSearchStatusTranslations, + AlarmSeverity, + alarmSeverityTranslations +} from '@shared/models/alarm.models'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { MatChipInputEvent } from '@angular/material/chips'; +import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; +import { OverlayRef } from '@angular/cdk/overlay'; + +export const ALARM_FILTER_PANEL_DATA = new InjectionToken('AlarmFilterPanelData'); + +export interface AlarmFilterPanelData { + statusList: AlarmSearchStatus[]; + severityList: AlarmSeverity[]; + typeList: string[]; +} + +@Component({ + selector: 'tb-alarm-filter-panel', + templateUrl: './alarm-filter-panel.component.html', + styleUrls: ['./alarm-filter-panel.component.scss'] +}) +export class AlarmFilterPanelComponent { + + readonly separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON]; + + alarmFilterFormGroup: FormGroup; + + result: AlarmFilterPanelData; + + alarmSearchStatuses = [AlarmSearchStatus.ACTIVE, + AlarmSearchStatus.CLEARED, + AlarmSearchStatus.ACK, + AlarmSearchStatus.UNACK]; + + alarmSearchStatusTranslationMap = alarmSearchStatusTranslations; + + alarmSeverities = Object.keys(AlarmSeverity); + alarmSeverityEnum = AlarmSeverity; + + alarmSeverityTranslationMap = alarmSeverityTranslations; + + constructor(@Inject(ALARM_FILTER_PANEL_DATA) + public data: AlarmFilterPanelData, + public overlayRef: OverlayRef, + private fb: FormBuilder) { + this.alarmFilterFormGroup = this.fb.group( + { + alarmStatusList: [this.data.statusList], + alarmSeverityList: [this.data.severityList], + alarmTypeList: [this.data.typeList] + } + ); + } + + public alarmTypeList(): string[] { + return this.alarmFilterFormGroup.get('alarmTypeList').value; + } + + public removeAlarmType(type: string): void { + const types: string[] = this.alarmFilterFormGroup.get('alarmTypeList').value; + const index = types.indexOf(type); + if (index >= 0) { + types.splice(index, 1); + this.alarmFilterFormGroup.get('alarmTypeList').setValue(types); + this.alarmFilterFormGroup.get('alarmTypeList').markAsDirty(); + } + } + + public addAlarmType(event: MatChipInputEvent): void { + const input = event.input; + const value = event.value; + + const types: string[] = this.alarmFilterFormGroup.get('alarmTypeList').value; + + if ((value || '').trim()) { + types.push(value.trim()); + this.alarmFilterFormGroup.get('alarmTypeList').setValue(types); + this.alarmFilterFormGroup.get('alarmTypeList').markAsDirty(); + } + + if (input) { + input.value = ''; + } + } + + update() { + this.result = { + statusList: this.alarmFilterFormGroup.get('alarmStatusList').value, + severityList: this.alarmFilterFormGroup.get('alarmSeverityList').value, + typeList: this.alarmFilterFormGroup.get('alarmTypeList').value + }; + this.overlayRef.dispose(); + } + + cancel() { + this.overlayRef.dispose(); + } +} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.html deleted file mode 100644 index e04ecd0fa5..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.html +++ /dev/null @@ -1,25 +0,0 @@ - -
- - - - {{ alarmSearchStatusTranslationMap.get(alarmSearchStatusEnum[searchStatus]) | translate }} - - -
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.ts deleted file mode 100644 index e9ca1116fe..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.ts +++ /dev/null @@ -1,43 +0,0 @@ -/// -/// Copyright © 2016-2020 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { Component, Inject, InjectionToken } from '@angular/core'; -import { IWidgetSubscription } from '@core/api/widget-api.models'; -import { AlarmSearchStatus, alarmSearchStatusTranslations } from '@shared/models/alarm.models'; - -export const ALARM_STATUS_FILTER_PANEL_DATA = new InjectionToken('AlarmStatusFilterPanelData'); - -export interface AlarmStatusFilterPanelData { - subscription: IWidgetSubscription; -} - -@Component({ - selector: 'tb-alarm-status-filter-panel', - templateUrl: './alarm-status-filter-panel.component.html', - styleUrls: ['./alarm-status-filter-panel.component.scss'] -}) -export class AlarmStatusFilterPanelComponent { - - subscription: IWidgetSubscription; - - alarmSearchStatuses = Object.keys(AlarmSearchStatus); - alarmSearchStatusTranslationMap = alarmSearchStatusTranslations; - alarmSearchStatusEnum = AlarmSearchStatus; - - constructor(@Inject(ALARM_STATUS_FILTER_PANEL_DATA) public data: AlarmStatusFilterPanelData) { - this.subscription = this.data.subscription; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts index 2ae3cb686c..45088392af 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts @@ -73,17 +73,13 @@ import { import { AlarmDataInfo, alarmFields, + AlarmSearchStatus, alarmSeverityColors, alarmSeverityTranslations, AlarmStatus, alarmStatusTranslations } from '@shared/models/alarm.models'; import { DatePipe } from '@angular/common'; -import { - ALARM_STATUS_FILTER_PANEL_DATA, - AlarmStatusFilterPanelComponent, - AlarmStatusFilterPanelData -} from '@home/components/widget/lib/alarm-status-filter-panel.component'; import { AlarmDetailsDialogComponent, AlarmDetailsDialogData @@ -101,11 +97,18 @@ import { KeyFilter } from '@app/shared/models/query/query.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { + ALARM_FILTER_PANEL_DATA, + AlarmFilterPanelComponent, + AlarmFilterPanelData +} from '@home/components/widget/lib/alarm-filter-panel.component'; +import { entityFields } from '@shared/models/entity.models'; interface AlarmsTableWidgetSettings extends TableWidgetSettings { alarmsTitle: string; enableSelection: boolean; - enableStatusFilter: boolean; + enableStatusFilter?: boolean; + enableFilter: boolean; enableStickyAction: boolean; displayDetails: boolean; allowAcknowledgment: boolean; @@ -178,11 +181,11 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, } }; - private statusFilterAction: WidgetAction = { - name: 'alarm.alarm-status-filter', + private alarmFilterAction: WidgetAction = { + name: 'alarm.alarm-filter', show: true, onAction: ($event) => { - this.editAlarmStatusFilter($event); + this.editAlarmFilter($event); }, icon: 'filter_list' }; @@ -255,7 +258,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, } private initializeConfig() { - this.ctx.widgetActions = [this.searchAction, this.statusFilterAction, this.columnDisplayAction]; + this.ctx.widgetActions = [this.searchAction, this.alarmFilterAction, this.columnDisplayAction]; this.displayDetails = isDefined(this.settings.displayDetails) ? this.settings.displayDetails : true; this.allowAcknowledgment = isDefined(this.settings.allowAcknowledgment) ? this.settings.allowAcknowledgment : true; @@ -312,7 +315,15 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true; this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : false; this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true; - this.statusFilterAction.show = isDefined(this.settings.enableStatusFilter) ? this.settings.enableStatusFilter : true; + let enableFilter; + if (isDefined(this.settings.enableFilter)) { + enableFilter = this.settings.enableFilter; + } else if (isDefined(this.settings.enableStatusFilter)) { + enableFilter = this.settings.enableStatusFilter; + } else { + enableFilter = true; + } + this.alarmFilterAction.show = enableFilter; const pageSize = this.settings.defaultPageSize; if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) { @@ -321,11 +332,17 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; this.pageLink.pageSize = this.displayPagination ? this.defaultPageSize : Number.POSITIVE_INFINITY; - // TODO: search status, severity, types, searchPropagatedAlarms from widget config to pageLink - this.pageLink.searchPropagatedAlarms = false; // true for old widget configs - this.pageLink.severityList = []; - this.pageLink.statusList = []; - this.pageLink.typeList = []; + this.pageLink.searchPropagatedAlarms = isDefined(this.widgetConfig.searchPropagatedAlarms) + ? this.widgetConfig.searchPropagatedAlarms : true; + let alarmStatusList: AlarmSearchStatus[] = []; + if (isDefined(this.widgetConfig.alarmStatusList) && this.widgetConfig.alarmStatusList.length) { + alarmStatusList = this.widgetConfig.alarmStatusList; + } else if (isDefined(this.widgetConfig.alarmSearchStatus) && this.widgetConfig.alarmSearchStatus !== AlarmSearchStatus.ANY) { + alarmStatusList = [this.widgetConfig.alarmSearchStatus]; + } + this.pageLink.statusList = alarmStatusList; + this.pageLink.severityList = isDefined(this.widgetConfig.alarmSeverityList) ? this.widgetConfig.alarmSeverityList : []; + this.pageLink.typeList = isDefined(this.widgetConfig.alarmTypeList) ? this.widgetConfig.alarmTypeList : []; const cssString = constructTableCssString(this.widgetConfig); const cssParser = new cssjs(); @@ -440,7 +457,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, this.ctx.detectChanges(); } - private editAlarmStatusFilter($event: Event) { + private editAlarmFilter($event: Event) { if ($event) { $event.stopPropagation(); } @@ -462,14 +479,25 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, overlayRef.dispose(); }); const injectionTokens = new WeakMap([ - [ALARM_STATUS_FILTER_PANEL_DATA, { - subscription: this.subscription, - } as AlarmStatusFilterPanelData], + [ALARM_FILTER_PANEL_DATA, { + statusList: this.pageLink.statusList, + severityList: this.pageLink.severityList, + typeList: this.pageLink.typeList + } as AlarmFilterPanelData], [OverlayRef, overlayRef] ]); const injector = new PortalInjector(this.viewContainerRef.injector, injectionTokens); - overlayRef.attach(new ComponentPortal(AlarmStatusFilterPanelComponent, + const componentRef = overlayRef.attach(new ComponentPortal(AlarmFilterPanelComponent, this.viewContainerRef, injector)); + componentRef.onDestroy(() => { + if (componentRef.instance.result) { + const result = componentRef.instance.result; + this.pageLink.statusList = result.statusList; + this.pageLink.severityList = result.severityList; + this.pageLink.typeList = result.typeList; + this.updateData(); + } + }); this.ctx.detectChanges(); } @@ -555,7 +583,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, content = '' + value; } } else { - content = this.defaultContent(key, value); + content = this.defaultContent(key, contentInfo, value); } return isDefined(content) ? this.domSanitizer.bypassSecurityTrustHtml(content) : ''; } else { @@ -750,7 +778,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, } } - private defaultContent(key: EntityColumn, value: any): any { + private defaultContent(key: EntityColumn, contentInfo: CellContentInfo, value: any): any { if (isDefined(value)) { const alarmField = alarmFields[key.name]; if (alarmField) { @@ -765,9 +793,16 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, } else { return value; } - } else { - return value; } + const entityField = entityFields[key.name]; + if (entityField) { + if (entityField.time) { + return this.datePipe.transform(value, 'yyyy-MM-dd HH:mm:ss'); + } + } + const decimals = (contentInfo.decimals || contentInfo.decimals === 0) ? contentInfo.decimals : this.ctx.widgetConfig.decimals; + const units = contentInfo.units || this.ctx.widgetConfig.units; + return this.ctx.utils.formatValue(value, decimals, units, true); } else { return ''; } @@ -827,13 +862,20 @@ class AlarmsDatasource implements DataSource { } loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { + this.dataLoading = true; + this.clear(); + this.appliedPageLink = pageLink; + this.appliedSortOrderLabel = sortOrderLabel; + this.subscription.subscribeForAlarms(pageLink, keyFilters); + } + + private clear() { if (this.selection.hasValue()) { this.selection.clear(); this.onSelectionModeChanged(false); } - this.appliedPageLink = pageLink; - this.appliedSortOrderLabel = sortOrderLabel; - this.subscription.subscribeForAlarms(pageLink, keyFilters); + this.alarmsSubject.next([]); + this.pageDataSubject.next(emptyPageData()); } updateAlarms() { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts index 3b5c40e157..142edcfe64 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts @@ -86,7 +86,6 @@ import { } from '@shared/models/query/query.models'; import { sortItems } from '@shared/models/page/page-link'; import { entityFields } from '@shared/models/entity.models'; -import { alarmFields } from '@shared/models/alarm.models'; import { DatePipe } from '@angular/common'; interface EntitiesTableWidgetSettings extends TableWidgetSettings { @@ -596,11 +595,17 @@ class EntityDatasource implements DataSource { loadEntities(pageLink: EntityDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { this.dataLoading = true; + this.clear(); this.appliedPageLink = pageLink; this.appliedSortOrderLabel = sortOrderLabel; this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters); } + private clear() { + this.entitiesSubject.next([]); + this.pageDataSubject.next(emptyPageData()); + } + dataUpdated() { const datasourcesPageData = this.subscription.datasourcePages[0]; const dataPageData = this.subscription.dataPages[0]; diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index 4e00afe9c3..a97096a953 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -20,7 +20,7 @@ import { SharedModule } from '@app/shared/shared.module'; import { EntitiesTableWidgetComponent } from '@home/components/widget/lib/entities-table-widget.component'; import { DisplayColumnsPanelComponent } from '@home/components/widget/lib/display-columns-panel.component'; import { AlarmsTableWidgetComponent } from '@home/components/widget/lib/alarms-table-widget.component'; -import { AlarmStatusFilterPanelComponent } from '@home/components/widget/lib/alarm-status-filter-panel.component'; +import { AlarmFilterPanelComponent } from '@home/components/widget/lib/alarm-filter-panel.component'; import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module'; import { TimeseriesTableWidgetComponent } from '@home/components/widget/lib/timeseries-table-widget.component'; import { EntitiesHierarchyWidgetComponent } from '@home/components/widget/lib/entities-hierarchy-widget.component'; @@ -40,7 +40,7 @@ import { ImportExportService } from '@home/components/import-export/import-expor declarations: [ DisplayColumnsPanelComponent, - AlarmStatusFilterPanelComponent, + AlarmFilterPanelComponent, EntitiesTableWidgetComponent, AlarmsTableWidgetComponent, TimeseriesTableWidgetComponent, diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html index 365421d76f..fc3fbccdda 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html @@ -42,58 +42,45 @@
- - alarm.alarm-status - + + alarm.alarm-status-list + - {{ ('alarm.search-status.' + searchStatus) | translate }} + {{ alarmSearchStatusTranslationMap.get(searchStatus) | translate }} - - alarm.polling-interval - - - {{ 'alarm.polling-interval-required' | translate }} - - - {{ 'alarm.min-polling-interval-message' | translate }} - + + alarm.alarm-severity-list + + + {{ alarmSeverityTranslationMap.get(alarmSeverityEnum[alarmSeverity]) | translate }} + +
- - alarm.max-count-load - - - {{ 'alarm.max-count-load-required' | translate }} - - - {{ 'alarm.max-count-load-error-min' | translate }} - - - - alarm.fetch-size - - - {{ 'alarm.fetch-size-required' | translate }} - - - {{ 'alarm.fetch-size-error-min' | translate }} - + + alarm.alarm-type-list + + + {{type}} + cancel + + + + + {{ 'alarm.search-propagated-alarms' | translate }} +