Browse Source

UI: Add JS modules support to get/set value converters of behavior.

pull/12186/head
Igor Kulikov 2 years ago
parent
commit
290e2faf6a
  1. 118
      ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.models.ts
  2. 1
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.html
  3. 1
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.html
  4. 3
      ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behavior-row.component.ts
  5. 5
      ui-ngx/src/app/shared/models/action-widget-settings.models.ts

118
ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.models.ts

@ -22,7 +22,17 @@ import {
telemetryTypeTranslationsShort telemetryTypeTranslationsShort
} from '@shared/models/telemetry/telemetry.models'; } from '@shared/models/telemetry/telemetry.models';
import { WidgetContext } from '@home/models/widget-component.models'; import { WidgetContext } from '@home/models/widget-component.models';
import { BehaviorSubject, forkJoin, Observable, Observer, of, Subscription, throwError } from 'rxjs'; import {
BehaviorSubject,
forkJoin,
Observable,
Observer,
of,
ReplaySubject,
Subscription,
switchMap,
throwError
} from 'rxjs';
import { catchError, delay, map, share, take } from 'rxjs/operators'; import { catchError, delay, map, share, take } from 'rxjs/operators';
import { AfterViewInit, ChangeDetectorRef, Directive, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core'; import { AfterViewInit, ChangeDetectorRef, Directive, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core';
import { import {
@ -45,6 +55,8 @@ import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.m
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { isDefinedAndNotNull } from '@core/utils'; import { isDefinedAndNotNull } from '@core/utils';
import { parseError } from '@shared/models/error.models'; import { parseError } from '@shared/models/error.models';
import { CompiledTbFunction, compileTbFunction } from '@shared/models/js-function.models';
import { HttpClient } from '@angular/common/http';
@Directive() @Directive()
// eslint-disable-next-line @angular-eslint/directive-class-suffix // eslint-disable-next-line @angular-eslint/directive-class-suffix
@ -163,46 +175,57 @@ type DataToValueFunction<V> = (data: any) => V;
export class DataToValueConverter<V> { export class DataToValueConverter<V> {
private readonly dataToValueFunction: DataToValueFunction<V>; private readonly dataToValueFunction$: Observable<CompiledTbFunction<DataToValueFunction<V>>>;
private readonly compareToValue: any; private readonly compareToValue: any;
constructor(private settings: DataToValueSettings, constructor(private http: HttpClient,
private settings: DataToValueSettings,
private valueType: ValueType) { private valueType: ValueType) {
this.compareToValue = settings.compareToValue; this.compareToValue = settings.compareToValue;
switch (settings.type) { switch (settings.type) {
case DataToValueType.FUNCTION: case DataToValueType.FUNCTION:
try { this.dataToValueFunction$ = compileTbFunction(this.http, settings.dataToValueFunction, 'data').pipe(
this.dataToValueFunction = new Function('data', settings.dataToValueFunction) as DataToValueFunction<V>; catchError(() => {
} catch (e) { return of(new CompiledTbFunction((data: any) => data, []));
this.dataToValueFunction = (data) => data; }),
} share({
connector: () => new ReplaySubject(1),
resetOnError: false,
resetOnComplete: false,
resetOnRefCountZero: false
})
);
break; break;
case DataToValueType.NONE: case DataToValueType.NONE:
break; break;
} }
} }
dataToValue(data: any): V { dataToValue(data: any): Observable<V> {
let result: V; let result: Observable<V>;
switch (this.settings.type) { switch (this.settings.type) {
case DataToValueType.FUNCTION: case DataToValueType.FUNCTION:
result = data; result = this.dataToValueFunction$.pipe(
try { map((dataToValueFunction) => {
let input = data; let input = data;
if (!!data) { if (!!data) {
try { try {
input = JSON.parse(data); input = JSON.parse(data);
} catch (_e) {} } catch (_e) {}
} }
result = this.dataToValueFunction(input); return dataToValueFunction.execute(input);
} catch (_e) {} }),
catchError(() => of(data))
);
break; break;
case DataToValueType.NONE: case DataToValueType.NONE:
result = data; result = of(data);
break; break;
} }
if (this.valueType === ValueType.BOOLEAN) { if (this.valueType === ValueType.BOOLEAN) {
result = (result === this.compareToValue) as any; result = result.pipe(
map(val => (val === this.compareToValue) as V)
);
} }
return result; return result;
} }
@ -260,17 +283,17 @@ export abstract class ValueGetter<V> extends ValueAction {
protected simulated: boolean) { protected simulated: boolean) {
super(ctx, settings); super(ctx, settings);
if (this.settings.action !== GetValueAction.DO_NOTHING && this.settings.action !== GetValueAction.GET_ALARM_STATUS) { if (this.settings.action !== GetValueAction.DO_NOTHING && this.settings.action !== GetValueAction.GET_ALARM_STATUS) {
this.dataConverter = new DataToValueConverter<V>(settings.dataToValue, valueType); this.dataConverter = new DataToValueConverter<V>(ctx.http, settings.dataToValue, valueType);
} }
} }
getValue(): Observable<V> { getValue(): Observable<V> {
const valueObservable: Observable<V> = this.doGetValue().pipe( const valueObservable: Observable<V> = this.doGetValue().pipe(
map((data) => { switchMap((data) => {
if (this.dataConverter) { if (this.dataConverter) {
return this.dataConverter.dataToValue(data); return this.dataConverter.dataToValue(data);
} else { } else {
return data; return of(data);
} }
}), }),
catchError(err => { catchError(err => {
@ -308,9 +331,10 @@ type ValueToDataFunction<V> = (value: V) => any;
export class ValueToDataConverter<V> { export class ValueToDataConverter<V> {
private readonly constantValue: any; private readonly constantValue: any;
private readonly valueToDataFunction: ValueToDataFunction<V>; private readonly valueToDataFunction$: Observable<CompiledTbFunction<ValueToDataFunction<V>>>;
constructor(protected settings: ValueToDataSettings) { constructor(private http: HttpClient,
private settings: ValueToDataSettings) {
switch (settings.type) { switch (settings.type) {
case ValueToDataType.VALUE: case ValueToDataType.VALUE:
break; break;
@ -318,31 +342,38 @@ export class ValueToDataConverter<V> {
this.constantValue = this.settings.constantValue; this.constantValue = this.settings.constantValue;
break; break;
case ValueToDataType.FUNCTION: case ValueToDataType.FUNCTION:
try { this.valueToDataFunction$ = compileTbFunction(this.http, settings.valueToDataFunction, 'value').pipe(
this.valueToDataFunction = new Function('value', settings.valueToDataFunction) as ValueToDataFunction<V>; catchError(() => {
} catch (e) { return of(new CompiledTbFunction((value: any) => value, []));
this.valueToDataFunction = (data) => data; }),
} share({
connector: () => new ReplaySubject(1),
resetOnError: false,
resetOnComplete: false,
resetOnRefCountZero: false
})
);
break; break;
case ValueToDataType.NONE: case ValueToDataType.NONE:
break; break;
} }
} }
valueToData(value: V): any { valueToData(value: V): Observable<any> {
switch (this.settings.type) { switch (this.settings.type) {
case ValueToDataType.VALUE: case ValueToDataType.VALUE:
return value; return of(value);
case ValueToDataType.CONSTANT: case ValueToDataType.CONSTANT:
return this.constantValue; return of(this.constantValue);
case ValueToDataType.FUNCTION: case ValueToDataType.FUNCTION:
let result = value; return this.valueToDataFunction$.pipe(
try { map((valueToDataFunction) => {
result = this.valueToDataFunction(value); return valueToDataFunction.execute(value);
} catch (e) {} }),
return result; catchError(() => of(value))
);
case ValueToDataType.NONE: case ValueToDataType.NONE:
return null; return of(null);
} }
} }
} }
@ -368,14 +399,15 @@ export abstract class ValueSetter<V> extends ValueAction {
protected settings: SetValueSettings, protected settings: SetValueSettings,
protected simulated: boolean) { protected simulated: boolean) {
super(ctx, settings); super(ctx, settings);
this.valueToDataConverter = new ValueToDataConverter<V>(settings.valueToData); this.valueToDataConverter = new ValueToDataConverter<V>(ctx.http, settings.valueToData);
} }
setValue(value: V): Observable<any> { setValue(value: V): Observable<any> {
if (this.simulated) { if (this.simulated) {
return of(null).pipe(delay(500)); return of(null).pipe(delay(500));
} else { } else {
return this.doSetValue(this.valueToDataConverter.valueToData(value)).pipe( return this.valueToDataConverter.valueToData(value).pipe(
switchMap(data => this.doSetValue(data)),
catchError(err => { catchError(err => {
throw this.handleError(err); throw this.handleError(err);
}) })

1
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.html

@ -138,6 +138,7 @@
</div> </div>
<tb-js-func *ngIf="getValueSettingsFormGroup.get('dataToValue').get('type').value === dataToValueType.FUNCTION" <tb-js-func *ngIf="getValueSettingsFormGroup.get('dataToValue').get('type').value === dataToValueType.FUNCTION"
formControlName="dataToValueFunction" formControlName="dataToValueFunction"
withModules
required required
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['data']" [functionArgs]="['data']"

1
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.html

@ -114,6 +114,7 @@
</tb-value-input> </tb-value-input>
<tb-js-func *ngIf="setValueSettingsFormGroup.get('valueToData').get('type').value === valueToDataType.FUNCTION" <tb-js-func *ngIf="setValueSettingsFormGroup.get('valueToData').get('type').value === valueToDataType.FUNCTION"
formControlName="valueToDataFunction" formControlName="valueToDataFunction"
withModules
required required
[globalVariables]="functionScopeVariables" [globalVariables]="functionScopeVariables"
[functionArgs]="['value']" [functionArgs]="['value']"

3
ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-behavior-row.component.ts

@ -57,6 +57,7 @@ import {
} from '@home/pages/scada-symbol/metadata-components/scada-symbol-behaviors.component'; } from '@home/pages/scada-symbol/metadata-components/scada-symbol-behaviors.component';
import { IAliasController } from '@core/api/widget-api.models'; import { IAliasController } from '@core/api/widget-api.models';
import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models'; import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models';
import { isNotEmptyTbFunction } from '@shared/models/js-function.models';
export const behaviorValid = (behavior: ScadaSymbolBehavior): boolean => { export const behaviorValid = (behavior: ScadaSymbolBehavior): boolean => {
if (!behavior.id || !behavior.name || !behavior.type) { if (!behavior.id || !behavior.name || !behavior.type) {
@ -77,7 +78,7 @@ export const behaviorValid = (behavior: ScadaSymbolBehavior): boolean => {
return false; return false;
} }
if (behavior.defaultSetValueSettings.valueToData?.type === ValueToDataType.FUNCTION if (behavior.defaultSetValueSettings.valueToData?.type === ValueToDataType.FUNCTION
&& isUndefinedOrNull(behavior.defaultSetValueSettings.valueToData?.valueToDataFunction)) { && !isNotEmptyTbFunction(behavior.defaultSetValueSettings.valueToData?.valueToDataFunction)) {
return false; return false;
} }
break; break;

5
ui-ngx/src/app/shared/models/action-widget-settings.models.ts

@ -17,6 +17,7 @@
import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { AttributeScope } from '@shared/models/telemetry/telemetry.models';
import { widgetType } from '@shared/models/widget.models'; import { widgetType } from '@shared/models/widget.models';
import { AlarmSeverity } from '@shared/models/alarm.models'; import { AlarmSeverity } from '@shared/models/alarm.models';
import { TbFunction } from '@shared/models/js-function.models';
export enum GetValueAction { export enum GetValueAction {
DO_NOTHING = 'DO_NOTHING', DO_NOTHING = 'DO_NOTHING',
@ -79,7 +80,7 @@ export enum DataToValueType {
export interface DataToValueSettings { export interface DataToValueSettings {
type: DataToValueType; type: DataToValueType;
dataToValueFunction: string; dataToValueFunction: TbFunction;
compareToValue?: any; compareToValue?: any;
} }
@ -131,7 +132,7 @@ export enum ValueToDataType {
export interface ValueToDataSettings { export interface ValueToDataSettings {
type: ValueToDataType; type: ValueToDataType;
constantValue: any; constantValue: any;
valueToDataFunction: string; valueToDataFunction: TbFunction;
} }
export interface SetValueSettings extends ValueActionSettings { export interface SetValueSettings extends ValueActionSettings {

Loading…
Cancel
Save