thingsboard/ui-ngx/src/app/core/http/alarm.service.ts

223 lines
8.4 KiB
TypeScript
Raw Normal View History

2019-08-29 12:08:49 +03:00
///
/// Copyright © 2016-2019 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 { Injectable } from '@angular/core';
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
import { Observable } from 'rxjs';
2019-08-29 12:08:49 +03:00
import { HttpClient } from '@angular/common/http';
import { PageData } from '@shared/models/page/page-data';
import { EntityId } from '@shared/models/id/entity-id';
import {
Alarm,
AlarmInfo,
AlarmQuery,
AlarmSearchStatus,
AlarmSeverity,
AlarmStatus,
simulatedAlarm
2019-08-29 12:08:49 +03:00
} from '@shared/models/alarm.models';
2019-09-10 15:12:10 +03:00
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 { EMPTY } from 'rxjs';
import Timeout = NodeJS.Timeout;
interface AlarmSourceListenerQuery {
entityType: EntityType;
entityId: string;
alarmSearchStatus: AlarmSearchStatus;
alarmStatus: AlarmStatus;
fetchOriginator?: boolean;
limit?: number;
interval?: number;
startTime?: number;
endTime?: number;
onAlarms?: (alarms: Array<AlarmInfo>) => void;
}
2019-09-10 15:12:10 +03:00
export interface AlarmSourceListener {
id?: string;
subscriptionTimewindow: SubscriptionTimewindow;
2019-09-10 15:12:10 +03:00
alarmSource: Datasource;
alarmsPollingInterval: number;
alarmSearchStatus: AlarmSearchStatus;
alarmsUpdated: (alarms: Array<AlarmInfo>) => void;
lastUpdateTs?: number;
alarmsQuery?: AlarmSourceListenerQuery;
pollTimer?: Timeout;
2019-09-10 15:12:10 +03:00
}
2019-08-29 12:08:49 +03:00
@Injectable({
providedIn: 'root'
})
export class AlarmService {
private alarmSourceListeners: {[id: string]: AlarmSourceListener} = {};
2019-08-29 12:08:49 +03:00
constructor(
private http: HttpClient,
private utils: UtilsService
2019-08-29 12:08:49 +03:00
) { }
public getAlarm(alarmId: string, config?: RequestConfig): Observable<Alarm> {
return this.http.get<Alarm>(`/api/alarm/${alarmId}`, defaultHttpOptionsFromConfig(config));
2019-08-29 12:08:49 +03:00
}
public getAlarmInfo(alarmId: string, config?: RequestConfig): Observable<AlarmInfo> {
return this.http.get<AlarmInfo>(`/api/alarm/info/${alarmId}`, defaultHttpOptionsFromConfig(config));
2019-08-29 12:08:49 +03:00
}
public saveAlarm(alarm: Alarm, config?: RequestConfig): Observable<Alarm> {
return this.http.post<Alarm>('/api/alarm', alarm, defaultHttpOptionsFromConfig(config));
2019-08-29 12:08:49 +03:00
}
public ackAlarm(alarmId: string, config?: RequestConfig): Observable<void> {
return this.http.post<void>(`/api/alarm/${alarmId}/ack`, null, defaultHttpOptionsFromConfig(config));
2019-08-29 12:08:49 +03:00
}
public clearAlarm(alarmId: string, config?: RequestConfig): Observable<void> {
return this.http.post<void>(`/api/alarm/${alarmId}/clear`, null, defaultHttpOptionsFromConfig(config));
2019-08-29 12:08:49 +03:00
}
public getAlarms(query: AlarmQuery,
config?: RequestConfig): Observable<PageData<AlarmInfo>> {
2019-08-29 12:08:49 +03:00
return this.http.get<PageData<AlarmInfo>>(`/api/alarm${query.toQuery()}`,
defaultHttpOptionsFromConfig(config));
2019-08-29 12:08:49 +03:00
}
public getHighestAlarmSeverity(entityId: EntityId, alarmSearchStatus: AlarmSearchStatus, alarmStatus: AlarmStatus,
config?: RequestConfig): Observable<AlarmSeverity> {
2019-08-29 12:08:49 +03:00
let url = `/api/alarm/highestSeverity/${entityId.entityType}/${entityId.entityType}`;
if (alarmSearchStatus) {
url += `?searchStatus=${alarmSearchStatus}`;
} else if (alarmStatus) {
url += `?status=${alarmStatus}`;
}
return this.http.get<AlarmSeverity>(url,
defaultHttpOptionsFromConfig(config));
2019-08-29 12:08:49 +03:00
}
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
};
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(100, 0,
null,
sortOrder, time - alarmsQuery.interval);
} else if (alarmsQuery.startTime) {
pageLink = new TimePageLink(100, 0,
null,
sortOrder, Math.round(alarmsQuery.startTime));
if (alarmsQuery.endTime) {
pageLink.endTime = Math.round(alarmsQuery.endTime);
}
}
return this.fetchAlarms(alarmsQuery, pageLink);
}
private fetchAlarms(query: AlarmSourceListenerQuery,
pageLink: TimePageLink): Observable<Array<AlarmInfo>> {
const alarmQuery = new AlarmQuery(
{id: query.entityId, entityType: query.entityType},
pageLink,
query.alarmSearchStatus,
query.alarmStatus,
query.fetchOriginator);
return this.getAlarms(alarmQuery, {ignoreLoading: true}).pipe(
expand((data) => {
if (data.hasNext && !query.limit) {
alarmQuery.pageLink.page += 1;
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))),
);
}
2019-08-29 12:08:49 +03:00
}