From 2a2a5583499bcc09afb5adeddc8da288f5e84f07 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 21 Nov 2024 17:00:02 +0200 Subject: [PATCH 1/2] fixed alarmStatusWs command --- .../service/subscription/TbAlarmStatusSubCtx.java | 12 +++++------- .../subscription/TbAlarmStatusSubscription.java | 12 ++++-------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmStatusSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmStatusSubCtx.java index fb330085a7..29e7d99688 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmStatusSubCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmStatusSubCtx.java @@ -15,21 +15,16 @@ */ package org.thingsboard.server.service.subscription; -import lombok.Getter; -import lombok.Setter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.alarm.AlarmInfo; -import org.thingsboard.server.common.data.query.AlarmCountQuery; import org.thingsboard.server.common.data.query.OriginatorAlarmFilter; import org.thingsboard.server.dao.alarm.AlarmService; -import org.thingsboard.server.dao.attributes.AttributesService; -import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.ws.WebSocketService; import org.thingsboard.server.service.ws.WebSocketSessionRef; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmStatusCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmStatusUpdate; import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; import java.util.List; @@ -82,7 +77,10 @@ public class TbAlarmStatusSubCtx extends TbAbstractSubCtx { } public void sendUpdate() { - sendWsMsg(subscription.createUpdate()); + sendWsMsg(AlarmStatusUpdate.builder() + .cmdId(cmdId) + .active(subscription.hasAlarms()) + .build()); } public void fetchActiveAlarms() { diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmStatusSubscription.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmStatusSubscription.java index 6e80d817d7..f153178fa0 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmStatusSubscription.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmStatusSubscription.java @@ -22,7 +22,6 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmStatusUpdate; import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; import java.util.HashSet; @@ -53,15 +52,12 @@ public class TbAlarmStatusSubscription extends TbSubscription Date: Thu, 21 Nov 2024 17:22:07 +0200 Subject: [PATCH 2/2] UI: Add new behavior "get alarm status" --- .../server/service/ws/WsCommandsWrapper.java | 2 + .../core/ws/telemetry-websocket.service.ts | 10 ++ .../widget/lib/action/action-widget.models.ts | 56 +++++++++- .../lib/button/action-button-widget.models.ts | 8 ++ .../button/command-button-widget.models.ts | 4 + .../lib/button/toggle-button-widget.models.ts | 8 ++ .../lib/indicator/status-widget.models.ts | 8 ++ .../lib/rpc/power-button-widget.models.ts | 8 ++ .../lib/rpc/single-switch-widget.models.ts | 8 ++ .../widget/lib/rpc/slider-widget.models.ts | 8 ++ .../widget/lib/scada/scada-symbol.models.ts | 4 + ...value-action-settings-panel.component.html | 25 ++++- ...t-value-action-settings-panel.component.ts | 17 ++- .../get-value-action-settings.component.ts | 3 + .../models/action-widget-settings.models.ts | 9 ++ .../models/telemetry/telemetry.models.ts | 103 +++++++++++++++++- .../assets/locale/locale.constant-en_US.json | 5 +- 17 files changed, 280 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/ws/WsCommandsWrapper.java b/application/src/main/java/org/thingsboard/server/service/ws/WsCommandsWrapper.java index b79d6b2a13..183292bb40 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/WsCommandsWrapper.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/WsCommandsWrapper.java @@ -38,6 +38,7 @@ import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUnsubscribeCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUnsubscribeCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmStatusUnsubscribeCmd; import java.util.List; @@ -67,6 +68,7 @@ public class WsCommandsWrapper { @Type(name = "ENTITY_DATA_UNSUBSCRIBE", value = EntityDataUnsubscribeCmd.class), @Type(name = "ENTITY_COUNT_UNSUBSCRIBE", value = EntityCountUnsubscribeCmd.class), @Type(name = "NOTIFICATIONS_UNSUBSCRIBE", value = NotificationsUnsubCmd.class), + @Type(name = "ALARM_STATUS_UNSUBSCRIBE", value = AlarmStatusUnsubscribeCmd.class), }) private List cmds; diff --git a/ui-ngx/src/app/core/ws/telemetry-websocket.service.ts b/ui-ngx/src/app/core/ws/telemetry-websocket.service.ts index 5c2c383bd9..4f0d8472dc 100644 --- a/ui-ngx/src/app/core/ws/telemetry-websocket.service.ts +++ b/ui-ngx/src/app/core/ws/telemetry-websocket.service.ts @@ -22,6 +22,9 @@ import { AlarmDataCmd, AlarmDataUnsubscribeCmd, AlarmDataUpdate, + AlarmStatusCmd, + AlarmStatusUnsubscribeCmd, + AlarmStatusUpdate, EntityCountCmd, EntityCountUnsubscribeCmd, EntityCountUpdate, @@ -30,6 +33,7 @@ import { EntityDataUpdate, isAlarmCountUpdateMsg, isAlarmDataUpdateMsg, + isAlarmStatusUpdateMsg, isEntityCountUpdateMsg, isEntityDataUpdateMsg, isNotificationCountUpdateMsg, @@ -121,6 +125,10 @@ export class TelemetryWebsocketService extends WebsocketService extends ValueAction { return new AttributeValueGetter(ctx, settings, valueType, valueObserver, simulated); case GetValueAction.GET_TIME_SERIES: return new TimeSeriesValueGetter(ctx, settings, valueType, valueObserver, simulated); + case GetValueAction.GET_ALARM_STATUS: + return new AlarmStatusValueGetter(ctx, settings, valueType, valueObserver, simulated); case GetValueAction.GET_DASHBOARD_STATE: return new DashboardStateGetter(ctx, settings, valueType, valueObserver, simulated); } @@ -257,7 +259,7 @@ export abstract class ValueGetter extends ValueAction { protected valueObserver: Partial>, protected simulated: boolean) { super(ctx, settings); - if (this.settings.action !== GetValueAction.DO_NOTHING) { + if (this.settings.action !== GetValueAction.DO_NOTHING && this.settings.action !== GetValueAction.GET_ALARM_STATUS) { this.dataConverter = new DataToValueConverter(settings.dataToValue, valueType); } } @@ -537,6 +539,58 @@ export class TimeSeriesValueGetter extends TelemetryValueGetter extends ValueGetter { + + protected targetEntityId: EntityId; + private telemetrySubscriber: SharedTelemetrySubscriber; + + constructor(protected ctx: WidgetContext, + protected settings: GetValueSettings, + protected valueType: ValueType, + protected valueObserver: Partial>, + protected simulated: boolean) { + super(ctx, settings, valueType, valueObserver, simulated); + const entityInfo = this.ctx.defaultSubscription.getFirstEntityInfo(); + this.targetEntityId = entityInfo?.entityId; + } + + protected doGetValue(): Observable { + if (this.simulated) { + return of(false).pipe(delay(100)); + } else { + if (!this.targetEntityId && !this.ctx.defaultSubscription.rpcEnabled) { + return throwError(() => new Error(this.ctx.translate.instant('widgets.value-action.error.target-entity-is-not-set'))); + } + if (this.targetEntityId) { + return this.subscribeForTelemetryValue(); + } else { + return of(null); + } + } + } + + private subscribeForTelemetryValue(): Observable { + this.telemetrySubscriber = + SharedTelemetrySubscriber.createAlarmStatusSubscription(this.ctx.telemetryWsService, this.targetEntityId, + this.ctx.ngZone, this.settings.getAlarmStatus.severityList, this.settings.getAlarmStatus.typeList); + this.telemetrySubscriber.subscribe(); + return this.telemetrySubscriber.alarmStatus$.pipe( + map((data) => { + return data.active; + }) + ); + } + + + destroy() { + if (this.telemetrySubscriber) { + this.telemetrySubscriber.unsubscribe(); + this.telemetrySubscriber = null; + } + super.destroy(); + } +} + export class DashboardStateGetter extends ValueGetter { constructor(protected ctx: WidgetContext, protected settings: GetValueSettings, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.models.ts index 4b3fafa55c..fd2c0bdcdc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.models.ts @@ -38,6 +38,10 @@ export const actionButtonDefaultSettings: ActionButtonWidgetSettings = { getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, @@ -54,6 +58,10 @@ export const actionButtonDefaultSettings: ActionButtonWidgetSettings = { getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.models.ts index a365e6929c..93484b5574 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.models.ts @@ -62,6 +62,10 @@ export const commandButtonDefaultSettings: CommandButtonWidgetSettings = { getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/toggle-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/button/toggle-button-widget.models.ts index 12e115792b..3ece34a82f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/button/toggle-button-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/toggle-button-widget.models.ts @@ -61,6 +61,10 @@ export const toggleButtonDefaultSettings: ToggleButtonWidgetSettings = { getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, @@ -77,6 +81,10 @@ export const toggleButtonDefaultSettings: ToggleButtonWidgetSettings = { getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/status-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/status-widget.models.ts index 4dbea591f9..1097842b4b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/status-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/status-widget.models.ts @@ -85,6 +85,10 @@ export const statusWidgetDefaultSettings: StatusWidgetSettings = { getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, @@ -101,6 +105,10 @@ export const statusWidgetDefaultSettings: StatusWidgetSettings = { getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts index 12e2a1065b..503347939d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts @@ -104,6 +104,10 @@ export const powerButtonDefaultSettings: PowerButtonWidgetSettings = { getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, @@ -120,6 +124,10 @@ export const powerButtonDefaultSettings: PowerButtonWidgetSettings = { getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.models.ts index 62aae25816..f186d4d58c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.models.ts @@ -100,6 +100,10 @@ export const singleSwitchDefaultSettings: SingleSwitchWidgetSettings = { getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, @@ -116,6 +120,10 @@ export const singleSwitchDefaultSettings: SingleSwitchWidgetSettings = { getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.models.ts index 3df4359dce..980aff2f0f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.models.ts @@ -101,6 +101,10 @@ export const sliderWidgetDefaultSettings: SliderWidgetSettings = { getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, @@ -117,6 +121,10 @@ export const sliderWidgetDefaultSettings: SliderWidgetSettings = { getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts index 6d3e9fde82..8a782a14a2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts @@ -415,6 +415,10 @@ export const defaultGetValueSettings = (valueType: ValueType): GetValueSettings< getTimeSeries: { key: 'state' }, + getAlarmStatus: { + severityList: null, + typeList: null + }, dataToValue: { type: DataToValueType.NONE, compareToValue: true, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.html index 4e5e6709a6..87a81db0eb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.html @@ -103,8 +103,31 @@ + + +
+
alarm.alarm-severity
+ + + {{ alarmSeverityTranslationMap.get(alarmSeverity) | translate }} + + +
+
+
alarm.alarm-types
+ + +
+
+
-
widgets.value-action.action-result-converter
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.ts index bf19d3e37a..6abc51b003 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.ts @@ -33,6 +33,8 @@ import { TargetDevice, widgetType } from '@shared/models/widget.models'; import { AttributeScope, DataKeyType, telemetryTypeTranslationsShort } from '@shared/models/telemetry/telemetry.models'; import { IAliasController } from '@core/api/widget-api.models'; import { WidgetService } from '@core/http/widget.service'; +import { AlarmSeverity, alarmSeverityTranslations } from '@shared/models/alarm.models'; +import { EntityType } from '@shared/models/entity-type.models'; @Component({ selector: 'tb-get-value-action-settings-panel', @@ -96,6 +98,9 @@ export class GetValueActionSettingsPanelComponent extends PageComponent implemen getValueSettingsFormGroup: UntypedFormGroup; + alarmSeverities = Object.keys(AlarmSeverity) as AlarmSeverity[]; + alarmSeverityTranslationMap = alarmSeverityTranslations; + constructor(private fb: UntypedFormBuilder, private widgetService: WidgetService, protected store: Store) { @@ -122,6 +127,10 @@ export class GetValueActionSettingsPanelComponent extends PageComponent implemen getTimeSeries: this.fb.group({ key: [this.getValueSettings?.getTimeSeries?.key, [Validators.required]] }), + getAlarmStatus: this.fb.group({ + severityList: [this.getValueSettings?.getAlarmStatus?.severityList], + typeList: [this.getValueSettings?.getAlarmStatus?.typeList] + }), dataToValue: this.fb.group({ type: [this.getValueSettings?.dataToValue?.type, [Validators.required]], dataToValueFunction: [this.getValueSettings?.dataToValue?.dataToValueFunction, [Validators.required]], @@ -159,6 +168,7 @@ export class GetValueActionSettingsPanelComponent extends PageComponent implemen this.getValueSettingsFormGroup.get('executeRpc').disable({emitEvent: false}); this.getValueSettingsFormGroup.get('getAttribute').disable({emitEvent: false}); this.getValueSettingsFormGroup.get('getTimeSeries').disable({emitEvent: false}); + this.getValueSettingsFormGroup.get('getAlarmStatus').disable({emitEvent: false}); switch (action) { case GetValueAction.DO_NOTHING: this.getValueSettingsFormGroup.get('defaultValue').enable({emitEvent: false}); @@ -178,8 +188,11 @@ export class GetValueActionSettingsPanelComponent extends PageComponent implemen case GetValueAction.GET_TIME_SERIES: this.getValueSettingsFormGroup.get('getTimeSeries').enable({emitEvent: false}); break; + case GetValueAction.GET_ALARM_STATUS: + this.getValueSettingsFormGroup.get('getAlarmStatus').enable({emitEvent: false}); + break; } - if (action === GetValueAction.DO_NOTHING) { + if (action === GetValueAction.DO_NOTHING || action === GetValueAction.GET_ALARM_STATUS) { this.getValueSettingsFormGroup.get('dataToValue').disable({emitEvent: false}); } else { this.getValueSettingsFormGroup.get('dataToValue').enable({emitEvent: false}); @@ -190,4 +203,6 @@ export class GetValueActionSettingsPanelComponent extends PageComponent implemen } } } + + protected readonly entityType = EntityType; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings.component.ts index 3a9f079db8..871923e5b7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings.component.ts @@ -176,6 +176,9 @@ export class GetValueActionSettingsComponent implements OnInit, ControlValueAcce case GetValueAction.GET_TIME_SERIES: this.displayValue = this.translate.instant('widgets.value-action.get-time-series-text', {key: this.modelValue.getTimeSeries.key}); break; + case GetValueAction.GET_ALARM_STATUS: + this.displayValue = this.translate.instant('widgets.value-action.get-alarm-status-text'); + break; case GetValueAction.GET_DASHBOARD_STATE: if (this.valueType === ValueType.BOOLEAN) { const state = this.modelValue.dataToValue?.compareToValue; diff --git a/ui-ngx/src/app/shared/models/action-widget-settings.models.ts b/ui-ngx/src/app/shared/models/action-widget-settings.models.ts index e7b962b34f..fc03ef2eb9 100644 --- a/ui-ngx/src/app/shared/models/action-widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/action-widget-settings.models.ts @@ -16,12 +16,14 @@ import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { widgetType } from '@shared/models/widget.models'; +import { AlarmSeverity } from '@shared/models/alarm.models'; export enum GetValueAction { DO_NOTHING = 'DO_NOTHING', EXECUTE_RPC = 'EXECUTE_RPC', GET_ATTRIBUTE = 'GET_ATTRIBUTE', GET_TIME_SERIES = 'GET_TIME_SERIES', + GET_ALARM_STATUS = 'GET_ALARM_STATUS', GET_DASHBOARD_STATE = 'GET_DASHBOARD_STATE' } @@ -41,6 +43,7 @@ export const getValueActionTranslations = new Map( [GetValueAction.EXECUTE_RPC, 'widgets.value-action.execute-rpc'], [GetValueAction.GET_ATTRIBUTE, 'widgets.value-action.get-attribute'], [GetValueAction.GET_TIME_SERIES, 'widgets.value-action.get-time-series'], + [GetValueAction.GET_ALARM_STATUS, 'widgets.value-action.get-alarm-status'], [GetValueAction.GET_DASHBOARD_STATE, 'widgets.value-action.get-dashboard-state'] ] ); @@ -52,6 +55,11 @@ export interface RpcSettings { persistentPollingInterval: number; } +export interface AlarmStatusSettings { + severityList: Array; + typeList: Array; +} + export interface TelemetryValueSettings { key: string; } @@ -85,6 +93,7 @@ export interface GetValueSettings extends ValueActionSettings { executeRpc?: RpcSettings; getAttribute: GetAttributeValueSettings; getTimeSeries: TelemetryValueSettings; + getAlarmStatus: AlarmStatusSettings; dataToValue: DataToValueSettings; } diff --git a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts index 2db1983b0b..827708351e 100644 --- a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts +++ b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts @@ -33,9 +33,9 @@ import { TsValue } from '@shared/models/query/query.models'; import { PageData } from '@shared/models/page/page-data'; -import { alarmFields } from '@shared/models/alarm.models'; +import { alarmFields, AlarmSeverity } from '@shared/models/alarm.models'; import { entityFields } from '@shared/models/entity.models'; -import { isDefinedAndNotNull, isUndefined } from '@core/utils'; +import { deepClone, isDefinedAndNotNull, isUndefined } from '@core/utils'; import { CmdWrapper, WsService, WsSubscriber } from '@shared/models/websocket/websocket.models'; import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; import { Notification, NotificationType } from '@shared/models/notification.models'; @@ -141,6 +141,7 @@ export enum WsCmdType { ENTITY_COUNT = 'ENTITY_COUNT', ALARM_DATA = 'ALARM_DATA', ALARM_COUNT = 'ALARM_COUNT', + ALARM_STATUS = 'ALARM_STATUS', NOTIFICATIONS = 'NOTIFICATIONS', NOTIFICATIONS_COUNT = 'NOTIFICATIONS_COUNT', @@ -149,6 +150,7 @@ export enum WsCmdType { ALARM_DATA_UNSUBSCRIBE = 'ALARM_DATA_UNSUBSCRIBE', ALARM_COUNT_UNSUBSCRIBE = 'ALARM_COUNT_UNSUBSCRIBE', + ALARM_STATUS_UNSUBSCRIBE = 'ALARM_STATUS_UNSUBSCRIBE', ENTITY_DATA_UNSUBSCRIBE = 'ENTITY_DATA_UNSUBSCRIBE', ENTITY_COUNT_UNSUBSCRIBE = 'ENTITY_COUNT_UNSUBSCRIBE', NOTIFICATIONS_UNSUBSCRIBE = 'NOTIFICATIONS_UNSUBSCRIBE' @@ -298,6 +300,14 @@ export class AlarmCountCmd implements WebsocketCmd { type = WsCmdType.ALARM_COUNT; } +export class AlarmStatusCmd implements WebsocketCmd { + cmdId: number; + originatorId: EntityId; + severityList?: Array; + typeList?: Array; + type = WsCmdType.ALARM_STATUS; +} + export class UnreadCountSubCmd implements WebsocketCmd { cmdId: number; type = WsCmdType.NOTIFICATIONS_COUNT; @@ -352,6 +362,11 @@ export class AlarmCountUnsubscribeCmd implements WebsocketCmd { type = WsCmdType.ALARM_COUNT_UNSUBSCRIBE; } +export class AlarmStatusUnsubscribeCmd implements WebsocketCmd { + cmdId: number; + type = WsCmdType.ALARM_STATUS_UNSUBSCRIBE; +} + export class UnsubscribeCmd implements WebsocketCmd { cmdId: number; type = WsCmdType.NOTIFICATIONS_UNSUBSCRIBE; @@ -432,6 +447,7 @@ export enum CmdUpdateType { ENTITY_DATA = 'ENTITY_DATA', ALARM_DATA = 'ALARM_DATA', ALARM_COUNT_DATA = 'ALARM_COUNT_DATA', + ALARM_STATUS = 'ALARM_STATUS', COUNT_DATA = 'COUNT_DATA', NOTIFICATIONS_COUNT = 'NOTIFICATIONS_COUNT', NOTIFICATIONS = 'NOTIFICATIONS' @@ -469,6 +485,11 @@ export interface AlarmCountUpdateMsg extends CmdUpdateMsg { count: number; } +export interface AlarmStatusUpdateMsg extends CmdUpdateMsg { + cmdUpdateType: CmdUpdateType.ALARM_STATUS; + active: boolean; +} + export interface NotificationCountUpdateMsg extends CmdUpdateMsg { cmdUpdateType: CmdUpdateType.NOTIFICATIONS_COUNT; totalUnreadCount: number; @@ -506,6 +527,11 @@ export const isAlarmCountUpdateMsg = (message: WebsocketDataMsg): message is Ala return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.ALARM_COUNT_DATA; }; +export const isAlarmStatusUpdateMsg = (message: WebsocketDataMsg): message is AlarmCountUpdateMsg => { + const updateMsg = (message as CmdUpdateMsg); + return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.ALARM_STATUS; +}; + export const isNotificationCountUpdateMsg = (message: WebsocketDataMsg): message is NotificationCountUpdateMsg => { const updateMsg = (message as CmdUpdateMsg); return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.NOTIFICATIONS_COUNT; @@ -705,6 +731,15 @@ export class AlarmCountUpdate extends CmdUpdate { } } +export class AlarmStatusUpdate extends CmdUpdate { + active: boolean; + + constructor(msg: AlarmStatusUpdateMsg) { + super(msg); + this.active = msg.active; + } +} + export class NotificationCountUpdate extends CmdUpdate { totalUnreadCount: number; sequenceNumber: number; @@ -750,14 +785,29 @@ export class SharedTelemetrySubscriber { return key; } + private static createAlarmStatusSubscriberKey (entityId: EntityId, severityList: AlarmSeverity[] = null, typeList: string[] = null): string { + let key = entityId.entityType + '_' + entityId.id; + if (severityList) { + key += '_' + severityList.sort().join('_'); + } + if (typeList) { + key += '_' + typeList.sort().join('_'); + } + return key; + } + private subscribed = false; private attributeDataSubject = connectable(this.sharedSubscriptionInfo.subscriber.attributeData$(), { connector: () => new ReplaySubject>(1)}); + private alarmStatusSubject = connectable(this.sharedSubscriptionInfo.subscriber.alarmStatus$, + { connector: () => new ReplaySubject()}); + private subscriptions = new Array(); public attributeData$: Observable> = this.attributeDataSubject; //this.attributeDataSubject.asObservable(); + public alarmStatus$: Observable = this.alarmStatusSubject; public static createEntityAttributesSubscription(telemetryService: TelemetryWebsocketService, entityId: EntityId, attributeScope: TelemetryType, @@ -781,6 +831,28 @@ export class SharedTelemetrySubscriber { return sharedSubscriber; } + public static createAlarmStatusSubscription(telemetryService: TelemetryWebsocketService, + entityId: EntityId, zone: NgZone, severityList: AlarmSeverity[] = null, + typeList: string[] = null): SharedTelemetrySubscriber { + const key = SharedTelemetrySubscriber.createAlarmStatusSubscriberKey(entityId, severityList, typeList); + let info = SharedTelemetrySubscriber.subscribersCache[key]; + if (!info) { + const subscriber = TelemetrySubscriber.createAlarmStatusSubscription( + telemetryService, entityId, zone, severityList, typeList + ); + info = { + key, + subscriber, + subscribed: false, + sharedSubscribers: new Set() + }; + SharedTelemetrySubscriber.subscribersCache[key] = info; + } + const sharedSubscriber = new SharedTelemetrySubscriber(info); + info.sharedSubscribers.add(sharedSubscriber); + return sharedSubscriber; + } + private constructor(private sharedSubscriptionInfo: SharedSubscriptionInfo) { } @@ -788,6 +860,7 @@ export class SharedTelemetrySubscriber { if (!this.subscribed) { this.subscribed = true; this.subscriptions.push(this.attributeDataSubject.connect()); + this.subscriptions.push(this.alarmStatusSubject.connect()); if (!this.sharedSubscriptionInfo.subscribed) { this.sharedSubscriptionInfo.subscriber.subscribe(); this.sharedSubscriptionInfo.subscribed = true; @@ -823,6 +896,7 @@ export class TelemetrySubscriber extends WsSubscriber { private alarmDataSubject = new ReplaySubject(1); private entityCountSubject = new ReplaySubject(1); private alarmCountSubject = new ReplaySubject(1); + private alarmStatusSubject = new ReplaySubject(1); private tsOffset = undefined; public data$ = this.dataSubject.asObservable(); @@ -830,6 +904,7 @@ export class TelemetrySubscriber extends WsSubscriber { public alarmData$ = this.alarmDataSubject.asObservable(); public entityCount$ = this.entityCountSubject.asObservable(); public alarmCount$ = this.alarmCountSubject.asObservable(); + public alarmStatus$ = this.alarmStatusSubject.asObservable(); public static createEntityAttributesSubscription(telemetryService: TelemetryWebsocketService, entityId: EntityId, attributeScope: TelemetryType, @@ -851,6 +926,17 @@ export class TelemetrySubscriber extends WsSubscriber { return subscriber; } + public static createAlarmStatusSubscription(telemetryService: TelemetryWebsocketService, entityId: EntityId, + zone: NgZone, severityList: AlarmSeverity[] = null, typeList: string[] = null): TelemetrySubscriber { + const subscriptionCommand = new AlarmStatusCmd(); + subscriptionCommand.originatorId = deepClone(entityId); + subscriptionCommand.severityList = severityList; + subscriptionCommand.typeList = typeList; + const subscriber = new TelemetrySubscriber(telemetryService, zone); + subscriber.subscriptionCommands.push(subscriptionCommand); + return subscriber; + } + public static createEntityFilterLatestSubscription(telemetryService: TelemetryWebsocketService, entityFilter: EntityFilter, zone: NgZone, latestKeys: EntityKey[] = null): TelemetrySubscriber { @@ -882,6 +968,7 @@ export class TelemetrySubscriber extends WsSubscriber { this.alarmDataSubject.complete(); this.entityCountSubject.complete(); this.alarmCountSubject.complete(); + this.alarmStatusSubject.complete(); super.complete(); } @@ -971,6 +1058,18 @@ export class TelemetrySubscriber extends WsSubscriber { } } + public onAlarmStatus(message: AlarmStatusUpdate) { + if (this.zone) { + this.zone.run( + () => { + this.alarmStatusSubject.next(message); + } + ); + } else { + this.alarmStatusSubject.next(message); + } + } + public attributeData$(): Observable> { const attributeData = new Array(); return this.data$.pipe( diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 5addc4b6c2..7fdea5d5d9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -596,6 +596,7 @@ "ack-time": "Acknowledged time", "clear-time": "Cleared time", "duration": "Duration", + "alarm-severity": "Alarm severity", "alarm-severity-list": "Alarm severity list", "any-severity": "Any severity", "severity-critical": "Critical", @@ -634,6 +635,7 @@ "fetch-size": "Fetch size", "fetch-size-required": "Fetch size is required.", "fetch-size-error-min": "Minimum value is 10.", + "alarm-types": "Alarm types", "alarm-type-list": "Alarm type list", "any-type": "Any type", "assigned-to-current-user": "Assigned to current user", @@ -7352,11 +7354,12 @@ "get-attribute": "Get attribute", "set-attribute": "Set attribute", "get-time-series": "Get time series", + "get-alarm-status": "Get alarm status", "get-dashboard-state": "Get dashboard state", "add-time-series": "Add time series", "execute-rpc-text": "Execute RPC method '{{methodName}}'", "get-attribute-text": "Use attribute '{{key}}'", - "get-time-series-text": "Use time series '{{key}}'", + "get-alarm-status-text": "Use alarm status", "get-dashboard-state-text": "Use dashboard state", "when-dashboard-state-is-text": "When dashboard state is '{{state}}'", "when-dashboard-state-function-is-text": "When f(dashboard state) is '{{state}}'",