Browse Source

UI: WS Alarm Data

pull/3068/head
Igor Kulikov 6 years ago
parent
commit
dee4625fa9
  1. 6
      application/src/main/data/json/system/widget_bundles/alarm_widgets.json
  2. 2
      ui-ngx/src/app/core/api/alias-controller.ts
  3. 14
      ui-ngx/src/app/core/api/widget-api.models.ts
  4. 72
      ui-ngx/src/app/core/api/widget-subscription.ts
  5. 168
      ui-ngx/src/app/core/http/alarm.service.ts
  6. 11
      ui-ngx/src/app/core/http/entity.service.ts
  7. 1
      ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html
  8. 1
      ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.ts
  9. 2
      ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html
  10. 3
      ui-ngx/src/app/modules/home/components/widget/data-key-config.component.ts
  11. 22
      ui-ngx/src/app/modules/home/components/widget/data-keys.component.html
  12. 57
      ui-ngx/src/app/modules/home/components/widget/data-keys.component.ts
  13. 66
      ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.html
  14. 0
      ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.scss
  15. 119
      ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.ts
  16. 25
      ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.html
  17. 43
      ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.ts
  18. 98
      ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts
  19. 7
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts
  20. 4
      ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
  21. 73
      ui-ngx/src/app/modules/home/components/widget/widget-config.component.html
  22. 85
      ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts
  23. 8
      ui-ngx/src/app/modules/home/components/widget/widget.component.ts
  24. 2
      ui-ngx/src/app/shared/models/query/query.models.ts
  25. 10
      ui-ngx/src/app/shared/models/widget.models.ts
  26. 11
      ui-ngx/src/assets/locale/locale.constant-en_US.json

6
application/src/main/data/json/system/widget_bundles/alarm_widgets.json

@ -16,10 +16,10 @@
"templateHtml": "<tb-alarms-table-widget \n [ctx]=\"ctx\">\n</tb-alarms-table-widget>",
"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}"
}
}
]
}
}

2
ui-ngx/src/app/core/api/alias-controller.ts

