1007 lines
28 KiB
TypeScript
Raw Normal View History

2019-08-19 20:09:41 +03:00
///
2024-01-09 10:46:16 +02:00
/// Copyright © 2016-2024 The Thingsboard Authors
2019-08-19 20:09:41 +03:00
///
/// 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.
///
2019-08-30 19:19:45 +03:00
import { EntityType } from '@shared/models/entity-type.models';
import { AggregationType } from '../time/time.models';
2023-12-11 13:30:34 +02:00
import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';
2019-08-30 19:19:45 +03:00
import { EntityId } from '@shared/models/id/entity-id';
import { map } from 'rxjs/operators';
import { NgZone } from '@angular/core';
2020-11-09 15:54:05 +02:00
import {
AlarmCountQuery,
2020-11-09 15:54:05 +02:00
AlarmData,
AlarmDataQuery,
EntityCountQuery,
2020-11-09 15:54:05 +02:00
EntityData,
EntityDataQuery,
EntityFilter,
2020-11-09 15:54:05 +02:00
EntityKey,
TsValue
} from '@shared/models/query/query.models';
import { PageData } from '@shared/models/page/page-data';
import { alarmFields } from '@shared/models/alarm.models';
import { entityFields } from '@shared/models/entity.models';
2023-12-11 13:30:34 +02:00
import { isDefinedAndNotNull, isUndefined } from '@core/utils';
import { CmdWrapper, WsService, WsSubscriber } from '@shared/models/websocket/websocket.models';
2023-02-16 12:59:43 +02:00
import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service';
2024-07-03 14:36:58 +03:00
import { Notification, NotificationType } from '@shared/models/notification.models';
2023-12-11 13:30:34 +02:00
import { WebsocketService } from '@core/ws/websocket.service';
2019-08-29 20:04:59 +03:00
export const NOT_SUPPORTED = 'Not supported!';
2019-08-19 20:09:41 +03:00
export enum DataKeyType {
timeseries = 'timeseries',
attribute = 'attribute',
function = 'function',
2020-02-21 19:04:49 +02:00
alarm = 'alarm',
2021-03-02 12:04:45 +02:00
entityField = 'entityField',
count = 'count'
2019-08-19 20:09:41 +03:00
}
2019-08-29 20:04:59 +03:00
export enum LatestTelemetry {
LATEST_TELEMETRY = 'LATEST_TELEMETRY'
}
export enum AttributeScope {
CLIENT_SCOPE = 'CLIENT_SCOPE',
SERVER_SCOPE = 'SERVER_SCOPE',
SHARED_SCOPE = 'SHARED_SCOPE'
}
export enum TimeseriesDeleteStrategy {
DELETE_ALL_DATA = 'DELETE_ALL_DATA',
DELETE_ALL_DATA_EXCEPT_LATEST_VALUE = 'DELETE_ALL_DATA_EXCEPT_LATEST_VALUE',
DELETE_LATEST_VALUE = 'DELETE_LATEST_VALUE',
DELETE_ALL_DATA_FOR_TIME_PERIOD = 'DELETE_ALL_DATA_FOR_TIME_PERIOD'
}
2019-08-29 20:04:59 +03:00
export type TelemetryType = LatestTelemetry | AttributeScope;
2023-02-06 13:09:43 +02:00
export const toTelemetryType = (val: string): TelemetryType => {
if (LatestTelemetry[val]) {
return LatestTelemetry[val];
} else {
return AttributeScope[val];
}
2023-02-06 13:09:43 +02:00
};
2019-08-29 20:04:59 +03:00
export const telemetryTypeTranslations = new Map<TelemetryType, string>(
[
[LatestTelemetry.LATEST_TELEMETRY, 'attribute.scope-telemetry'],
2019-08-29 20:04:59 +03:00
[AttributeScope.CLIENT_SCOPE, 'attribute.scope-client'],
[AttributeScope.SERVER_SCOPE, 'attribute.scope-server'],
[AttributeScope.SHARED_SCOPE, 'attribute.scope-shared']
]
);
export const telemetryTypeTranslationsShort = new Map<TelemetryType, string>(
[
[LatestTelemetry.LATEST_TELEMETRY, 'attribute.scope-telemetry-short'],
[AttributeScope.CLIENT_SCOPE, 'attribute.scope-client-short'],
[AttributeScope.SERVER_SCOPE, 'attribute.scope-server-short'],
[AttributeScope.SHARED_SCOPE, 'attribute.scope-shared-short']
]
);
2019-08-29 20:04:59 +03:00
export const isClientSideTelemetryType = new Map<TelemetryType, boolean>(
[
[LatestTelemetry.LATEST_TELEMETRY, true],
[AttributeScope.CLIENT_SCOPE, true],
[AttributeScope.SERVER_SCOPE, false],
[AttributeScope.SHARED_SCOPE, false]
]
);
export const timeseriesDeleteStrategyTranslations = new Map<TimeseriesDeleteStrategy, string>(
[
[TimeseriesDeleteStrategy.DELETE_ALL_DATA, 'attribute.delete-timeseries.all-data'],
[TimeseriesDeleteStrategy.DELETE_ALL_DATA_EXCEPT_LATEST_VALUE, 'attribute.delete-timeseries.all-data-except-latest-value'],
[TimeseriesDeleteStrategy.DELETE_LATEST_VALUE, 'attribute.delete-timeseries.latest-value'],
[TimeseriesDeleteStrategy.DELETE_ALL_DATA_FOR_TIME_PERIOD, 'attribute.delete-timeseries.all-data-for-time-period']
]
);
2019-08-29 20:04:59 +03:00
export interface AttributeData {
lastUpdateTs?: number;
2019-08-29 20:04:59 +03:00
key: string;
value: any;
}
2019-08-30 19:19:45 +03:00
export interface TimeseriesData {
2020-11-09 15:54:05 +02:00
[key: string]: Array<TsValue>;
}
export enum DataSortOrder {
ASC = 'ASC',
DESC = 'DESC'
}
export enum WsCmdType {
2023-12-08 18:05:47 +02:00
AUTH = 'AUTH',
ATTRIBUTES = 'ATTRIBUTES',
TIMESERIES = 'TIMESERIES',
TIMESERIES_HISTORY = 'TIMESERIES_HISTORY',
ENTITY_DATA = 'ENTITY_DATA',
ENTITY_COUNT = 'ENTITY_COUNT',
ALARM_DATA = 'ALARM_DATA',
ALARM_COUNT = 'ALARM_COUNT',
NOTIFICATIONS = 'NOTIFICATIONS',
NOTIFICATIONS_COUNT = 'NOTIFICATIONS_COUNT',
MARK_NOTIFICATIONS_AS_READ = 'MARK_NOTIFICATIONS_AS_READ',
MARK_ALL_NOTIFICATIONS_AS_READ = 'MARK_ALL_NOTIFICATIONS_AS_READ',
ALARM_DATA_UNSUBSCRIBE = 'ALARM_DATA_UNSUBSCRIBE',
ALARM_COUNT_UNSUBSCRIBE = 'ALARM_COUNT_UNSUBSCRIBE',
ENTITY_DATA_UNSUBSCRIBE = 'ENTITY_DATA_UNSUBSCRIBE',
ENTITY_COUNT_UNSUBSCRIBE = 'ENTITY_COUNT_UNSUBSCRIBE',
NOTIFICATIONS_UNSUBSCRIBE = 'NOTIFICATIONS_UNSUBSCRIBE'
}
export interface WebsocketCmd {
2019-08-30 19:19:45 +03:00
cmdId: number;
type: WsCmdType;
}
2023-12-08 18:05:47 +02:00
export interface AuthWsCmd {
authCmd: AuthCmd;
}
export interface TelemetryPluginCmd extends WebsocketCmd {
2019-08-30 19:19:45 +03:00
keys: string;
}
export abstract class SubscriptionCmd implements TelemetryPluginCmd {
cmdId: number;
keys: string;
entityType: EntityType;
entityId: string;
scope?: AttributeScope;
unsubscribe: boolean;
abstract type: WsCmdType;
2019-08-30 19:19:45 +03:00
}
export class AttributesSubscriptionCmd extends SubscriptionCmd {
type = WsCmdType.ATTRIBUTES;
2019-08-30 19:19:45 +03:00
}
export enum IntervalType {
MILLISECONDS = 'MILLISECONDS',
WEEK = 'WEEK',
WEEK_ISO = 'WEEK_ISO',
MONTH = 'MONTH',
QUARTER = 'QUARTER'
}
2019-08-30 19:19:45 +03:00
export class TimeseriesSubscriptionCmd extends SubscriptionCmd {
startTs: number;
timeWindow: number;
interval: number;
limit: number;
agg: AggregationType;
type = WsCmdType.TIMESERIES;
2019-08-30 19:19:45 +03:00
}
export class GetHistoryCmd implements TelemetryPluginCmd {
cmdId: number;
keys: string;
entityType: EntityType;
entityId: string;
startTs: number;
endTs: number;
interval: number;
limit: number;
agg: AggregationType;
type = WsCmdType.TIMESERIES_HISTORY;
2019-08-30 19:19:45 +03:00
}
export interface EntityHistoryCmd {
keys: Array<string>;
startTs: number;
endTs: number;
intervalType: IntervalType;
interval: number;
timeZoneId: string;
limit: number;
agg: AggregationType;
2020-06-24 18:07:47 +03:00
fetchLatestPreviousPoint?: boolean;
}
export interface LatestValueCmd {
2020-06-22 18:55:15 +03:00
keys: Array<EntityKey>;
}
export interface TimeSeriesCmd {
keys: Array<string>;
startTs: number;
timeWindow: number;
intervalType: IntervalType;
interval: number;
timeZoneId: string;
limit: number;
agg: AggregationType;
2020-06-24 18:07:47 +03:00
fetchLatestPreviousPoint?: boolean;
}
export interface AggKey {
id: number;
key: string;
agg: AggregationType;
previousStartTs?: number;
previousEndTs?: number;
previousValueOnly?: boolean;
}
export interface AggEntityHistoryCmd {
keys: Array<AggKey>;
startTs: number;
endTs: number;
}
export interface AggTimeSeriesCmd {
keys: Array<AggKey>;
startTs: number;
timeWindow: number;
}
export class EntityDataCmd implements WebsocketCmd {
cmdId: number;
2020-06-22 18:55:15 +03:00
query?: EntityDataQuery;
historyCmd?: EntityHistoryCmd;
latestCmd?: LatestValueCmd;
tsCmd?: TimeSeriesCmd;
aggHistoryCmd?: AggEntityHistoryCmd;
aggTsCmd?: AggTimeSeriesCmd;
type = WsCmdType.ENTITY_DATA;
2020-06-23 20:56:44 +03:00
public isEmpty(): boolean {
return !this.query && !this.historyCmd && !this.latestCmd && !this.tsCmd && !this.aggTsCmd && !this.aggHistoryCmd;
2020-06-23 20:56:44 +03:00
}
}
2021-03-02 12:04:45 +02:00
export class EntityCountCmd implements WebsocketCmd {
cmdId: number;
query?: EntityCountQuery;
type = WsCmdType.ENTITY_COUNT;
2021-03-02 12:04:45 +02:00
}
2020-07-03 18:33:06 +03:00
export class AlarmDataCmd implements WebsocketCmd {
cmdId: number;
query?: AlarmDataQuery;
type = WsCmdType.ALARM_DATA;
2020-07-03 18:33:06 +03:00
public isEmpty(): boolean {
return !this.query;
}
}
export class AlarmCountCmd implements WebsocketCmd {
cmdId: number;
query?: AlarmCountQuery;
type = WsCmdType.ALARM_COUNT;
}
2023-12-11 13:30:34 +02:00
export class UnreadCountSubCmd implements WebsocketCmd {
cmdId: number;
type = WsCmdType.NOTIFICATIONS_COUNT;
}
export class UnreadSubCmd implements WebsocketCmd {
limit: number;
2024-07-03 14:36:58 +03:00
types: Array<NotificationType>;
2023-12-11 13:30:34 +02:00
cmdId: number;
type = WsCmdType.NOTIFICATIONS;
2024-07-03 14:36:58 +03:00
constructor(limit = 10,
types: Array<NotificationType> = []) {
2023-12-11 13:30:34 +02:00
this.limit = limit;
2024-07-03 14:36:58 +03:00
this.types = types;
2023-12-11 13:30:34 +02:00
}
}
export class MarkAsReadCmd implements WebsocketCmd {
cmdId: number;
notifications: string[];
type = WsCmdType.MARK_NOTIFICATIONS_AS_READ;
constructor(ids: string[]) {
this.notifications = ids;
}
}
export class MarkAllAsReadCmd implements WebsocketCmd {
cmdId: number;
type = WsCmdType.MARK_ALL_NOTIFICATIONS_AS_READ;
}
export class EntityDataUnsubscribeCmd implements WebsocketCmd {
cmdId: number;
type = WsCmdType.ENTITY_DATA_UNSUBSCRIBE;
}
2021-03-02 12:04:45 +02:00
export class EntityCountUnsubscribeCmd implements WebsocketCmd {
cmdId: number;
type = WsCmdType.ENTITY_COUNT_UNSUBSCRIBE;
2021-03-02 12:04:45 +02:00
}
2020-07-03 18:33:06 +03:00
export class AlarmDataUnsubscribeCmd implements WebsocketCmd {
cmdId: number;
type = WsCmdType.ALARM_DATA_UNSUBSCRIBE;
2020-07-03 18:33:06 +03:00
}
export class AlarmCountUnsubscribeCmd implements WebsocketCmd {
cmdId: number;
type = WsCmdType.ALARM_COUNT_UNSUBSCRIBE;
}
2023-12-11 13:30:34 +02:00
export class UnsubscribeCmd implements WebsocketCmd {
cmdId: number;
type = WsCmdType.NOTIFICATIONS_UNSUBSCRIBE;
}
2023-12-08 18:05:47 +02:00
export class AuthCmd implements WebsocketCmd {
cmdId = 0;
type: WsCmdType.AUTH;
token: string;
constructor(token: string) {
this.token = token;
}
}
2023-02-16 12:59:43 +02:00
export class TelemetryPluginCmdsWrapper implements CmdWrapper {
2019-08-30 19:19:45 +03:00
constructor() {
this.cmds = [];
2019-08-30 19:19:45 +03:00
}
cmds: Array<WebsocketCmd>;
authCmd: AuthCmd;
private static popCmds<T>(cmds: Array<T>, leftCount: number): Array<T> {
const toPublish = Math.min(cmds.length, leftCount);
if (toPublish > 0) {
return cmds.splice(0, toPublish);
} else {
return [];
}
}
2019-08-30 19:19:45 +03:00
public setAuth(token: string) {
this.authCmd = new AuthCmd(token);
}
2019-08-30 19:19:45 +03:00
public hasCommands(): boolean {
return this.cmds.length > 0;
2019-08-30 19:19:45 +03:00
}
public clear() {
this.cmds.length = 0;
2019-08-30 19:19:45 +03:00
}
public preparePublishCommands(maxCommands: number): TelemetryPluginCmdsWrapper {
const preparedWrapper = new TelemetryPluginCmdsWrapper();
if (this.authCmd) {
preparedWrapper.authCmd = this.authCmd;
this.authCmd = null;
}
preparedWrapper.cmds = TelemetryPluginCmdsWrapper.popCmds(this.cmds, maxCommands);
2019-08-30 19:19:45 +03:00
return preparedWrapper;
}
}
export type SubscriptionDataEntry = [number, any, number?];
export interface SubscriptionData {
[key: string]: SubscriptionDataEntry[];
}
export interface IndexedSubscriptionData {
[id: number]: SubscriptionDataEntry[];
}
export interface SubscriptionDataHolder {
data: SubscriptionData;
}
export interface SubscriptionUpdateMsg extends SubscriptionDataHolder {
2019-08-30 19:19:45 +03:00
subscriptionId: number;
errorCode: number;
errorMsg: string;
}
2021-03-02 12:04:45 +02:00
export enum CmdUpdateType {
2020-07-03 18:33:06 +03:00
ENTITY_DATA = 'ENTITY_DATA',
2021-03-02 12:04:45 +02:00
ALARM_DATA = 'ALARM_DATA',
ALARM_COUNT_DATA = 'ALARM_COUNT_DATA',
2023-01-20 15:08:44 +02:00
COUNT_DATA = 'COUNT_DATA',
NOTIFICATIONS_COUNT = 'NOTIFICATIONS_COUNT',
NOTIFICATIONS = 'NOTIFICATIONS'
2020-07-03 18:33:06 +03:00
}
2021-03-02 12:04:45 +02:00
export interface CmdUpdateMsg {
cmdId: number;
errorCode: number;
errorMsg: string;
2021-03-02 12:04:45 +02:00
cmdUpdateType: CmdUpdateType;
}
export interface DataUpdateMsg<T> extends CmdUpdateMsg {
data?: PageData<T>;
update?: Array<T>;
2020-07-03 18:33:06 +03:00
}
export interface EntityDataUpdateMsg extends DataUpdateMsg<EntityData> {
2021-03-02 12:04:45 +02:00
cmdUpdateType: CmdUpdateType.ENTITY_DATA;
}
2020-07-03 18:33:06 +03:00
export interface AlarmDataUpdateMsg extends DataUpdateMsg<AlarmData> {
2021-03-02 12:04:45 +02:00
cmdUpdateType: CmdUpdateType.ALARM_DATA;
allowedEntities: number;
totalEntities: number;
2020-07-03 18:33:06 +03:00
}
2021-03-02 12:04:45 +02:00
export interface EntityCountUpdateMsg extends CmdUpdateMsg {
cmdUpdateType: CmdUpdateType.COUNT_DATA;
count: number;
}
export interface AlarmCountUpdateMsg extends CmdUpdateMsg {
cmdUpdateType: CmdUpdateType.ALARM_COUNT_DATA;
count: number;
}
2023-12-11 13:30:34 +02:00
export interface NotificationCountUpdateMsg extends CmdUpdateMsg {
cmdUpdateType: CmdUpdateType.NOTIFICATIONS_COUNT;
totalUnreadCount: number;
sequenceNumber: number;
}
export interface NotificationsUpdateMsg extends CmdUpdateMsg {
cmdUpdateType: CmdUpdateType.NOTIFICATIONS;
update?: Notification;
notifications?: Notification[];
totalUnreadCount: number;
sequenceNumber: number;
}
export type WebsocketDataMsg = AlarmDataUpdateMsg | AlarmCountUpdateMsg |
EntityDataUpdateMsg | EntityCountUpdateMsg | SubscriptionUpdateMsg | NotificationCountUpdateMsg | NotificationsUpdateMsg;
2023-02-06 13:09:43 +02:00
export const isEntityDataUpdateMsg = (message: WebsocketDataMsg): message is EntityDataUpdateMsg => {
2021-03-02 12:04:45 +02:00
const updateMsg = (message as CmdUpdateMsg);
return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.ENTITY_DATA;
2023-02-06 13:09:43 +02:00
};
2020-07-03 18:33:06 +03:00
2023-02-06 13:09:43 +02:00
export const isAlarmDataUpdateMsg = (message: WebsocketDataMsg): message is AlarmDataUpdateMsg => {
2021-03-02 12:04:45 +02:00
const updateMsg = (message as CmdUpdateMsg);
return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.ALARM_DATA;
2023-02-06 13:09:43 +02:00
};
2021-03-02 12:04:45 +02:00
2023-02-06 13:09:43 +02:00
export const isEntityCountUpdateMsg = (message: WebsocketDataMsg): message is EntityCountUpdateMsg => {
2021-03-02 12:04:45 +02:00
const updateMsg = (message as CmdUpdateMsg);
return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.COUNT_DATA;
2023-02-06 13:09:43 +02:00
};
export const isAlarmCountUpdateMsg = (message: WebsocketDataMsg): message is AlarmCountUpdateMsg => {
const updateMsg = (message as CmdUpdateMsg);
return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.ALARM_COUNT_DATA;
};
2023-12-11 13:30:34 +02:00
export const isNotificationCountUpdateMsg = (message: WebsocketDataMsg): message is NotificationCountUpdateMsg => {
const updateMsg = (message as CmdUpdateMsg);
return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.NOTIFICATIONS_COUNT;
};
export const isNotificationsUpdateMsg = (message: WebsocketDataMsg): message is NotificationsUpdateMsg => {
const updateMsg = (message as CmdUpdateMsg);
return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.NOTIFICATIONS;
};
2019-08-30 19:19:45 +03:00
export class SubscriptionUpdate implements SubscriptionUpdateMsg {
subscriptionId: number;
errorCode: number;
errorMsg: string;
data: SubscriptionData;
2019-08-30 19:19:45 +03:00
constructor(msg: SubscriptionUpdateMsg) {
this.subscriptionId = msg.subscriptionId;
this.errorCode = msg.errorCode;
this.errorMsg = msg.errorMsg;
this.data = msg.data;
}
public prepareData(keys: string[]) {
if (!this.data) {
this.data = {};
}
if (keys) {
keys.forEach((key) => {
if (!this.data[key]) {
this.data[key] = [];
}
});
}
}
public updateAttributeData(origData: Array<AttributeData>): Array<AttributeData> {
for (const key of Object.keys(this.data)) {
const keyData = this.data[key];
if (keyData.length) {
const existing = origData.find((data) => data.key === key);
if (existing) {
existing.lastUpdateTs = keyData[0][0];
existing.value = keyData[0][1];
} else {
origData.push(
{
key,
lastUpdateTs: keyData[0][0],
value: keyData[0][1]
}
);
}
}
}
return origData;
}
}
2021-03-02 12:04:45 +02:00
export class CmdUpdate implements CmdUpdateMsg {
cmdId: number;
errorCode: number;
errorMsg: string;
2021-03-02 12:04:45 +02:00
cmdUpdateType: CmdUpdateType;
2021-03-02 12:04:45 +02:00
constructor(msg: CmdUpdateMsg) {
this.cmdId = msg.cmdId;
this.errorCode = msg.errorCode;
this.errorMsg = msg.errorMsg;
2021-03-02 12:04:45 +02:00
this.cmdUpdateType = msg.cmdUpdateType;
}
}
export class DataUpdate<T> extends CmdUpdate implements DataUpdateMsg<T> {
data?: PageData<T>;
update?: Array<T>;
constructor(msg: DataUpdateMsg<T>) {
super(msg);
this.data = msg.data;
this.update = msg.update;
2020-07-03 18:33:06 +03:00
}
}
export class EntityDataUpdate extends DataUpdate<EntityData> {
constructor(msg: EntityDataUpdateMsg) {
super(msg);
}
2021-03-17 18:38:57 +02:00
private static processEntityData(data: Array<EntityData>, tsOffset: number) {
2021-03-17 18:38:57 +02:00
for (const entityData of data) {
if (entityData.timeseries) {
for (const key of Object.keys(entityData.timeseries)) {
const tsValues = entityData.timeseries[key];
for (const tsValue of tsValues) {
if (tsValue.ts) {
tsValue.ts += tsOffset;
}
2021-03-17 18:38:57 +02:00
}
}
}
if (entityData.latest) {
for (const entityKeyType of Object.keys(entityData.latest)) {
const keyTypeValues = entityData.latest[entityKeyType];
for (const key of Object.keys(keyTypeValues)) {
const tsValue = keyTypeValues[key];
if (tsValue.ts) {
tsValue.ts += tsOffset;
}
if (key === entityFields.createdTime.keyName && tsValue.value) {
tsValue.value = (Number(tsValue.value) + tsOffset) + '';
}
2021-03-17 18:38:57 +02:00
}
}
}
}
}
public prepareData(tsOffset: number) {
if (this.data) {
EntityDataUpdate.processEntityData(this.data.data, tsOffset);
}
if (this.update) {
EntityDataUpdate.processEntityData(this.update, tsOffset);
}
}
2020-07-03 18:33:06 +03:00
}
export class AlarmDataUpdate extends DataUpdate<AlarmData> {
2020-07-03 18:33:06 +03:00
constructor(msg: AlarmDataUpdateMsg) {
super(msg);
this.allowedEntities = msg.allowedEntities;
this.totalEntities = msg.totalEntities;
}
allowedEntities: number;
totalEntities: number;
private static processAlarmData(data: Array<AlarmData>, tsOffset: number) {
for (const alarmData of data) {
alarmData.createdTime += tsOffset;
if (alarmData.ackTs) {
alarmData.ackTs += tsOffset;
}
if (alarmData.clearTs) {
alarmData.clearTs += tsOffset;
}
if (alarmData.endTs) {
alarmData.endTs += tsOffset;
}
if (alarmData.latest) {
for (const entityKeyType of Object.keys(alarmData.latest)) {
const keyTypeValues = alarmData.latest[entityKeyType];
for (const key of Object.keys(keyTypeValues)) {
const tsValue = keyTypeValues[key];
if (tsValue.ts) {
tsValue.ts += tsOffset;
}
if (key in [entityFields.createdTime.keyName,
alarmFields.startTime.keyName,
alarmFields.endTime.keyName,
alarmFields.ackTime.keyName,
alarmFields.clearTime.keyName] && tsValue.value) {
tsValue.value = (Number(tsValue.value) + tsOffset) + '';
}
}
}
}
}
}
public prepareData(tsOffset: number) {
if (this.data) {
AlarmDataUpdate.processAlarmData(this.data.data, tsOffset);
}
if (this.update) {
AlarmDataUpdate.processAlarmData(this.update, tsOffset);
}
}
}
2021-03-02 12:04:45 +02:00
export class EntityCountUpdate extends CmdUpdate {
count: number;
constructor(msg: EntityCountUpdateMsg) {
super(msg);
this.count = msg.count;
}
}
export class AlarmCountUpdate extends CmdUpdate {
count: number;
constructor(msg: AlarmCountUpdateMsg) {
super(msg);
this.count = msg.count;
}
}
export class NotificationCountUpdate extends CmdUpdate {
totalUnreadCount: number;
sequenceNumber: number;
constructor(msg: NotificationCountUpdateMsg) {
super(msg);
this.totalUnreadCount = msg.totalUnreadCount;
this.sequenceNumber = msg.sequenceNumber;
}
}
export class NotificationsUpdate extends CmdUpdate {
totalUnreadCount: number;
sequenceNumber: number;
update?: Notification;
notifications?: Notification[];
constructor(msg: NotificationsUpdateMsg) {
super(msg);
this.totalUnreadCount = msg.totalUnreadCount;
this.sequenceNumber = msg.sequenceNumber;
this.update = msg.update;
this.notifications = msg.notifications;
}
}
2023-02-16 12:59:43 +02:00
export class TelemetrySubscriber extends WsSubscriber {
2019-08-30 19:19:45 +03:00
private dataSubject = new ReplaySubject<SubscriptionUpdate>(1);
private entityDataSubject = new ReplaySubject<EntityDataUpdate>(1);
2020-07-03 18:33:06 +03:00
private alarmDataSubject = new ReplaySubject<AlarmDataUpdate>(1);
2021-03-02 12:04:45 +02:00
private entityCountSubject = new ReplaySubject<EntityCountUpdate>(1);
private alarmCountSubject = new ReplaySubject<AlarmCountUpdate>(1);
private tsOffset = undefined;
2021-03-17 18:38:57 +02:00
2019-08-30 19:19:45 +03:00
public data$ = this.dataSubject.asObservable();
public entityData$ = this.entityDataSubject.asObservable();
2020-07-03 18:33:06 +03:00
public alarmData$ = this.alarmDataSubject.asObservable();
2021-03-02 12:04:45 +02:00
public entityCount$ = this.entityCountSubject.asObservable();
public alarmCount$ = this.alarmCountSubject.asObservable();
2019-08-30 19:19:45 +03:00
2023-02-16 12:59:43 +02:00
public static createEntityAttributesSubscription(telemetryService: TelemetryWebsocketService,
2019-08-30 19:19:45 +03:00
entityId: EntityId, attributeScope: TelemetryType,
zone: NgZone, keys: string[] = null): TelemetrySubscriber {
2019-08-30 19:19:45 +03:00
let subscriptionCommand: SubscriptionCmd;
if (attributeScope === LatestTelemetry.LATEST_TELEMETRY) {
subscriptionCommand = new TimeseriesSubscriptionCmd();
} else {
subscriptionCommand = new AttributesSubscriptionCmd();
}
subscriptionCommand.entityType = entityId.entityType as EntityType;
subscriptionCommand.entityId = entityId.id;
subscriptionCommand.scope = attributeScope as AttributeScope;
if (keys) {
subscriptionCommand.keys = keys.join(',');
}
2021-03-19 11:01:28 +02:00
const subscriber = new TelemetrySubscriber(telemetryService, zone);
2019-08-30 19:19:45 +03:00
subscriber.subscriptionCommands.push(subscriptionCommand);
return subscriber;
}
2023-04-04 17:27:00 +03:00
public static createEntityFilterLatestSubscription(telemetryService: TelemetryWebsocketService,
entityFilter: EntityFilter, zone: NgZone,
latestKeys: EntityKey[] = null): TelemetrySubscriber {
const entityDataQuery: EntityDataQuery = {
entityFilter,
pageLink: {
pageSize: 1,
page: 0
},
latestValues: latestKeys || []
};
const cmd = new EntityDataCmd();
cmd.query = entityDataQuery;
cmd.latestCmd = {
keys: latestKeys || []
};
const subscriber = new TelemetrySubscriber(telemetryService, zone);
subscriber.subscriptionCommands.push(cmd);
return subscriber;
}
2023-02-16 12:59:43 +02:00
constructor(private telemetryService: TelemetryWebsocketService, protected zone?: NgZone) {
super(telemetryService, zone);
}
public complete() {
2019-08-30 19:19:45 +03:00
this.dataSubject.complete();
this.entityDataSubject.complete();
2020-07-03 18:33:06 +03:00
this.alarmDataSubject.complete();
2021-03-02 12:04:45 +02:00
this.entityCountSubject.complete();
this.alarmCountSubject.complete();
2023-02-16 12:59:43 +02:00
super.complete();
2019-08-30 19:19:45 +03:00
}
public setTsOffset(tsOffset: number): boolean {
if (this.tsOffset !== tsOffset) {
const changed = !isUndefined(this.tsOffset);
this.tsOffset = tsOffset;
return changed;
} else {
return false;
}
2021-03-17 18:38:57 +02:00
}
2019-08-30 19:19:45 +03:00
public onData(message: SubscriptionUpdate) {
const cmdId = message.subscriptionId;
let keys: string[];
const cmd = this.subscriptionCommands.find((command) => command.cmdId === cmdId);
if (cmd) {
const telemetryPluginCmd = cmd as TelemetryPluginCmd;
if (telemetryPluginCmd.keys && telemetryPluginCmd.keys.length) {
keys = telemetryPluginCmd.keys.split(',');
2019-08-30 19:19:45 +03:00
}
}
message.prepareData(keys);
if (this.zone) {
this.zone.run(
() => {
this.dataSubject.next(message);
}
);
} else {
this.dataSubject.next(message);
}
2019-08-30 19:19:45 +03:00
}
public onEntityData(message: EntityDataUpdate) {
2021-03-17 18:38:57 +02:00
if (this.tsOffset) {
message.prepareData(this.tsOffset);
}
if (this.zone) {
this.zone.run(
() => {
this.entityDataSubject.next(message);
}
);
} else {
this.entityDataSubject.next(message);
}
}
2020-07-03 18:33:06 +03:00
public onAlarmData(message: AlarmDataUpdate) {
if (this.tsOffset) {
message.prepareData(this.tsOffset);
}
2020-07-03 18:33:06 +03:00
if (this.zone) {
this.zone.run(
() => {
this.alarmDataSubject.next(message);
}
);
} else {
this.alarmDataSubject.next(message);
}
}
2021-03-02 12:04:45 +02:00
public onEntityCount(message: EntityCountUpdate) {
if (this.zone) {
this.zone.run(
() => {
this.entityCountSubject.next(message);
}
);
} else {
this.entityCountSubject.next(message);
}
}
public onAlarmCount(message: AlarmCountUpdate) {
if (this.zone) {
this.zone.run(
() => {
this.alarmCountSubject.next(message);
}
);
} else {
this.alarmCountSubject.next(message);
}
}
2019-08-30 19:19:45 +03:00
public attributeData$(): Observable<Array<AttributeData>> {
const attributeData = new Array<AttributeData>();
return this.data$.pipe(
map((message) => message.updateAttributeData(attributeData))
);
}
}
2023-12-11 13:30:34 +02:00
export class NotificationSubscriber extends WsSubscriber {
private notificationCountSubject = new BehaviorSubject<NotificationCountUpdate>({
cmdId: 0,
cmdUpdateType: undefined,
errorCode: 0,
errorMsg: '',
totalUnreadCount: 0,
sequenceNumber: 0
});
private notificationsSubject = new BehaviorSubject<NotificationsUpdate>({
cmdId: 0,
cmdUpdateType: undefined,
errorCode: 0,
errorMsg: '',
notifications: null,
totalUnreadCount: 0,
sequenceNumber: 0
});
public messageLimit = 10;
2024-07-03 14:36:58 +03:00
public notificationType = [];
2023-12-11 13:30:34 +02:00
public notificationCount$ = this.notificationCountSubject.asObservable().pipe(map(msg => msg.totalUnreadCount));
public notifications$ = this.notificationsSubject.asObservable().pipe(map(msg => msg.notifications ));
public static createNotificationCountSubscription(websocketService: WebsocketService<WsSubscriber>,
zone: NgZone): NotificationSubscriber {
const subscriptionCommand = new UnreadCountSubCmd();
const subscriber = new NotificationSubscriber(websocketService, zone);
subscriber.subscriptionCommands.push(subscriptionCommand);
return subscriber;
}
public static createNotificationsSubscription(websocketService: WebsocketService<WsSubscriber>,
2024-07-03 14:36:58 +03:00
zone: NgZone, limit = 10, types: Array<NotificationType> = []): NotificationSubscriber {
const subscriptionCommand = new UnreadSubCmd(limit, types);
2023-12-11 13:30:34 +02:00
const subscriber = new NotificationSubscriber(websocketService, zone);
subscriber.messageLimit = limit;
subscriber.subscriptionCommands.push(subscriptionCommand);
return subscriber;
}
public static createMarkAsReadCommand(websocketService: WebsocketService<WsSubscriber>,
ids: string[]): NotificationSubscriber {
const subscriptionCommand = new MarkAsReadCmd(ids);
const subscriber = new NotificationSubscriber(websocketService);
subscriber.subscriptionCommands.push(subscriptionCommand);
return subscriber;
}
public static createMarkAllAsReadCommand(websocketService: WebsocketService<WsSubscriber>): NotificationSubscriber {
const subscriptionCommand = new MarkAllAsReadCmd();
const subscriber = new NotificationSubscriber(websocketService);
subscriber.subscriptionCommands.push(subscriptionCommand);
return subscriber;
}
constructor(private websocketService: WsService<any>, protected zone?: NgZone) {
super(websocketService, zone);
}
onNotificationCountUpdate(message: NotificationCountUpdate) {
const currentNotificationCount = this.notificationCountSubject.value;
if (message.sequenceNumber <= currentNotificationCount.sequenceNumber) {
return;
}
if (this.zone) {
this.zone.run(
() => {
this.notificationCountSubject.next(message);
}
);
} else {
this.notificationCountSubject.next(message);
}
}
public complete() {
this.notificationCountSubject.complete();
this.notificationsSubject.complete();
super.complete();
}
onNotificationsUpdate(message: NotificationsUpdate) {
const currentNotifications = this.notificationsSubject.value;
if (message.sequenceNumber <= currentNotifications.sequenceNumber) {
message.totalUnreadCount = currentNotifications.totalUnreadCount;
}
let processMessage = message;
if (isDefinedAndNotNull(currentNotifications) && message.update) {
currentNotifications.notifications.unshift(message.update);
if (currentNotifications.notifications.length > this.messageLimit) {
currentNotifications.notifications.pop();
}
processMessage = currentNotifications;
processMessage.totalUnreadCount = message.totalUnreadCount;
}
if (this.zone) {
this.zone.run(
() => {
this.notificationsSubject.next(processMessage);
this.notificationCountSubject.next(processMessage);
}
);
} else {
this.notificationsSubject.next(processMessage);
this.notificationCountSubject.next(processMessage);
}
}
}