UI: WS Alarm Data
This commit is contained in:
parent
15e302e06c
commit
dee4625fa9
@ -16,9 +16,9 @@
|
||||
"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}"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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>;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -30,6 +30,7 @@ export interface DataKeyConfigDialogData {
|
||||
dataKey: DataKey;
|
||||
dataKeySettingsSchema: any;
|
||||
entityAliasId?: string;
|
||||
showPostProcessing?: boolean;
|
||||
callbacks?: DataKeysCallbacks;
|
||||
}
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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, '...')}) | 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">
|
||||
|
||||
@ -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,6 +226,9 @@ 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');
|
||||
@ -238,7 +236,6 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, AfterVie
|
||||
this.placeholder = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private reset() {
|
||||
if (this.widgetType === widgetType.alarm) {
|
||||
@ -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 {
|
||||
|
||||
@ -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,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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 &&
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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>(
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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…
x
Reference in New Issue
Block a user