Merge pull request #12093 from dashevchenko/alarmStatusCmdFix
Add new behavior "get alarm status"
This commit is contained in:
commit
eae6909be8
@ -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() {
|
||||
|
||||
@ -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<AlarmSubscriptionU
|
||||
this.severityList = severityList;
|
||||
}
|
||||
|
||||
public AlarmStatusUpdate createUpdate() {
|
||||
return AlarmStatusUpdate.builder()
|
||||
.cmdId(getSubscriptionId())
|
||||
.active(!alarmIds.isEmpty())
|
||||
.build();
|
||||
}
|
||||
|
||||
public boolean matches(AlarmInfo alarm) {
|
||||
return !alarm.isCleared() && (this.typeList == null || this.typeList.contains(alarm.getType())) &&
|
||||
(this.severityList == null || this.severityList.contains(alarm.getSeverity()));
|
||||
}
|
||||
|
||||
public boolean hasAlarms() {
|
||||
return !alarmIds.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<WsCmd> cmds;
|
||||
|
||||
|
||||
@ -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<TelemetrySubscri
|
||||
const alarmCountUnsubscribeCmd = new AlarmCountUnsubscribeCmd();
|
||||
alarmCountUnsubscribeCmd.cmdId = subscriptionCommand.cmdId;
|
||||
this.cmdWrapper.cmds.push(alarmCountUnsubscribeCmd);
|
||||
} else if (subscriptionCommand instanceof AlarmStatusCmd) {
|
||||
const alarmCountUnsubscribeCmd = new AlarmStatusUnsubscribeCmd();
|
||||
alarmCountUnsubscribeCmd.cmdId = subscriptionCommand.cmdId;
|
||||
this.cmdWrapper.cmds.push(alarmCountUnsubscribeCmd);
|
||||
} else if (subscriptionCommand instanceof UnreadCountSubCmd || subscriptionCommand instanceof UnreadSubCmd) {
|
||||
const notificationsUnsubCmds = new UnsubscribeCmd();
|
||||
notificationsUnsubCmds.cmdId = subscriptionCommand.cmdId;
|
||||
@ -157,6 +165,8 @@ export class TelemetryWebsocketService extends WebsocketService<TelemetrySubscri
|
||||
subscriber.onEntityCount(new EntityCountUpdate(message));
|
||||
} else if (isAlarmCountUpdateMsg(message)) {
|
||||
subscriber.onAlarmCount(new AlarmCountUpdate(message));
|
||||
} else if (isAlarmStatusUpdateMsg(message)) {
|
||||
subscriber.onAlarmStatus(new AlarmStatusUpdate(message))
|
||||
}
|
||||
}
|
||||
} else if ('subscriptionId' in message && message.subscriptionId) {
|
||||
|
||||
@ -242,6 +242,8 @@ export abstract class ValueGetter<V> extends ValueAction {
|
||||
return new AttributeValueGetter<V>(ctx, settings, valueType, valueObserver, simulated);
|
||||
case GetValueAction.GET_TIME_SERIES:
|
||||
return new TimeSeriesValueGetter<V>(ctx, settings, valueType, valueObserver, simulated);
|
||||
case GetValueAction.GET_ALARM_STATUS:
|
||||
return new AlarmStatusValueGetter<V>(ctx, settings, valueType, valueObserver, simulated);
|
||||
case GetValueAction.GET_DASHBOARD_STATE:
|
||||
return new DashboardStateGetter<V>(ctx, settings, valueType, valueObserver, simulated);
|
||||
}
|
||||
@ -257,7 +259,7 @@ export abstract class ValueGetter<V> extends ValueAction {
|
||||
protected valueObserver: Partial<Observer<V>>,
|
||||
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<V>(settings.dataToValue, valueType);
|
||||
}
|
||||
}
|
||||
@ -537,6 +539,58 @@ export class TimeSeriesValueGetter<V> extends TelemetryValueGetter<V, TelemetryV
|
||||
}
|
||||
}
|
||||
|
||||
export class AlarmStatusValueGetter<V> extends ValueGetter<V> {
|
||||
|
||||
protected targetEntityId: EntityId;
|
||||
private telemetrySubscriber: SharedTelemetrySubscriber;
|
||||
|
||||
constructor(protected ctx: WidgetContext,
|
||||
protected settings: GetValueSettings<V>,
|
||||
protected valueType: ValueType,
|
||||
protected valueObserver: Partial<Observer<V>>,
|
||||
protected simulated: boolean) {
|
||||
super(ctx, settings, valueType, valueObserver, simulated);
|
||||
const entityInfo = this.ctx.defaultSubscription.getFirstEntityInfo();
|
||||
this.targetEntityId = entityInfo?.entityId;
|
||||
}
|
||||
|
||||
protected doGetValue(): Observable<boolean> {
|
||||
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<boolean> {
|
||||
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<V> extends ValueGetter<V> {
|
||||
constructor(protected ctx: WidgetContext,
|
||||
protected settings: GetValueSettings<V>,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -62,6 +62,10 @@ export const commandButtonDefaultSettings: CommandButtonWidgetSettings = {
|
||||
getTimeSeries: {
|
||||
key: 'state'
|
||||
},
|
||||
getAlarmStatus: {
|
||||
severityList: null,
|
||||
typeList: null
|
||||
},
|
||||
dataToValue: {
|
||||
type: DataToValueType.NONE,
|
||||
compareToValue: true,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -415,6 +415,10 @@ export const defaultGetValueSettings = (valueType: ValueType): GetValueSettings<
|
||||
getTimeSeries: {
|
||||
key: 'state'
|
||||
},
|
||||
getAlarmStatus: {
|
||||
severityList: null,
|
||||
typeList: null
|
||||
},
|
||||
dataToValue: {
|
||||
type: DataToValueType.NONE,
|
||||
compareToValue: true,
|
||||
|
||||
@ -103,8 +103,31 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="getValueAction.GET_ALARM_STATUS">
|
||||
<ng-container formGroupName="getAlarmStatus">
|
||||
<div class="tb-form-row space-between column-xs">
|
||||
<div class="fixed-title-width" translate>alarm.alarm-severity</div>
|
||||
<mat-chip-listbox multiple formControlName="severityList">
|
||||
<mat-chip-option *ngFor="let alarmSeverity of alarmSeverities" [value]="alarmSeverity">
|
||||
{{ alarmSeverityTranslationMap.get(alarmSeverity) | translate }}
|
||||
</mat-chip-option>
|
||||
</mat-chip-listbox>
|
||||
</div>
|
||||
<div class="tb-form-row column-xs">
|
||||
<div class="fixed-title-width" translate>alarm.alarm-types</div>
|
||||
<tb-entity-subtype-list subscriptSizing="dynamic"
|
||||
formControlName="typeList"
|
||||
appearance="outline"
|
||||
class="flex-1"
|
||||
[additionalClasses]="['tb-chips', 'flex']"
|
||||
[entityType]="entityType.ALARM">
|
||||
</tb-entity-subtype-list>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<div *ngIf="getValueSettingsFormGroup.get('action').value !== getValueAction.DO_NOTHING"
|
||||
<div *ngIf="getValueSettingsFormGroup.get('action').value !== getValueAction.DO_NOTHING &&
|
||||
getValueSettingsFormGroup.get('action').value !== getValueAction.GET_ALARM_STATUS"
|
||||
class="tb-form-panel stroked" formGroupName="dataToValue">
|
||||
<div class="tb-form-row no-padding no-border column-xs">
|
||||
<div class="fixed-title-width" translate>widgets.value-action.action-result-converter</div>
|
||||
|
||||
@ -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<AppState>) {
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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, string>(
|
||||
[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<AlarmSeverity>;
|
||||
typeList: Array<string>;
|
||||
}
|
||||
|
||||
export interface TelemetryValueSettings {
|
||||
key: string;
|
||||
}
|
||||
@ -85,6 +93,7 @@ export interface GetValueSettings<V> extends ValueActionSettings {
|
||||
executeRpc?: RpcSettings;
|
||||
getAttribute: GetAttributeValueSettings;
|
||||
getTimeSeries: TelemetryValueSettings;
|
||||
getAlarmStatus: AlarmStatusSettings;
|
||||
dataToValue: DataToValueSettings;
|
||||
}
|
||||
|
||||
|
||||
@ -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<AlarmSeverity>;
|
||||
typeList?: Array<string>;
|
||||
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<Array<AttributeData>>(1)});
|
||||
|
||||
private alarmStatusSubject = connectable(this.sharedSubscriptionInfo.subscriber.alarmStatus$,
|
||||
{ connector: () => new ReplaySubject<AlarmStatusUpdate>()});
|
||||
|
||||
private subscriptions = new Array<Subscription>();
|
||||
|
||||
public attributeData$: Observable<Array<AttributeData>> = this.attributeDataSubject; //this.attributeDataSubject.asObservable();
|
||||
public alarmStatus$: Observable<AlarmStatusUpdate> = 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>()
|
||||
};
|
||||
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<AlarmDataUpdate>(1);
|
||||
private entityCountSubject = new ReplaySubject<EntityCountUpdate>(1);
|
||||
private alarmCountSubject = new ReplaySubject<AlarmCountUpdate>(1);
|
||||
private alarmStatusSubject = new ReplaySubject<AlarmStatusUpdate>(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<Array<AttributeData>> {
|
||||
const attributeData = new Array<AttributeData>();
|
||||
return this.data$.pipe(
|
||||
|
||||
@ -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}}'",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user