@ -293,7 +293,7 @@ export class AliasController implements IAliasController {
}
resolveAlarmSource(alarmSource: Datasource): Observable<Datasource> {
return this.resolveDatasource(alarmSource, true).pipe(
return this.resolveDatasource(alarmSource).pipe(
map((datasource) => {
if (datasource.type === DatasourceType.function) {
let name: string;

14
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<Datasource>;
hasDataPageLink?: boolean;
singleEntity?: boolean;
@ -273,8 +269,6 @@ export interface IWidgetSubscription {
alarms?: PageData<AlarmData>;
alarmSource?: Datasource;
/* alarmSearchStatus?: AlarmSearchStatus;
alarmsPollingInterval?: number; */
targetDeviceAliasIds?: Array<string>;
targetDeviceIds?: Array<string>;

72
ui-ngx/src/app/core/api/widget-subscription.ts

@ -104,30 +104,10 @@ export class WidgetSubscription implements IWidgetSubscription {
comparisonTimeWindow: WidgetTimewindow;
timewindowForComparison: SubscriptionTimewindow;
// alarms: Array<AlarmInfo>;
alarms: PageData<AlarmData>;
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<string>;
@ -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) {

168
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<AlarmInfo>) => void;
}
export interface AlarmSourceListener {
id?: string;
subscriptionTimewindow: SubscriptionTimewindow;
alarmSource: Datasource;
alarmsPollingInterval: number;
alarmSearchStatus: AlarmSearchStatus;
alarmsMaxCountLoad: number;
alarmsFetchSize: number;
alarmsUpdated: (alarms: Array<AlarmInfo>) => 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<Array<AlarmInfo>> {
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<Array<AlarmInfo>> {
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))),
);
}
}

11
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<string> {
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<Array<string>> {
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) {

1
ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html

@ -32,6 +32,7 @@
<tb-data-key-config #dataKeyConfig
[dataKeySettingsSchema]="data.dataKeySettingsSchema"
[entityAliasId]="data.entityAliasId"
[showPostProcessing]="data.showPostProcessing"
[callbacks]="data.callbacks"
formControlName="dataKey">
</tb-data-key-config>

1
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;
}

2
ui-ngx/src/app/modules/home/components/widget/data-key-config.component.html

@ -72,7 +72,7 @@
formControlName="funcBody">
</tb-js-func>
</section>
<section fxLayout="column" *ngIf="modelValue.type === dataKeyTypes.timeseries || modelValue.type === dataKeyTypes.attribute">
<section fxLayout="column" *ngIf="(modelValue.type === dataKeyTypes.timeseries || modelValue.type === dataKeyTypes.attribute) && showPostProcessing">
<mat-checkbox formControlName="usePostProcessing">
{{ 'datakey.use-data-post-processing-func' | translate }}
</mat-checkbox>

3
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;

22
ui-ngx/src/app/modules/home/components/widget/data-keys.component.html

@ -31,7 +31,12 @@
</div>
<div class="tb-chip-labels">
<div class="tb-chip-label">
<span *ngIf="datasourceType !== datasourceTypes.function && widgetType !== widgetTypes.alarm">
<span *ngIf="datasourceType !== datasourceTypes.function">
<span *ngIf="key.type === dataKeyTypes.alarm"
matTooltip="{{'datakey.alarm' | translate }}"
matTooltipPosition="above">
<mat-icon class="tb-mat-20">notifications</mat-icon>
</span>
<span *ngIf="key.type === dataKeyTypes.attribute"
matTooltip="{{'datakey.attributes' | translate }}"
matTooltipPosition="above">
@ -86,7 +91,12 @@
[displayWith]="displayKeyFn">
<mat-option *ngFor="let key of filteredKeys | async" [value]="key">
<span style="white-space: nowrap;">
<span *ngIf="datasourceType !== datasourceTypes.function && widgetType !== widgetTypes.alarm">
<span *ngIf="datasourceType !== datasourceTypes.function">
<span *ngIf="key.type === dataKeyTypes.alarm"
matTooltip="{{'datakey.alarm' | translate }}"
matTooltipPosition="above">
<mat-icon class="tb-mat-16">notifications</mat-icon>
</span>
<span *ngIf="key.type === dataKeyTypes.attribute"
matTooltip="{{'datakey.attributes' | translate }}"
matTooltipPosition="above">
@ -118,11 +128,17 @@
{{ translate.get('entity.no-key-matching',
{key: truncate.transform(searchText, true, 6, &apos;...&apos;)}) | async }}
</span>
<span *ngIf="datasourceType === datasourceTypes.function || widgetType === widgetTypes.alarm; else createEntityKey">
<span *ngIf="datasourceType === datasourceTypes.function; else createEntityKey">
<a translate (click)="createKey(searchText)">entity.create-new-key</a>
</span>
<ng-template #createEntityKey>
<span>{{'entity.create-new-key' | translate }} </span>
<span *ngIf="widgetType == widgetTypes.alarm"
matTooltip="{{'datakey.alarm' | translate }}"
matTooltipPosition="above">
<mat-icon (click)="createKey(searchText, dataKeyTypes.alarm)"
class="tb-mat-16">notifications</mat-icon>
</span>
<span *ngIf="widgetType == widgetTypes.latest"
matTooltip="{{'datakey.attributes' | translate }}"
matTooltipPosition="above">

57
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<Array<DataKey>> = 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 {

66
ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.html

@ -0,0 +1,66 @@
<!--
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.
-->
<form fxLayout="column" class="mat-content mat-padding" [formGroup]="alarmFilterFormGroup" (ngSubmit)="update()">
<mat-form-field fxFlex class="mat-block" floatLabel="always">
<mat-label translate>alarm.alarm-status-list</mat-label>
<mat-select formControlName="alarmStatusList" multiple
placeholder="{{ !alarmFilterFormGroup.get('alarmStatusList').value?.length ? ('alarm.any-status' | translate) : '' }}">
<mat-option *ngFor="let searchStatus of alarmSearchStatuses" [value]="searchStatus">
{{ alarmSearchStatusTranslationMap.get(searchStatus) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex class="mat-block" floatLabel="always">
<mat-label translate>alarm.alarm-severity-list</mat-label>
<mat-select formControlName="alarmSeverityList" multiple
placeholder="{{ !alarmFilterFormGroup.get('alarmSeverityList').value?.length ? ('alarm.any-severity' | translate) : '' }}">
<mat-option *ngFor="let alarmSeverity of alarmSeverities" [value]="alarmSeverity">
{{ alarmSeverityTranslationMap.get(alarmSeverityEnum[alarmSeverity]) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex class="mat-block" floatLabel="always">
<mat-label translate>alarm.alarm-type-list</mat-label>
<mat-chip-list #alarmTypeChipList formControlName="alarmTypeList">
<mat-chip *ngFor="let type of alarmTypeList()" [selectable]="true"
[removable]="true" (removed)="removeAlarmType(type)">
{{type}}
<mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
<input placeholder="{{ !alarmFilterFormGroup.get('alarmTypeList').value?.length ? ('alarm.any-type' | translate) : '' }}"
[matChipInputFor]="alarmTypeChipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addAlarmType($event)">
</mat-chip-list>
</mat-form-field>
<div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center">
<button type="submit"
mat-raised-button
color="primary"
[disabled]="alarmFilterFormGroup.invalid || !alarmFilterFormGroup.dirty">
{{ 'action.update' | translate }}
</button>
<button type="button"
mat-button
(click)="cancel()"
style="margin-right: 20px;">
{{ 'action.cancel' | translate }}
</button>
</div>
</form>

0
ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.scss → ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.scss

119
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<any>('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();
}
}

25
ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.html

@ -1,25 +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.
-->
<div fxLayout="column" class="mat-content mat-padding">
<label class="tb-title" translate>alarm.alarm-status-filter</label>
<mat-radio-group [(ngModel)]="subscription.alarmSearchStatus" fxLayout="column" fxLayoutGap="16px">
<mat-radio-button *ngFor="let searchStatus of alarmSearchStatuses" [value]="searchStatus" color="primary">
{{ alarmSearchStatusTranslationMap.get(alarmSearchStatusEnum[searchStatus]) | translate }}
</mat-radio-button>
</mat-radio-group>
</div>

43
ui-ngx/src/app/modules/home/components/widget/lib/alarm-status-filter-panel.component.ts

@ -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<any>('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;
}
}

98
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<any, any>([
[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<AlarmDataInfo> {
}
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<AlarmDataInfo>());
}
updateAlarms() {

7
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<EntityData> {
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<EntityData>());
}
dataUpdated() {
const datasourcesPageData = this.subscription.datasourcePages[0];
const dataPageData = this.subscription.dataPages[0];

4
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,

73
ui-ngx/src/app/modules/home/components/widget/widget-config.component.html

@ -42,58 +42,45 @@
<div *ngIf="widgetType === widgetTypes.alarm" fxLayout="column" fxLayoutAlign="center">
<div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center"
fxLayoutGap="8px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>alarm.alarm-status</mat-label>
<mat-select formControlName="alarmSearchStatus">
<mat-form-field fxFlex class="mat-block" floatLabel="always">
<mat-label translate>alarm.alarm-status-list</mat-label>
<mat-select formControlName="alarmStatusList" multiple
placeholder="{{ !dataSettings.get('alarmStatusList').value?.length ? ('alarm.any-status' | translate) : '' }}">
<mat-option *ngFor="let searchStatus of alarmSearchStatuses" [value]="searchStatus">
{{ ('alarm.search-status.' + searchStatus) | translate }}
{{ alarmSearchStatusTranslationMap.get(searchStatus) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>alarm.polling-interval</mat-label>
<input matInput required
formControlName="alarmsPollingInterval"
type="number"
step="1"/>
<mat-error *ngIf="dataSettings.get('alarmsPollingInterval').hasError('required')">
{{ 'alarm.polling-interval-required' | translate }}
</mat-error>
<mat-error *ngIf="dataSettings.get('alarmsPollingInterval').hasError('min')">
{{ 'alarm.min-polling-interval-message' | translate }}
</mat-error>
<mat-form-field fxFlex class="mat-block" floatLabel="always">
<mat-label translate>alarm.alarm-severity-list</mat-label>
<mat-select formControlName="alarmSeverityList" multiple
placeholder="{{ !dataSettings.get('alarmSeverityList').value?.length ? ('alarm.any-severity' | translate) : '' }}">
<mat-option *ngFor="let alarmSeverity of alarmSeverities" [value]="alarmSeverity">
{{ alarmSeverityTranslationMap.get(alarmSeverityEnum[alarmSeverity]) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div fxLayout="column" fxLayoutAlign="center" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center"
fxLayoutGap="8px">
<mat-form-field fxFlex class="mat-block">
<mat-label translate>alarm.max-count-load</mat-label>
<input matInput required
formControlName="alarmsMaxCountLoad"
type="number"
min="0"
step="1">
<mat-error *ngIf="dataSettings.get('alarmsMaxCountLoad').hasError('required')">
{{ 'alarm.max-count-load-required' | translate }}
</mat-error>
<mat-error *ngIf="dataSettings.get('alarmsMaxCountLoad').hasError('min')">
{{ 'alarm.max-count-load-error-min' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>alarm.fetch-size</mat-label>
<input matInput required
formControlName="alarmsFetchSize"
type="number"
min="10"
step="1">
<mat-error *ngIf="dataSettings.get('alarmsFetchSize').hasError('required')">
{{ 'alarm.fetch-size-required' | translate }}
</mat-error>
<mat-error *ngIf="dataSettings.get('alarmsFetchSize').hasError('min')">
{{ 'alarm.fetch-size-error-min' | translate }}
</mat-error>
<mat-form-field fxFlex class="mat-block" floatLabel="always">
<mat-label translate>alarm.alarm-type-list</mat-label>
<mat-chip-list #alarmTypeChipList formControlName="alarmTypeList">
<mat-chip *ngFor="let type of alarmTypeList()" [selectable]="true"
[removable]="true" (removed)="removeAlarmType(type)">
{{type}}
<mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
<input placeholder="{{ !dataSettings.get('alarmTypeList').value?.length ? ('alarm.any-type' | translate) : '' }}"
[matChipInputFor]="alarmTypeChipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addAlarmType($event)">
</mat-chip-list>
</mat-form-field>
<mat-checkbox fxFlex formControlName="searchPropagatedAlarms">
{{ 'alarm.search-propagated-alarms' | translate }}
</mat-checkbox>
</div>
</div>
<mat-expansion-panel class="tb-datasources" *ngIf="widgetType !== widgetTypes.rpc &&

85
ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts

@ -41,7 +41,13 @@ import {
} from '@angular/forms';
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
import { deepClone, isDefined, isObject } from '@app/core/utils';
import { alarmFields, AlarmSearchStatus } from '@shared/models/alarm.models';
import {
alarmFields,
AlarmSearchStatus,
alarmSearchStatusTranslations,
AlarmSeverity,
alarmSeverityTranslations
} from '@shared/models/alarm.models';
import { IAliasController } from '@core/api/widget-api.models';
import { EntityAlias, EntityAliases } from '@shared/models/alias.models';
import { UtilsService } from '@core/services/utils.service';
@ -63,6 +69,8 @@ import { DashboardState } from '@shared/models/dashboard.models';
import { entityFields } from '@shared/models/entity.models';
import { Filter, Filters } from '@shared/models/query/query.models';
import { FilterDialogComponent, FilterDialogData } from '@home/components/filter/filter-dialog.component';
import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
import { MatChipInputEvent } from '@angular/material/chips';
const emptySettingsSchema: JsonSchema = {
type: 'object',
@ -92,11 +100,23 @@ const defaultSettingsForm = [
})
export class WidgetConfigComponent extends PageComponent implements OnInit, ControlValueAccessor, Validator {
readonly separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON];
widgetTypes = widgetType;
entityTypes = EntityType;
alarmSearchStatuses = Object.keys(AlarmSearchStatus);
alarmSearchStatuses = [AlarmSearchStatus.ACTIVE,
AlarmSearchStatus.CLEARED,
AlarmSearchStatus.ACK,
AlarmSearchStatus.UNACK];
alarmSearchStatusTranslationMap = alarmSearchStatusTranslations;
alarmSeverities = Object.keys(AlarmSeverity);
alarmSeverityEnum = AlarmSeverity;
alarmSeverityTranslationMap = alarmSeverityTranslations;
@Input()
forceExpandDatasources: boolean;
@ -293,13 +313,10 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
}
});
if (this.widgetType === widgetType.alarm) {
this.dataSettings.addControl('alarmSearchStatus', this.fb.control(null));
this.dataSettings.addControl('alarmsPollingInterval', this.fb.control(null,
[Validators.required, Validators.min(1)]));
this.dataSettings.addControl('alarmsMaxCountLoad', this.fb.control(null,
[Validators.required, Validators.min(0)]));
this.dataSettings.addControl('alarmsFetchSize', this.fb.control(null,
[Validators.required, Validators.min(10)]));
this.dataSettings.addControl('alarmStatusList', this.fb.control(null));
this.dataSettings.addControl('alarmSeverityList', this.fb.control(null));
this.dataSettings.addControl('alarmTypeList', this.fb.control(null));
this.dataSettings.addControl('searchPropagatedAlarms', this.fb.control(null));
}
}
if (this.modelValue.isDataEnabled) {
@ -445,21 +462,24 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
targetDeviceAliasId
}, {emitEvent: false});
} else if (this.widgetType === widgetType.alarm) {
let alarmStatusList: AlarmSearchStatus[] = [];
if (isDefined(config.alarmStatusList) && config.alarmStatusList.length) {
alarmStatusList = config.alarmStatusList;
} else if (isDefined(config.alarmSearchStatus) && config.alarmSearchStatus !== AlarmSearchStatus.ANY) {
alarmStatusList = [config.alarmSearchStatus];
}
this.dataSettings.patchValue(
{ alarmSearchStatus: isDefined(config.alarmSearchStatus) ?
config.alarmSearchStatus : AlarmSearchStatus.ANY }, {emitEvent: false}
{ alarmStatusList }, {emitEvent: false}
);
this.dataSettings.patchValue(
{ alarmsPollingInterval: isDefined(config.alarmsPollingInterval) ?
config.alarmsPollingInterval : 5}, {emitEvent: false}
{ alarmSeverityList: isDefined(config.alarmSeverityList) ? config.alarmSeverityList : [] }, {emitEvent: false}
);
this.dataSettings.patchValue(
{ alarmsMaxCountLoad: isDefined(config.alarmsMaxCountLoad) ?
config.alarmsMaxCountLoad : 0}, {emitEvent: false}
{ alarmTypeList: isDefined(config.alarmTypeList) ? config.alarmTypeList : [] }, {emitEvent: false}
);
this.dataSettings.patchValue(
{ alarmsFetchSize: isDefined(config.alarmsFetchSize) ?
config.alarmsFetchSize : 100}, {emitEvent: false}
{ searchPropagatedAlarms: isDefined(config.searchPropagatedAlarms) ?
config.searchPropagatedAlarms : true }, {emitEvent: false}
);
this.alarmSourceSettings.patchValue(
config.alarmSource, {emitEvent: false}
@ -604,6 +624,37 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
}
}
public alarmTypeList(): string[] {
return this.dataSettings.get('alarmTypeList').value;
}
public removeAlarmType(type: string): void {
const types: string[] = this.dataSettings.get('alarmTypeList').value;
const index = types.indexOf(type);
if (index >= 0) {
types.splice(index, 1);
this.dataSettings.get('alarmTypeList').setValue(types);
this.dataSettings.get('alarmTypeList').markAsDirty();
}
}
public addAlarmType(event: MatChipInputEvent): void {
const input = event.input;
const value = event.value;
const types: string[] = this.dataSettings.get('alarmTypeList').value;
if ((value || '').trim()) {
types.push(value.trim());
this.dataSettings.get('alarmTypeList').setValue(types);
this.dataSettings.get('alarmTypeList').markAsDirty();
}
if (input) {
input.value = '';
}
}
public displayAdvanced(): boolean {
return !!this.modelValue && !!this.modelValue.settingsSchema && !!this.modelValue.settingsSchema.schema;
}

8
ui-ngx/src/app/modules/home/components/widget/widget.component.ts

@ -902,14 +902,6 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
};
if (this.widget.type === widgetType.alarm) {
options.alarmSource = deepClone(this.widget.config.alarmSource);
/*options.alarmSearchStatus = isDefined(this.widget.config.alarmSearchStatus) ?
this.widget.config.alarmSearchStatus : AlarmSearchStatus.ANY;
options.alarmsPollingInterval = isDefined(this.widget.config.alarmsPollingInterval) ?
this.widget.config.alarmsPollingInterval * 1000 : 5000;
options.alarmsMaxCountLoad = isDefined(this.widget.config.alarmsMaxCountLoad) ?
this.widget.config.alarmsMaxCountLoad : 0;
options.alarmsFetchSize = isDefined(this.widget.config.alarmsFetchSize) ?
this.widget.config.alarmsFetchSize : 100;*/
} else {
options.datasources = deepClone(this.widget.config.datasources);
}

2
ui-ngx/src/app/shared/models/query/query.models.ts

@ -33,7 +33,7 @@ export enum EntityKeyType {
SERVER_ATTRIBUTE = 'SERVER_ATTRIBUTE',
TIME_SERIES = 'TIME_SERIES',
ENTITY_FIELD = 'ENTITY_FIELD',
ALARM_FIELD = 'ENTITY_FIELD'
ALARM_FIELD = 'ALARM_FIELD'
}
export const entityKeyTypeTranslationMap = new Map<EntityKeyType, string>(

10
ui-ngx/src/app/shared/models/widget.models.ts

@ -19,7 +19,7 @@ import { TenantId } from '@shared/models/id/tenant-id';
import { WidgetTypeId } from '@shared/models/id/widget-type-id';
import { Timewindow } from '@shared/models/time/time.models';
import { EntityType } from '@shared/models/entity-type.models';
import { AlarmSearchStatus } from '@shared/models/alarm.models';
import { AlarmSearchStatus, AlarmSeverity } from '@shared/models/alarm.models';
import { DataKeyType } from './telemetry/telemetry.models';
import { EntityId } from '@shared/models/id/entity-id';
import * as moment_ from 'moment';
@ -378,10 +378,10 @@ export interface WidgetConfig {
actions?: {[actionSourceId: string]: Array<WidgetActionDescriptor>};
settings?: any;
alarmSource?: Datasource;
alarmSearchStatus?: AlarmSearchStatus;
alarmsPollingInterval?: number;
alarmsMaxCountLoad?: number;
alarmsFetchSize?: number;
alarmStatusList?: AlarmSearchStatus[];
alarmSeverityList?: AlarmSeverity[];
alarmTypeList?: string[];
searchPropagatedAlarms?: boolean;
datasources?: Array<Datasource>;
targetDeviceAliasIds?: Array<string>;
[key: string]: any;

11
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -128,6 +128,8 @@
"no-alarms-matching": "No alarms matching '{{entity}}' were found.",
"alarm-required": "Alarm is required",
"alarm-status": "Alarm status",
"alarm-status-list": "Alarm status list",
"any-status": "Any status",
"search-status": {
"ANY": "Any",
"ACTIVE": "Active",
@ -154,6 +156,8 @@
"end-time": "End time",
"ack-time": "Acknowledged time",
"clear-time": "Cleared time",
"alarm-severity-list": "Alarm severity list",
"any-severity": "Any severity",
"severity-critical": "Critical",
"severity-major": "Major",
"severity-minor": "Minor",
@ -176,12 +180,16 @@
"clear-alarm-title": "Clear Alarm",
"clear-alarm-text": "Are you sure you want to clear Alarm?",
"alarm-status-filter": "Alarm Status Filter",
"alarm-filter": "Alarm Filter",
"max-count-load": "Maximum number of alarms to load (0 - unlimited)",
"max-count-load-required": "Maximum number of alarms to load is required.",
"max-count-load-error-min": "Minimum value is 0.",
"fetch-size": "Fetch size",
"fetch-size-required": "Fetch size is required.",
"fetch-size-error-min": "Minimum value is 10."
"fetch-size-error-min": "Minimum value is 10.",
"alarm-type-list": "Alarm type list",
"any-type": "Any type",
"search-propagated-alarms": "Search propagated alarms"
},
"alias": {
"add": "Add alias",
@ -616,6 +624,7 @@
"alarm": "Alarm fields",
"timeseries-required": "Entity timeseries are required.",
"timeseries-or-attributes-required": "Entity timeseries/attributes are required.",
"alarm-fields-timeseries-or-attributes-required": "Alarm fields or entity timeseries/attributes are required.",
"maximum-timeseries-or-attributes": "Maximum { count, plural, 1 {1 timeseries/attribute is allowed.} other {# timeseries/attributes are allowed} }",
"alarm-fields-required": "Alarm fields are required.",
"function-types": "Function types",

Loading…
Cancel
Save