49 changed files with 1718 additions and 1580 deletions
@ -0,0 +1,43 @@ |
|||
/** |
|||
* Copyright © 2016-2023 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.sql; |
|||
|
|||
import org.thingsboard.server.dao.model.BaseEntity; |
|||
import org.thingsboard.server.dao.util.SqlDao; |
|||
|
|||
import javax.persistence.EntityManager; |
|||
import javax.persistence.PersistenceContext; |
|||
|
|||
@SqlDao |
|||
public abstract class JpaPartitionedAbstractDao<E extends BaseEntity<D>, D> extends JpaAbstractDao<E, D> { |
|||
|
|||
@PersistenceContext |
|||
private EntityManager entityManager; |
|||
|
|||
@Override |
|||
protected E doSave(E entity, boolean isNew) { |
|||
createPartition(entity); |
|||
if (isNew) { |
|||
entityManager.persist(entity); |
|||
} else { |
|||
entity = entityManager.merge(entity); |
|||
} |
|||
return entity; |
|||
} |
|||
|
|||
public abstract void createPartition(E entity); |
|||
|
|||
} |
|||
@ -0,0 +1,659 @@ |
|||
///
|
|||
/// Copyright © 2016-2024 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { |
|||
AttributeData, |
|||
AttributeScope, |
|||
LatestTelemetry, |
|||
TelemetrySubscriber, |
|||
TelemetryType, |
|||
telemetryTypeTranslationsShort |
|||
} from '@shared/models/telemetry/telemetry.models'; |
|||
import { WidgetContext } from '@home/models/widget-component.models'; |
|||
import { BehaviorSubject, forkJoin, Observable, Observer, of, throwError } from 'rxjs'; |
|||
import { catchError, delay, map, share, take } from 'rxjs/operators'; |
|||
import { UtilsService } from '@core/services/utils.service'; |
|||
import { AfterViewInit, ChangeDetectorRef, Directive, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core'; |
|||
import { |
|||
DataToValueSettings, |
|||
DataToValueType, |
|||
GetAttributeValueSettings, |
|||
GetTelemetryValueSettings, |
|||
GetValueAction, |
|||
GetValueSettings, |
|||
RpcSettings, |
|||
SetAttributeValueSettings, |
|||
SetValueAction, |
|||
SetValueSettings, |
|||
TelemetryValueSettings, |
|||
ValueActionSettings, |
|||
ValueToDataSettings, |
|||
ValueToDataType |
|||
} from '@shared/models/action-widget-settings.models'; |
|||
import { ValueType } from '@shared/models/constants'; |
|||
import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; |
|||
import { EntityId } from '@shared/models/id/entity-id'; |
|||
|
|||
@Directive() |
|||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
|||
export abstract class BasicActionWidgetComponent implements OnInit, OnDestroy, AfterViewInit { |
|||
|
|||
@Input() |
|||
ctx: WidgetContext; |
|||
|
|||
@Input() |
|||
widgetTitlePanel: TemplateRef<any>; |
|||
|
|||
private loadingSubject = new BehaviorSubject(false); |
|||
private valueGetters: ValueGetter<any>[] = []; |
|||
private valueActions: ValueAction[] = []; |
|||
|
|||
loading$ = this.loadingSubject.asObservable().pipe(share()); |
|||
|
|||
error = ''; |
|||
|
|||
protected constructor(protected cd: ChangeDetectorRef) { |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
this.ctx.$scope.actionWidget = this; |
|||
} |
|||
|
|||
ngAfterViewInit(): void { |
|||
const getValueObservables: Array<Observable<any>> = []; |
|||
this.valueGetters.forEach(valueGetter => { |
|||
getValueObservables.push(valueGetter.getValue()); |
|||
}); |
|||
this.loadingSubject.next(true); |
|||
forkJoin(getValueObservables).subscribe( |
|||
{ |
|||
next: () => { |
|||
this.loadingSubject.next(false); |
|||
}, |
|||
error: () => { |
|||
this.loadingSubject.next(false); |
|||
} |
|||
} |
|||
); |
|||
} |
|||
|
|||
ngOnDestroy() { |
|||
this.valueActions.forEach(v => v.destroy()); |
|||
} |
|||
|
|||
public onInit() { |
|||
} |
|||
|
|||
public clearError() { |
|||
this.error = ''; |
|||
this.cd.markForCheck(); |
|||
} |
|||
|
|||
protected createValueGetter<V>(getValueSettings: GetValueSettings<V>, |
|||
valueType: ValueType, |
|||
valueObserver?: Partial<Observer<V>>): ValueGetter<V> { |
|||
const observer: Partial<Observer<V>> = { |
|||
next: (value: V) => { |
|||
if (valueObserver?.next) { |
|||
valueObserver.next(value); |
|||
} |
|||
}, |
|||
error: (err: any) => { |
|||
this.onError(err); |
|||
if (valueObserver?.error) { |
|||
valueObserver.error(err); |
|||
} |
|||
} |
|||
}; |
|||
const valueGetter = ValueGetter.fromSettings(this.ctx, getValueSettings, valueType, observer); |
|||
this.valueGetters.push(valueGetter); |
|||
this.valueActions.push(valueGetter); |
|||
return valueGetter; |
|||
} |
|||
|
|||
protected createValueSetter<V>(setValueSettings: SetValueSettings): ValueSetter<V> { |
|||
const valueSetter = ValueSetter.fromSettings<V>(this.ctx, setValueSettings); |
|||
this.valueActions.push(valueSetter); |
|||
return valueSetter; |
|||
} |
|||
|
|||
private onError(error: string) { |
|||
this.error = error; |
|||
this.cd.markForCheck(); |
|||
} |
|||
|
|||
protected updateValue<V>(valueSetter: ValueSetter<V>, |
|||
value: V, |
|||
setValueObserver?: Partial<Observer<void>>): void { |
|||
this.clearError(); |
|||
this.loadingSubject.next(true); |
|||
valueSetter.setValue(value).subscribe({ |
|||
next: () => { |
|||
if (setValueObserver?.next) { |
|||
setValueObserver.next(); |
|||
} |
|||
this.loadingSubject.next(false); |
|||
}, |
|||
error: (err) => { |
|||
this.loadingSubject.next(false); |
|||
if (setValueObserver?.error) { |
|||
setValueObserver.error(err); |
|||
} |
|||
const message = parseError(this.ctx, err); |
|||
this.onError(message); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
type DataToValueFunction<V> = (data: any) => V; |
|||
|
|||
export class DataToValueConverter<V> { |
|||
|
|||
private readonly dataToValueFunction: DataToValueFunction<V>; |
|||
private readonly compareToValue: any; |
|||
|
|||
constructor(private settings: DataToValueSettings, |
|||
private valueType: ValueType) { |
|||
this.compareToValue = settings.compareToValue; |
|||
switch (settings.type) { |
|||
case DataToValueType.FUNCTION: |
|||
try { |
|||
this.dataToValueFunction = new Function('data', settings.dataToValueFunction) as DataToValueFunction<V>; |
|||
} catch (e) { |
|||
this.dataToValueFunction = (data) => data; |
|||
} |
|||
break; |
|||
case DataToValueType.NONE: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
dataToValue(data: any): V { |
|||
let result: V; |
|||
switch (this.settings.type) { |
|||
case DataToValueType.FUNCTION: |
|||
result = data; |
|||
try { |
|||
result = this.dataToValueFunction(!!data ? JSON.parse(data) : data); |
|||
} catch (e) {} |
|||
break; |
|||
case DataToValueType.NONE: |
|||
result = data; |
|||
break; |
|||
} |
|||
if (this.valueType === ValueType.BOOLEAN) { |
|||
result = (result === this.compareToValue) as any; |
|||
} |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
export abstract class ValueAction { |
|||
|
|||
protected constructor(protected ctx: WidgetContext, |
|||
protected settings: ValueActionSettings) {} |
|||
|
|||
protected handleError(err: any): Error { |
|||
const reason = parseError(this.ctx, err); |
|||
let errorMessage = this.ctx.translate.instant('widgets.value-action.error.failed-to-perform-action', |
|||
{actionLabel: this.settings.actionLabel}); |
|||
if (reason) { |
|||
errorMessage += '<br>' + reason; |
|||
} |
|||
return new Error(errorMessage); |
|||
} |
|||
|
|||
destroy(): void {} |
|||
} |
|||
|
|||
export abstract class ValueGetter<V> extends ValueAction { |
|||
|
|||
static fromSettings<V>(ctx: WidgetContext, |
|||
settings: GetValueSettings<V>, |
|||
valueType: ValueType, |
|||
valueObserver: Partial<Observer<V>>): ValueGetter<V> { |
|||
switch (settings.action) { |
|||
case GetValueAction.DO_NOTHING: |
|||
return new DefaultValueGetter<V>(ctx, settings, valueType, valueObserver); |
|||
case GetValueAction.EXECUTE_RPC: |
|||
return new ExecuteRpcValueGetter<V>(ctx, settings, valueType, valueObserver); |
|||
case GetValueAction.GET_ATTRIBUTE: |
|||
return new AttributeValueGetter<V>(ctx, settings, valueType, valueObserver); |
|||
case GetValueAction.GET_TIME_SERIES: |
|||
return new TimeSeriesValueGetter<V>(ctx, settings, valueType, valueObserver); |
|||
} |
|||
} |
|||
|
|||
private readonly isSimulated: boolean; |
|||
private readonly dataConverter: DataToValueConverter<V>; |
|||
|
|||
protected constructor(protected ctx: WidgetContext, |
|||
protected settings: GetValueSettings<V>, |
|||
protected valueType: ValueType, |
|||
protected valueObserver: Partial<Observer<V>>) { |
|||
super(ctx, settings); |
|||
this.isSimulated = this.ctx.$injector.get(UtilsService).widgetEditMode; |
|||
if (this.settings.action !== GetValueAction.DO_NOTHING) { |
|||
this.dataConverter = new DataToValueConverter<V>(settings.dataToValue, valueType); |
|||
} |
|||
} |
|||
|
|||
getValue(): Observable<V> { |
|||
const valueObservable: Observable<V> = (this.isSimulated ? of(null).pipe(delay(500)) : this.doGetValue()).pipe( |
|||
map((data) => { |
|||
if (this.dataConverter) { |
|||
return this.dataConverter.dataToValue(data); |
|||
} else { |
|||
return data; |
|||
} |
|||
}), |
|||
catchError(err => { |
|||
throw this.handleError(err); |
|||
}) |
|||
); |
|||
valueObservable.subscribe({ |
|||
next: (value) => { |
|||
this.valueObserver.next(value); |
|||
}, |
|||
error: (err) => { |
|||
this.valueObserver.error(err); |
|||
} |
|||
}); |
|||
return valueObservable.pipe( |
|||
take(1) |
|||
); |
|||
} |
|||
|
|||
destroy() { |
|||
super.destroy(); |
|||
} |
|||
|
|||
protected abstract doGetValue(): Observable<any>; |
|||
} |
|||
|
|||
type ValueToDataFunction<V> = (value: V) => any; |
|||
|
|||
export class ValueToDataConverter<V> { |
|||
|
|||
private readonly constantValue: any; |
|||
private readonly valueToDataFunction: ValueToDataFunction<V>; |
|||
|
|||
constructor(protected settings: ValueToDataSettings) { |
|||
switch (settings.type) { |
|||
case ValueToDataType.CONSTANT: |
|||
this.constantValue = this.settings.constantValue; |
|||
break; |
|||
case ValueToDataType.FUNCTION: |
|||
try { |
|||
this.valueToDataFunction = new Function('value', settings.valueToDataFunction) as ValueToDataFunction<V>; |
|||
} catch (e) { |
|||
this.valueToDataFunction = (data) => data; |
|||
} |
|||
break; |
|||
case ValueToDataType.NONE: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
valueToData(value: V): any { |
|||
switch (this.settings.type) { |
|||
case ValueToDataType.CONSTANT: |
|||
return this.constantValue; |
|||
case ValueToDataType.FUNCTION: |
|||
let result = value; |
|||
try { |
|||
result = this.valueToDataFunction(value); |
|||
} catch (e) {} |
|||
return result; |
|||
case ValueToDataType.NONE: |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
export abstract class ValueSetter<V> extends ValueAction { |
|||
|
|||
static fromSettings<V>(ctx: WidgetContext, |
|||
settings: SetValueSettings): ValueSetter<V> { |
|||
switch (settings.action) { |
|||
case SetValueAction.EXECUTE_RPC: |
|||
return new ExecuteRpcValueSetter<V>(ctx, settings); |
|||
case SetValueAction.SET_ATTRIBUTE: |
|||
return new AttributeValueSetter<V>(ctx, settings); |
|||
case SetValueAction.ADD_TIME_SERIES: |
|||
return new TimeSeriesValueSetter<V>(ctx, settings); |
|||
} |
|||
} |
|||
|
|||
private readonly isSimulated: boolean; |
|||
private readonly valueToDataConverter: ValueToDataConverter<V>; |
|||
|
|||
protected constructor(protected ctx: WidgetContext, |
|||
protected settings: SetValueSettings) { |
|||
super(ctx, settings); |
|||
this.isSimulated = this.ctx.$injector.get(UtilsService).widgetEditMode; |
|||
this.valueToDataConverter = new ValueToDataConverter<V>(settings.valueToData); |
|||
} |
|||
|
|||
setValue(value: V): Observable<any> { |
|||
if (this.isSimulated) { |
|||
return of(null).pipe(delay(500)); |
|||
} else { |
|||
return this.doSetValue(this.valueToDataConverter.valueToData(value)).pipe( |
|||
catchError(err => { |
|||
throw this.handleError(err); |
|||
}) |
|||
); |
|||
} |
|||
} |
|||
|
|||
protected abstract doSetValue(data: any): Observable<any>; |
|||
} |
|||
|
|||
export class DefaultValueGetter<V> extends ValueGetter<V> { |
|||
|
|||
private readonly defaultValue: V; |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: GetValueSettings<V>, |
|||
protected valueType: ValueType, |
|||
protected valueObserver: Partial<Observer<V>>) { |
|||
super(ctx, settings, valueType, valueObserver); |
|||
this.defaultValue = settings.defaultValue; |
|||
} |
|||
|
|||
protected doGetValue(): Observable<V> { |
|||
return of(this.defaultValue); |
|||
} |
|||
} |
|||
|
|||
export class ExecuteRpcValueGetter<V> extends ValueGetter<V> { |
|||
|
|||
private readonly executeRpcSettings: RpcSettings; |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: GetValueSettings<V>, |
|||
protected valueType: ValueType, |
|||
protected valueObserver: Partial<Observer<V>>) { |
|||
super(ctx, settings, valueType, valueObserver); |
|||
this.executeRpcSettings = settings.executeRpc; |
|||
} |
|||
|
|||
protected doGetValue(): Observable<V> { |
|||
return this.ctx.controlApi.sendTwoWayCommand(this.executeRpcSettings.method, null, |
|||
this.executeRpcSettings.requestTimeout, |
|||
this.executeRpcSettings.requestPersistent, |
|||
this.executeRpcSettings.persistentPollingInterval).pipe( |
|||
catchError((err) => { |
|||
throw handleRpcError(this.ctx, err); |
|||
}) |
|||
); |
|||
} |
|||
} |
|||
|
|||
export abstract class TelemetryValueGetter<V, S extends GetTelemetryValueSettings> extends ValueGetter<V> { |
|||
|
|||
protected targetEntityId: EntityId; |
|||
private telemetrySubscriber: TelemetrySubscriber; |
|||
|
|||
protected constructor(protected ctx: WidgetContext, |
|||
protected settings: GetValueSettings<V>, |
|||
protected valueType: ValueType, |
|||
protected valueObserver: Partial<Observer<V>>) { |
|||
super(ctx, settings, valueType, valueObserver); |
|||
const entityInfo = this.ctx.defaultSubscription.getFirstEntityInfo(); |
|||
this.targetEntityId = entityInfo?.entityId; |
|||
} |
|||
|
|||
protected doGetValue(): Observable<V> { |
|||
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) { |
|||
const err = validateAttributeScope(this.ctx, this.targetEntityId, this.scope()); |
|||
if (err) { |
|||
return throwError(() => err); |
|||
} |
|||
if (this.getTelemetryValueSettings().subscribeForUpdates) { |
|||
return this.subscribeForTelemetryValue(); |
|||
} else { |
|||
return this.doGetTelemetryValue(); |
|||
} |
|||
} else { |
|||
return of(null); |
|||
} |
|||
} |
|||
|
|||
private subscribeForTelemetryValue(): Observable<V> { |
|||
this.telemetrySubscriber = |
|||
TelemetrySubscriber.createEntityAttributesSubscription(this.ctx.telemetryWsService, this.targetEntityId, |
|||
this.scope(), this.ctx.ngZone, [this.getTelemetryValueSettings().key]); |
|||
this.telemetrySubscriber.subscribe(); |
|||
return this.telemetrySubscriber.attributeData$().pipe( |
|||
map((data) => { |
|||
let value: V = null; |
|||
const entry = data.find(attr => attr.key === this.getTelemetryValueSettings().key); |
|||
if (entry) { |
|||
value = entry.value; |
|||
try { |
|||
value = JSON.parse(entry.value); |
|||
} catch (_e) {} |
|||
} |
|||
return value; |
|||
}) |
|||
); |
|||
} |
|||
|
|||
protected scope(): TelemetryType { |
|||
return LatestTelemetry.LATEST_TELEMETRY; |
|||
} |
|||
|
|||
protected abstract getTelemetryValueSettings(): S; |
|||
|
|||
protected abstract doGetTelemetryValue(): Observable<V>; |
|||
|
|||
destroy() { |
|||
if (this.telemetrySubscriber) { |
|||
this.telemetrySubscriber.unsubscribe(); |
|||
this.telemetrySubscriber = null; |
|||
} |
|||
super.destroy(); |
|||
} |
|||
} |
|||
|
|||
export class AttributeValueGetter<V> extends TelemetryValueGetter<V, GetAttributeValueSettings> { |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: GetValueSettings<V>, |
|||
protected valueType: ValueType, |
|||
protected valueObserver: Partial<Observer<V>>) { |
|||
super(ctx, settings, valueType, valueObserver); |
|||
} |
|||
|
|||
protected getTelemetryValueSettings(): GetAttributeValueSettings { |
|||
return this.settings.getAttribute; |
|||
} |
|||
|
|||
protected scope(): TelemetryType { |
|||
return this.getTelemetryValueSettings().scope; |
|||
} |
|||
|
|||
protected doGetTelemetryValue(): Observable<V> { |
|||
const getAttributeValueSettings = this.getTelemetryValueSettings(); |
|||
return this.ctx.attributeService.getEntityAttributes(this.targetEntityId, |
|||
getAttributeValueSettings.scope, [getAttributeValueSettings.key], {ignoreLoading: true, ignoreErrors: true}).pipe( |
|||
map((data) => data.find(attr => attr.key === getAttributeValueSettings.key)?.value) |
|||
); |
|||
} |
|||
|
|||
} |
|||
|
|||
export class TimeSeriesValueGetter<V> extends TelemetryValueGetter<V, GetTelemetryValueSettings> { |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: GetValueSettings<V>, |
|||
protected valueType: ValueType, |
|||
protected valueObserver: Partial<Observer<V>>) { |
|||
super(ctx, settings, valueType, valueObserver); |
|||
} |
|||
|
|||
protected getTelemetryValueSettings(): GetTelemetryValueSettings { |
|||
return this.settings.getTimeSeries; |
|||
} |
|||
|
|||
protected doGetTelemetryValue(): Observable<V> { |
|||
const getTelemetryValueSettings = this.getTelemetryValueSettings(); |
|||
return this.ctx.attributeService.getEntityTimeseriesLatest(this.targetEntityId, |
|||
[getTelemetryValueSettings.key], true, {ignoreLoading: true, ignoreErrors: true}) |
|||
.pipe( |
|||
map((data) => { |
|||
let value: any = null; |
|||
if (data[getTelemetryValueSettings.key]) { |
|||
const dataSet = data[getTelemetryValueSettings.key]; |
|||
if (dataSet.length) { |
|||
value = dataSet[0].value; |
|||
} |
|||
} |
|||
return value; |
|||
}) |
|||
); |
|||
} |
|||
|
|||
} |
|||
|
|||
export class ExecuteRpcValueSetter<V> extends ValueSetter<V> { |
|||
|
|||
private readonly executeRpcSettings: RpcSettings; |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: SetValueSettings) { |
|||
super(ctx, settings); |
|||
this.executeRpcSettings = settings.executeRpc; |
|||
} |
|||
|
|||
protected doSetValue(data: any): Observable<any> { |
|||
return this.ctx.controlApi.sendOneWayCommand(this.executeRpcSettings.method, data, |
|||
this.executeRpcSettings.requestTimeout, |
|||
this.executeRpcSettings.requestPersistent, |
|||
this.executeRpcSettings.persistentPollingInterval).pipe( |
|||
catchError((err) => { |
|||
throw handleRpcError(this.ctx, err); |
|||
}) |
|||
); |
|||
} |
|||
} |
|||
|
|||
export abstract class TelemetryValueSetter<V> extends ValueSetter<V> { |
|||
|
|||
protected targetEntityId: EntityId; |
|||
|
|||
protected constructor(protected ctx: WidgetContext, |
|||
protected settings: SetValueSettings) { |
|||
super(ctx, settings); |
|||
const entityInfo = this.ctx.defaultSubscription.getFirstEntityInfo(); |
|||
this.targetEntityId = entityInfo?.entityId; |
|||
} |
|||
|
|||
protected doSetValue(data: any): Observable<V> { |
|||
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) { |
|||
const err = validateAttributeScope(this.ctx, this.targetEntityId, this.scope()); |
|||
if (err) { |
|||
return throwError(() => err); |
|||
} |
|||
return this.doSetTelemetryValue(data); |
|||
} else { |
|||
return of(null); |
|||
} |
|||
} |
|||
|
|||
protected scope(): TelemetryType { |
|||
return LatestTelemetry.LATEST_TELEMETRY; |
|||
} |
|||
|
|||
protected abstract doSetTelemetryValue(data: any): Observable<V>; |
|||
|
|||
} |
|||
|
|||
export class AttributeValueSetter<V> extends TelemetryValueSetter<V> { |
|||
|
|||
private readonly setAttributeValueSettings: SetAttributeValueSettings; |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: SetValueSettings) { |
|||
super(ctx, settings); |
|||
this.setAttributeValueSettings = settings.setAttribute; |
|||
} |
|||
|
|||
protected doSetTelemetryValue(data: any): Observable<any> { |
|||
const attributes: Array<AttributeData> = [{key: this.setAttributeValueSettings.key, value: data}]; |
|||
return this.ctx.attributeService.saveEntityAttributes(this.targetEntityId, |
|||
this.setAttributeValueSettings.scope, attributes, {ignoreLoading: true, ignoreErrors: true}); |
|||
} |
|||
|
|||
protected scope(): TelemetryType { |
|||
return this.setAttributeValueSettings.scope; |
|||
} |
|||
|
|||
} |
|||
|
|||
export class TimeSeriesValueSetter<V> extends TelemetryValueSetter<V> { |
|||
|
|||
private readonly putTimeSeriesValueSettings: TelemetryValueSettings; |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: SetValueSettings) { |
|||
super(ctx, settings); |
|||
this.putTimeSeriesValueSettings = settings.putTimeSeries; |
|||
} |
|||
|
|||
protected doSetTelemetryValue(data: any): Observable<any> { |
|||
const timeSeries: Array<AttributeData> = [{key: this.putTimeSeriesValueSettings.key, value: data}]; |
|||
return this.ctx.attributeService.saveEntityTimeseries(this.targetEntityId, |
|||
LatestTelemetry.LATEST_TELEMETRY, timeSeries, {ignoreLoading: true, ignoreErrors: true}); |
|||
} |
|||
|
|||
} |
|||
|
|||
const parseError = (ctx: WidgetContext, err: any): string => |
|||
ctx.$injector.get(UtilsService).parseException(err).message || 'Unknown Error'; |
|||
|
|||
const handleRpcError = (ctx: WidgetContext, err: any): Error => { |
|||
let reason: string; |
|||
if (ctx.defaultSubscription.rpcErrorText) { |
|||
reason = ctx.defaultSubscription.rpcErrorText; |
|||
} else { |
|||
reason = parseError(ctx, err); |
|||
} |
|||
return new Error(reason); |
|||
}; |
|||
|
|||
const validateAttributeScope = (ctx: WidgetContext, targetEntityId: EntityId, scope?: TelemetryType): Error | null => { |
|||
if (targetEntityId.entityType !== EntityType.DEVICE && scope && |
|||
![AttributeScope.SERVER_SCOPE, LatestTelemetry.LATEST_TELEMETRY].includes(scope)) { |
|||
const scopeStr = ctx.translate.instant(telemetryTypeTranslationsShort.get(scope)); |
|||
const entityType = |
|||
ctx.translate.instant(entityTypeTranslations.get(targetEntityId.entityType).type); |
|||
const errorMessage = |
|||
ctx.translate.instant('widgets.value-action.error.invalid-attribute-scope', {scope: scopeStr, entityType}); |
|||
return new Error(errorMessage); |
|||
} else { |
|||
return null; |
|||
} |
|||
}; |
|||
@ -1,626 +0,0 @@ |
|||
///
|
|||
/// Copyright © 2016-2024 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { |
|||
AttributeData, |
|||
AttributeScope, |
|||
LatestTelemetry, |
|||
telemetryTypeTranslationsShort |
|||
} from '@shared/models/telemetry/telemetry.models'; |
|||
import { WidgetContext } from '@home/models/widget-component.models'; |
|||
import { BehaviorSubject, Observable, of, throwError } from 'rxjs'; |
|||
import { catchError, delay, map, share } from 'rxjs/operators'; |
|||
import { UtilsService } from '@core/services/utils.service'; |
|||
import { AfterViewInit, ChangeDetectorRef, Directive, Input, OnInit, TemplateRef } from '@angular/core'; |
|||
import { backgroundStyle, ComponentStyle, overlayStyle } from '@shared/models/widget-settings.models'; |
|||
import { ImagePipe } from '@shared/pipe/image.pipe'; |
|||
import { DomSanitizer } from '@angular/platform-browser'; |
|||
import { |
|||
RpcActionSettings, |
|||
RpcGetAttributeSettings, |
|||
RpcSetAttributeSettings, |
|||
RpcSettings, |
|||
RpcStateToParamsSettings, |
|||
RpcStateToParamsType, |
|||
RpcStateWidgetSettings, |
|||
RpcTelemetrySettings, |
|||
RpcUpdateStateAction |
|||
} from '@shared/models/rpc-widget-settings.models'; |
|||
import { |
|||
RpcDataToStateSettings, |
|||
RpcDataToStateType, |
|||
RpcInitialStateAction, |
|||
RpcInitialStateSettings, |
|||
RpcStateBehaviourSettings, |
|||
RpcUpdateStateSettings |
|||
} from '@app/shared/models/rpc-widget-settings.models'; |
|||
import { ValueType } from '@shared/models/constants'; |
|||
import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; |
|||
|
|||
@Directive() |
|||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
|||
export abstract class BasicRpcStateWidgetComponent<V, S extends RpcStateWidgetSettings<V>> implements OnInit, AfterViewInit { |
|||
|
|||
@Input() |
|||
ctx: WidgetContext; |
|||
|
|||
@Input() |
|||
widgetTitlePanel: TemplateRef<any>; |
|||
|
|||
settings: S; |
|||
|
|||
behaviorApi: RpcStateBehaviorApi<V>; |
|||
loading$: Observable<boolean>; |
|||
|
|||
backgroundStyle$: Observable<ComponentStyle>; |
|||
overlayStyle: ComponentStyle = {}; |
|||
|
|||
value: V; |
|||
|
|||
error = ''; |
|||
|
|||
protected constructor(protected imagePipe: ImagePipe, |
|||
protected sanitizer: DomSanitizer, |
|||
protected cd: ChangeDetectorRef) { |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
this.ctx.$scope.rpcWidget = this; |
|||
this.settings = {...this.defaultSettings(), ...this.ctx.settings}; |
|||
this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); |
|||
this.overlayStyle = overlayStyle(this.settings.background.overlay); |
|||
|
|||
const behaviourSettings: RpcStateBehaviourSettings<V> = { |
|||
initialState: this.initialState(), |
|||
updateStateByValue: val => this.getUpdateStateSettingsForValue(val) |
|||
}; |
|||
|
|||
const callbacks: RpcStateCallbacks<V> = { |
|||
onStateValue: val => this.setValue(val), |
|||
onError: err => this.onError(err), |
|||
validateStateValue: val => this.validateValue(val) |
|||
}; |
|||
|
|||
this.behaviorApi = new RpcStateBehaviorApi<V>(this.defaultValue(), this.ctx, |
|||
behaviourSettings, callbacks, this.stateValueType()); |
|||
this.loading$ = this.behaviorApi.loading$.pipe(share()); |
|||
} |
|||
|
|||
ngAfterViewInit(): void { |
|||
this.behaviorApi.initState(); |
|||
} |
|||
|
|||
public onInit() { |
|||
const borderRadius = this.ctx.$widgetElement.css('borderRadius'); |
|||
this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; |
|||
this.cd.detectChanges(); |
|||
} |
|||
|
|||
public clearError() { |
|||
this.error = ''; |
|||
} |
|||
|
|||
public updateValue() { |
|||
this.behaviorApi.updateState(this.value); |
|||
} |
|||
|
|||
private onError(error: string) { |
|||
this.error = error; |
|||
this.ctx.detectChanges(); |
|||
} |
|||
|
|||
protected abstract defaultSettings(): S; |
|||
|
|||
protected abstract initialState(): RpcInitialStateSettings<V>; |
|||
|
|||
protected abstract getUpdateStateSettingsForValue(value: V): RpcUpdateStateSettings; |
|||
|
|||
protected stateValueType(): ValueType { |
|||
return ValueType.BOOLEAN; |
|||
} |
|||
|
|||
protected defaultValue(): V { |
|||
return null; |
|||
} |
|||
|
|||
protected setValue(value: V) { |
|||
this.value = value; |
|||
} |
|||
|
|||
protected validateValue(value: any): V { |
|||
return value; |
|||
} |
|||
|
|||
} |
|||
|
|||
export abstract class RpcHasLoading { |
|||
|
|||
public get loading$() { |
|||
return this.loadingSubject.asObservable(); |
|||
} |
|||
|
|||
protected loadingSubject = new BehaviorSubject(false); |
|||
} |
|||
|
|||
export interface RpcStateCallbacks<V> { |
|||
onStateValue: (value: V) => void; |
|||
validateStateValue: (value: any) => V; |
|||
onError: (error: string) => void; |
|||
} |
|||
|
|||
export class RpcStateBehaviorApi<V> extends RpcHasLoading { |
|||
|
|||
private readonly initialStateGetter: RpcInitialStateGetter<V>; |
|||
private readonly stateUpdatersMap: Map<RpcUpdateStateSettings, RpcStateUpdater<V>>; |
|||
|
|||
constructor(private state: V, |
|||
private ctx: WidgetContext, |
|||
private settings: RpcStateBehaviourSettings<V>, |
|||
private callbacks: RpcStateCallbacks<V>, |
|||
stateValueType: ValueType) { |
|||
super(); |
|||
this.initialStateGetter = RpcInitialStateGetter.fromSettings(ctx, settings.initialState, stateValueType, callbacks); |
|||
this.stateUpdatersMap = new Map<RpcUpdateStateSettings, RpcStateUpdater<V>>(); |
|||
} |
|||
|
|||
initState() { |
|||
if (this.ctx.defaultSubscription.targetEntityId || this.ctx.defaultSubscription.rpcEnabled) { |
|||
this.loadingSubject.next(true); |
|||
this.initialStateGetter.initState().subscribe( |
|||
{ |
|||
next: (value) => { |
|||
this.state = value; |
|||
this.loadingSubject.next(false); |
|||
this.callbacks.onStateValue(value); |
|||
this.ctx.detectChanges(); |
|||
}, |
|||
error: (err: any) => { |
|||
this.loadingSubject.next(false); |
|||
const message = parseError(this.ctx, err); |
|||
this.callbacks.onError(message); |
|||
} |
|||
} |
|||
); |
|||
} else { |
|||
this.callbacks.onError(this.ctx.translate.instant('widgets.rpc-state.error.target-entity-is-not-set')); |
|||
} |
|||
} |
|||
|
|||
updateState(value: V) { |
|||
this.callbacks.onError(null); |
|||
let updater: RpcStateUpdater<V>; |
|||
const updateStateSettings = this.settings.updateStateByValue(value); |
|||
if (updateStateSettings) { |
|||
updater = this.stateUpdatersMap.get(updateStateSettings); |
|||
if (!updater) { |
|||
updater = RpcStateUpdater.fromSettings(this.ctx, updateStateSettings); |
|||
this.stateUpdatersMap.set(updateStateSettings, updater); |
|||
} |
|||
} |
|||
if (updater) { |
|||
this.loadingSubject.next(true); |
|||
updater.updateState(value).subscribe( |
|||
{ |
|||
next: () => { |
|||
this.state = value; |
|||
this.loadingSubject.next(false); |
|||
this.ctx.detectChanges(); |
|||
}, |
|||
error: (err: any) => { |
|||
this.loadingSubject.next(false); |
|||
const message = parseError(this.ctx, err); |
|||
this.callbacks.onStateValue(this.state); |
|||
this.callbacks.onError(message); |
|||
this.ctx.detectChanges(); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
type RpcDataToStateFunction<V> = (data: any) => V; |
|||
|
|||
export class RpcDataToStateConverter<V> { |
|||
|
|||
private readonly dataToStateFunction: RpcDataToStateFunction<V>; |
|||
private readonly compareToValue: any; |
|||
|
|||
constructor(private settings: RpcDataToStateSettings, |
|||
private stateValueType: ValueType, |
|||
private callbacks: RpcStateCallbacks<V>) { |
|||
this.compareToValue = settings.compareToValue; |
|||
switch (settings.type) { |
|||
case RpcDataToStateType.FUNCTION: |
|||
try { |
|||
this.dataToStateFunction = new Function('data', settings.dataToStateFunction) as RpcDataToStateFunction<V>; |
|||
} catch (e) { |
|||
this.dataToStateFunction = (data) => data; |
|||
} |
|||
break; |
|||
case RpcDataToStateType.NONE: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
dataToStateValue(data: any): V { |
|||
let result: V; |
|||
switch (this.settings.type) { |
|||
case RpcDataToStateType.FUNCTION: |
|||
result = data; |
|||
try { |
|||
result = this.dataToStateFunction(!!data ? JSON.parse(data) : data); |
|||
} catch (e) {} |
|||
break; |
|||
case RpcDataToStateType.NONE: |
|||
result = data; |
|||
break; |
|||
} |
|||
if (this.stateValueType === ValueType.BOOLEAN) { |
|||
result = (result === this.compareToValue) as any; |
|||
} |
|||
result = this.callbacks.validateStateValue(result); |
|||
return result; |
|||
} |
|||
} |
|||
|
|||
export abstract class RpcAction { |
|||
|
|||
protected constructor(protected ctx: WidgetContext, |
|||
protected settings: RpcActionSettings) {} |
|||
|
|||
handleError(err: any): Error { |
|||
const reason = parseError(this.ctx, err); |
|||
let errorMessage = this.ctx.translate.instant('widgets.rpc-state.error.failed-to-perform-action', |
|||
{actionLabel: this.settings.actionLabel}); |
|||
if (reason) { |
|||
errorMessage += '<br>' + reason; |
|||
} |
|||
return new Error(errorMessage); |
|||
} |
|||
} |
|||
|
|||
export abstract class RpcInitialStateGetter<V> extends RpcAction { |
|||
|
|||
static fromSettings<V>(ctx: WidgetContext, |
|||
settings: RpcInitialStateSettings<V>, |
|||
stateValueType: ValueType, |
|||
callbacks: RpcStateCallbacks<V>): RpcInitialStateGetter<V> { |
|||
switch (settings.action) { |
|||
case RpcInitialStateAction.DO_NOTHING: |
|||
return new RpcDefaultStateGetter<V>(ctx, settings, stateValueType, callbacks); |
|||
case RpcInitialStateAction.EXECUTE_RPC: |
|||
return new ExecuteRpcStateGetter<V>(ctx, settings, stateValueType, callbacks); |
|||
case RpcInitialStateAction.GET_ATTRIBUTE: |
|||
return new RpcAttributeStateGetter<V>(ctx, settings, stateValueType, callbacks); |
|||
case RpcInitialStateAction.GET_TIME_SERIES: |
|||
return new RpcTimeSeriesStateGetter<V>(ctx, settings, stateValueType, callbacks); |
|||
} |
|||
} |
|||
|
|||
private readonly isSimulated: boolean; |
|||
private readonly dataConverter: RpcDataToStateConverter<V>; |
|||
|
|||
protected constructor(protected ctx: WidgetContext, |
|||
protected settings: RpcInitialStateSettings<V>, |
|||
protected stateValueType: ValueType, |
|||
protected callbacks: RpcStateCallbacks<V>) { |
|||
super(ctx, settings); |
|||
this.isSimulated = this.ctx.$injector.get(UtilsService).widgetEditMode; |
|||
if (this.settings.action !== RpcInitialStateAction.DO_NOTHING) { |
|||
this.dataConverter = new RpcDataToStateConverter<V>(settings.dataToState, stateValueType, this.callbacks); |
|||
} |
|||
} |
|||
|
|||
initState(): Observable<V> { |
|||
const stateObservable: Observable<V> = this.isSimulated ? of(null).pipe(delay(500)) : this.doGetState(); |
|||
return stateObservable.pipe( |
|||
map((data) => { |
|||
if (this.dataConverter) { |
|||
return this.dataConverter.dataToStateValue(data); |
|||
} else { |
|||
return data; |
|||
} |
|||
}), |
|||
catchError(err => { |
|||
throw this.handleError(err); |
|||
}) |
|||
); |
|||
} |
|||
|
|||
protected abstract doGetState(): Observable<any>; |
|||
} |
|||
|
|||
type RpcStateToParamsFunction<V> = (state: V) => any; |
|||
|
|||
export class RpcStateToParamsConverter<V> { |
|||
|
|||
private readonly constantValue: any; |
|||
private readonly stateToParamsFunction: RpcStateToParamsFunction<V>; |
|||
|
|||
constructor(protected settings: RpcStateToParamsSettings) { |
|||
switch (settings.type) { |
|||
case RpcStateToParamsType.CONSTANT: |
|||
this.constantValue = this.settings.constantValue; |
|||
break; |
|||
case RpcStateToParamsType.FUNCTION: |
|||
try { |
|||
this.stateToParamsFunction = new Function('value', settings.stateToParamsFunction) as RpcStateToParamsFunction<V>; |
|||
} catch (e) { |
|||
this.stateToParamsFunction = (data) => data; |
|||
} |
|||
break; |
|||
case RpcStateToParamsType.NONE: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
stateToParams(state: V): any { |
|||
switch (this.settings.type) { |
|||
case RpcStateToParamsType.CONSTANT: |
|||
return this.constantValue; |
|||
case RpcStateToParamsType.FUNCTION: |
|||
let result = state; |
|||
try { |
|||
result = this.stateToParamsFunction(state); |
|||
} catch (e) {} |
|||
return result; |
|||
case RpcStateToParamsType.NONE: |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
export abstract class RpcStateUpdater<V> extends RpcAction { |
|||
|
|||
static fromSettings<V>(ctx: WidgetContext, |
|||
settings: RpcUpdateStateSettings): RpcStateUpdater<V> { |
|||
switch (settings.action) { |
|||
case RpcUpdateStateAction.EXECUTE_RPC: |
|||
return new ExecuteRpcStateUpdater<V>(ctx, settings); |
|||
case RpcUpdateStateAction.SET_ATTRIBUTE: |
|||
return new RpcAttributeStateUpdater<V>(ctx, settings); |
|||
case RpcUpdateStateAction.ADD_TIME_SERIES: |
|||
return new RpcTimeSeriesStateUpdater<V>(ctx, settings); |
|||
} |
|||
} |
|||
|
|||
private readonly isSimulated: boolean; |
|||
private readonly paramsConverter: RpcStateToParamsConverter<V>; |
|||
|
|||
protected constructor(protected ctx: WidgetContext, |
|||
protected settings: RpcUpdateStateSettings) { |
|||
super(ctx, settings); |
|||
this.isSimulated = this.ctx.$injector.get(UtilsService).widgetEditMode; |
|||
this.paramsConverter = new RpcStateToParamsConverter<V>(settings.stateToParams); |
|||
} |
|||
|
|||
updateState(state: V): Observable<any> { |
|||
if (this.isSimulated) { |
|||
return of(null).pipe(delay(500)); |
|||
} else { |
|||
return this.doUpdateState(this.paramsConverter.stateToParams(state)).pipe( |
|||
catchError(err => { |
|||
throw this.handleError(err); |
|||
}) |
|||
); |
|||
} |
|||
} |
|||
|
|||
protected abstract doUpdateState(params: any): Observable<any>; |
|||
} |
|||
|
|||
export class RpcDefaultStateGetter<V> extends RpcInitialStateGetter<V> { |
|||
|
|||
private readonly defaultValue: V; |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: RpcInitialStateSettings<V>, |
|||
protected stateValueType: ValueType, |
|||
protected callbacks: RpcStateCallbacks<V>) { |
|||
super(ctx, settings, stateValueType, callbacks); |
|||
this.defaultValue = settings.defaultValue; |
|||
} |
|||
|
|||
protected doGetState(): Observable<V> { |
|||
return of(this.defaultValue); |
|||
} |
|||
} |
|||
|
|||
export class ExecuteRpcStateGetter<V> extends RpcInitialStateGetter<V> { |
|||
|
|||
private readonly executeRpcSettings: RpcSettings; |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: RpcInitialStateSettings<V>, |
|||
protected stateValueType: ValueType, |
|||
protected callbacks: RpcStateCallbacks<V>) { |
|||
super(ctx, settings, stateValueType, callbacks); |
|||
this.executeRpcSettings = settings.executeRpc; |
|||
} |
|||
|
|||
protected doGetState(): Observable<V> { |
|||
return this.ctx.controlApi.sendTwoWayCommand(this.executeRpcSettings.method, null, |
|||
this.executeRpcSettings.requestTimeout, |
|||
this.executeRpcSettings.requestPersistent, |
|||
this.executeRpcSettings.persistentPollingInterval).pipe( |
|||
catchError((err) => { |
|||
throw handleRpcError(this.ctx, err); |
|||
}) |
|||
); |
|||
} |
|||
} |
|||
|
|||
export class RpcAttributeStateGetter<V> extends RpcInitialStateGetter<V> { |
|||
|
|||
private readonly getAttributeSettings: RpcGetAttributeSettings; |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: RpcInitialStateSettings<V>, |
|||
protected stateValueType: ValueType, |
|||
protected callbacks: RpcStateCallbacks<V>) { |
|||
super(ctx, settings, stateValueType, callbacks); |
|||
this.getAttributeSettings = settings.getAttribute; |
|||
} |
|||
|
|||
protected doGetState(): Observable<V> { |
|||
if (this.ctx.defaultSubscription.targetEntityId) { |
|||
const err = validateAttributeScope(this.ctx, this.getAttributeSettings.scope); |
|||
if (err) { |
|||
return throwError(() => err); |
|||
} |
|||
return this.ctx.attributeService.getEntityAttributes(this.ctx.defaultSubscription.targetEntityId, |
|||
this.getAttributeSettings.scope, [this.getAttributeSettings.key], {ignoreLoading: true, ignoreErrors: true}) |
|||
.pipe( |
|||
map((data) => data.find(attr => attr.key === this.getAttributeSettings.key)?.value) |
|||
); |
|||
} else { |
|||
return of(null); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
export class RpcTimeSeriesStateGetter<V> extends RpcInitialStateGetter<V> { |
|||
|
|||
private readonly getTimeSeriesSettings: RpcTelemetrySettings; |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: RpcInitialStateSettings<V>, |
|||
protected stateValueType: ValueType, |
|||
protected callbacks: RpcStateCallbacks<V>) { |
|||
super(ctx, settings, stateValueType, callbacks); |
|||
this.getTimeSeriesSettings = settings.getTimeSeries; |
|||
} |
|||
|
|||
protected doGetState(): Observable<V> { |
|||
if (this.ctx.defaultSubscription.targetEntityId) { |
|||
return this.ctx.attributeService.getEntityTimeseriesLatest(this.ctx.defaultSubscription.targetEntityId, |
|||
[this.getTimeSeriesSettings.key], true, {ignoreLoading: true, ignoreErrors: true}) |
|||
.pipe( |
|||
map((data) => { |
|||
let value: any = null; |
|||
if (data[this.getTimeSeriesSettings.key]) { |
|||
const dataSet = data[this.getTimeSeriesSettings.key]; |
|||
if (dataSet.length) { |
|||
value = dataSet[0].value; |
|||
} |
|||
} |
|||
return value; |
|||
}) |
|||
); |
|||
} else { |
|||
return of(null); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
export class ExecuteRpcStateUpdater<V> extends RpcStateUpdater<V> { |
|||
|
|||
private readonly executeRpcSettings: RpcSettings; |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: RpcUpdateStateSettings) { |
|||
super(ctx, settings); |
|||
this.executeRpcSettings = settings.executeRpc; |
|||
} |
|||
|
|||
protected doUpdateState(params: any): Observable<any> { |
|||
return this.ctx.controlApi.sendOneWayCommand(this.executeRpcSettings.method, params, |
|||
this.executeRpcSettings.requestTimeout, |
|||
this.executeRpcSettings.requestPersistent, |
|||
this.executeRpcSettings.persistentPollingInterval).pipe( |
|||
catchError((err) => { |
|||
throw handleRpcError(this.ctx, err); |
|||
}) |
|||
); |
|||
} |
|||
} |
|||
|
|||
export class RpcAttributeStateUpdater<V> extends RpcStateUpdater<V> { |
|||
|
|||
private readonly setAttributeSettings: RpcSetAttributeSettings; |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: RpcUpdateStateSettings) { |
|||
super(ctx, settings); |
|||
this.setAttributeSettings = settings.setAttribute; |
|||
} |
|||
|
|||
protected doUpdateState(params: any): Observable<any> { |
|||
if (this.ctx.defaultSubscription.targetEntityId) { |
|||
const err = validateAttributeScope(this.ctx, this.setAttributeSettings.scope); |
|||
if (err) { |
|||
return throwError(() => err); |
|||
} |
|||
const attributes: Array<AttributeData> = [{key: this.setAttributeSettings.key, value: params}]; |
|||
return this.ctx.attributeService.saveEntityAttributes(this.ctx.defaultSubscription.targetEntityId, |
|||
this.setAttributeSettings.scope, attributes, {ignoreLoading: true, ignoreErrors: true}); |
|||
} else { |
|||
return of(null); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
export class RpcTimeSeriesStateUpdater<V> extends RpcStateUpdater<V> { |
|||
|
|||
private readonly putTimeSeriesSettings: RpcTelemetrySettings; |
|||
|
|||
constructor(protected ctx: WidgetContext, |
|||
protected settings: RpcUpdateStateSettings) { |
|||
super(ctx, settings); |
|||
this.putTimeSeriesSettings = settings.putTimeSeries; |
|||
} |
|||
|
|||
protected doUpdateState(params: any): Observable<any> { |
|||
if (this.ctx.defaultSubscription.targetEntityId) { |
|||
const timeSeries: Array<AttributeData> = [{key: this.putTimeSeriesSettings.key, value: params}]; |
|||
return this.ctx.attributeService.saveEntityTimeseries(this.ctx.defaultSubscription.targetEntityId, |
|||
LatestTelemetry.LATEST_TELEMETRY, timeSeries, {ignoreLoading: true, ignoreErrors: true}); |
|||
} else { |
|||
return of(null); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
const parseError = (ctx: WidgetContext, err: any): string => |
|||
ctx.$injector.get(UtilsService).parseException(err).message || 'Unknown Error'; |
|||
|
|||
const handleRpcError = (ctx: WidgetContext, err: any): Error => { |
|||
let reason: string; |
|||
if (ctx.defaultSubscription.rpcErrorText) { |
|||
reason = ctx.defaultSubscription.rpcErrorText; |
|||
} else { |
|||
reason = parseError(ctx, err); |
|||
} |
|||
return new Error(reason); |
|||
}; |
|||
|
|||
const validateAttributeScope = (ctx: WidgetContext, scope?: AttributeScope): Error | null => { |
|||
if (ctx.defaultSubscription.targetEntityId.entityType !== EntityType.DEVICE && scope && scope !== AttributeScope.SERVER_SCOPE) { |
|||
const scopeStr = ctx.translate.instant(telemetryTypeTranslationsShort.get(scope)); |
|||
const entityType = |
|||
ctx.translate.instant(entityTypeTranslations.get(ctx.defaultSubscription.targetEntityId.entityType).type); |
|||
const errorMessage = |
|||
ctx.translate.instant('widgets.rpc-state.error.invalid-attribute-scope', {scope: scopeStr, entityType}); |
|||
return new Error(errorMessage); |
|||
} else { |
|||
return null; |
|||
} |
|||
}; |
|||
@ -0,0 +1,182 @@ |
|||
///
|
|||
/// Copyright © 2016-2024 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; |
|||
import { PageComponent } from '@shared/components/page.component'; |
|||
import { TbPopoverComponent } from '@shared/components/popover.component'; |
|||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { merge } from 'rxjs'; |
|||
import { |
|||
DataToValueType, |
|||
GetValueAction, |
|||
getValueActions, |
|||
getValueActionTranslations, |
|||
GetValueSettings |
|||
} from '@shared/models/action-widget-settings.models'; |
|||
import { ValueType } from '@shared/models/constants'; |
|||
import { TargetDevice } 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'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-get-value-action-settings-panel', |
|||
templateUrl: './get-value-action-settings-panel.component.html', |
|||
providers: [], |
|||
styleUrls: ['./value-action-settings-panel.component.scss'], |
|||
encapsulation: ViewEncapsulation.None |
|||
}) |
|||
export class GetValueActionSettingsPanelComponent extends PageComponent implements OnInit { |
|||
|
|||
@Input() |
|||
getValueSettings: GetValueSettings<any>; |
|||
|
|||
@Input() |
|||
panelTitle: string; |
|||
|
|||
@Input() |
|||
valueType: ValueType; |
|||
|
|||
@Input() |
|||
aliasController: IAliasController; |
|||
|
|||
@Input() |
|||
targetDevice: TargetDevice; |
|||
|
|||
@Input() |
|||
popover: TbPopoverComponent<GetValueActionSettingsPanelComponent>; |
|||
|
|||
@Output() |
|||
getValueSettingsApplied = new EventEmitter<GetValueSettings<any>>(); |
|||
|
|||
getValueAction = GetValueAction; |
|||
|
|||
getValueActions = getValueActions; |
|||
|
|||
getValueActionTranslationsMap = getValueActionTranslations; |
|||
|
|||
telemetryTypeTranslationsMap = telemetryTypeTranslationsShort; |
|||
|
|||
attributeScopes = Object.keys(AttributeScope) as AttributeScope[]; |
|||
|
|||
dataKeyType = DataKeyType; |
|||
|
|||
dataToValueType = DataToValueType; |
|||
|
|||
functionScopeVariables = this.widgetService.getWidgetScopeVariables(); |
|||
|
|||
ValueType = ValueType; |
|||
|
|||
getValueSettingsFormGroup: UntypedFormGroup; |
|||
|
|||
constructor(private fb: UntypedFormBuilder, |
|||
private widgetService: WidgetService, |
|||
protected store: Store<AppState>) { |
|||
super(store); |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
this.getValueSettingsFormGroup = this.fb.group( |
|||
{ |
|||
action: [this.getValueSettings?.action, []], |
|||
defaultValue: [this.getValueSettings?.defaultValue, [Validators.required]], |
|||
executeRpc: this.fb.group({ |
|||
method: [this.getValueSettings?.executeRpc?.method, [Validators.required]], |
|||
requestTimeout: [this.getValueSettings?.executeRpc?.requestTimeout, [Validators.required, Validators.min(5000)]], |
|||
requestPersistent: [this.getValueSettings?.executeRpc?.requestPersistent, []], |
|||
persistentPollingInterval: |
|||
[this.getValueSettings?.executeRpc?.persistentPollingInterval, [Validators.required, Validators.min(1000)]] |
|||
}), |
|||
getAttribute: this.fb.group({ |
|||
scope: [this.getValueSettings?.getAttribute?.scope, []], |
|||
key: [this.getValueSettings?.getAttribute?.key, [Validators.required]], |
|||
subscribeForUpdates: [this.getValueSettings?.getAttribute?.subscribeForUpdates, []] |
|||
}), |
|||
getTimeSeries: this.fb.group({ |
|||
key: [this.getValueSettings?.getTimeSeries?.key, [Validators.required]], |
|||
subscribeForUpdates: [this.getValueSettings?.getTimeSeries?.subscribeForUpdates, []] |
|||
}), |
|||
dataToValue: this.fb.group({ |
|||
type: [this.getValueSettings?.dataToValue?.type, [Validators.required]], |
|||
dataToValueFunction: [this.getValueSettings?.dataToValue?.dataToValueFunction, [Validators.required]], |
|||
}), |
|||
} |
|||
); |
|||
if (this.valueType === ValueType.BOOLEAN) { |
|||
(this.getValueSettingsFormGroup.get('dataToValue') as UntypedFormGroup).addControl( |
|||
'compareToValue', this.fb.control(this.getValueSettings?.dataToValue?.compareToValue, [Validators.required]) |
|||
); |
|||
} |
|||
|
|||
merge(this.getValueSettingsFormGroup.get('action').valueChanges, |
|||
this.getValueSettingsFormGroup.get('dataToValue').get('type').valueChanges, |
|||
this.getValueSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges).subscribe(() => { |
|||
this.updateValidators(); |
|||
}); |
|||
this.updateValidators(); |
|||
} |
|||
|
|||
cancel() { |
|||
this.popover?.hide(); |
|||
} |
|||
|
|||
applyGetValueSettings() { |
|||
const getValueSettings: GetValueSettings<any> = this.getValueSettingsFormGroup.getRawValue(); |
|||
this.getValueSettingsApplied.emit(getValueSettings); |
|||
} |
|||
|
|||
private updateValidators() { |
|||
const action: GetValueAction = this.getValueSettingsFormGroup.get('action').value; |
|||
const dataToValueType: DataToValueType = this.getValueSettingsFormGroup.get('dataToValue').get('type').value; |
|||
|
|||
this.getValueSettingsFormGroup.get('defaultValue').disable({emitEvent: false}); |
|||
this.getValueSettingsFormGroup.get('executeRpc').disable({emitEvent: false}); |
|||
this.getValueSettingsFormGroup.get('getAttribute').disable({emitEvent: false}); |
|||
this.getValueSettingsFormGroup.get('getTimeSeries').disable({emitEvent: false}); |
|||
switch (action) { |
|||
case GetValueAction.DO_NOTHING: |
|||
this.getValueSettingsFormGroup.get('defaultValue').enable({emitEvent: false}); |
|||
break; |
|||
case GetValueAction.EXECUTE_RPC: |
|||
this.getValueSettingsFormGroup.get('executeRpc').enable({emitEvent: false}); |
|||
const requestPersistent: boolean = this.getValueSettingsFormGroup.get('executeRpc').get('requestPersistent').value; |
|||
if (requestPersistent) { |
|||
this.getValueSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').enable({emitEvent: false}); |
|||
} else { |
|||
this.getValueSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').disable({emitEvent: false}); |
|||
} |
|||
break; |
|||
case GetValueAction.GET_ATTRIBUTE: |
|||
this.getValueSettingsFormGroup.get('getAttribute').enable({emitEvent: false}); |
|||
break; |
|||
case GetValueAction.GET_TIME_SERIES: |
|||
this.getValueSettingsFormGroup.get('getTimeSeries').enable({emitEvent: false}); |
|||
break; |
|||
} |
|||
if (action === GetValueAction.DO_NOTHING) { |
|||
this.getValueSettingsFormGroup.get('dataToValue').disable({emitEvent: false}); |
|||
} else { |
|||
this.getValueSettingsFormGroup.get('dataToValue').enable({emitEvent: false}); |
|||
if (dataToValueType === DataToValueType.FUNCTION) { |
|||
this.getValueSettingsFormGroup.get('dataToValue').get('dataToValueFunction').enable({emitEvent: false}); |
|||
} else { |
|||
this.getValueSettingsFormGroup.get('dataToValue').get('dataToValueFunction').disable({emitEvent: false}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,182 @@ |
|||
///
|
|||
/// Copyright © 2016-2024 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; |
|||
import { PageComponent } from '@shared/components/page.component'; |
|||
import { TbPopoverComponent } from '@shared/components/popover.component'; |
|||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { merge } from 'rxjs'; |
|||
import { |
|||
getValueActions, |
|||
SetValueAction, |
|||
setValueActions, |
|||
setValueActionTranslations, |
|||
SetValueSettings, |
|||
ValueToDataType |
|||
} from '@shared/models/action-widget-settings.models'; |
|||
import { ValueType } from '@shared/models/constants'; |
|||
import { TargetDevice } 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'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-set-value-action-settings-panel', |
|||
templateUrl: './set-value-action-settings-panel.component.html', |
|||
providers: [], |
|||
styleUrls: ['./value-action-settings-panel.component.scss'], |
|||
encapsulation: ViewEncapsulation.None |
|||
}) |
|||
export class SetValueActionSettingsPanelComponent extends PageComponent implements OnInit { |
|||
|
|||
@Input() |
|||
panelTitle: string; |
|||
|
|||
@Input() |
|||
setValueSettings: SetValueSettings; |
|||
|
|||
@Input() |
|||
valueType: ValueType; |
|||
|
|||
@Input() |
|||
aliasController: IAliasController; |
|||
|
|||
@Input() |
|||
targetDevice: TargetDevice; |
|||
|
|||
@Input() |
|||
popover: TbPopoverComponent<SetValueActionSettingsPanelComponent>; |
|||
|
|||
@Output() |
|||
setValueSettingsApplied = new EventEmitter<SetValueSettings>(); |
|||
|
|||
setValueAction = SetValueAction; |
|||
|
|||
setValueActions = setValueActions; |
|||
|
|||
setValueActionTranslationsMap = setValueActionTranslations; |
|||
|
|||
telemetryTypeTranslationsMap = telemetryTypeTranslationsShort; |
|||
|
|||
attributeScopes = [AttributeScope.SERVER_SCOPE, AttributeScope.SHARED_SCOPE]; |
|||
|
|||
dataKeyType = DataKeyType; |
|||
|
|||
valueToDataType = ValueToDataType; |
|||
|
|||
functionScopeVariables = this.widgetService.getWidgetScopeVariables(); |
|||
|
|||
ValueType = ValueType; |
|||
|
|||
setValueSettingsFormGroup: UntypedFormGroup; |
|||
|
|||
constructor(private fb: UntypedFormBuilder, |
|||
private widgetService: WidgetService, |
|||
protected store: Store<AppState>) { |
|||
super(store); |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
this.setValueSettingsFormGroup = this.fb.group( |
|||
{ |
|||
action: [this.setValueSettings?.action, []], |
|||
executeRpc: this.fb.group({ |
|||
method: [this.setValueSettings?.executeRpc?.method, [Validators.required]], |
|||
requestTimeout: [this.setValueSettings?.executeRpc?.requestTimeout, [Validators.required, Validators.min(5000)]], |
|||
requestPersistent: [this.setValueSettings?.executeRpc?.requestPersistent, []], |
|||
persistentPollingInterval: |
|||
[this.setValueSettings?.executeRpc?.persistentPollingInterval, [Validators.required, Validators.min(1000)]] |
|||
}), |
|||
setAttribute: this.fb.group({ |
|||
scope: [this.setValueSettings?.setAttribute?.scope, []], |
|||
key: [this.setValueSettings?.setAttribute?.key, [Validators.required]], |
|||
}), |
|||
putTimeSeries: this.fb.group({ |
|||
key: [this.setValueSettings?.putTimeSeries?.key, [Validators.required]], |
|||
}), |
|||
valueToData: this.fb.group({ |
|||
type: [this.setValueSettings?.valueToData?.type, [Validators.required]], |
|||
constantValue: [this.setValueSettings?.valueToData?.constantValue, [Validators.required]], |
|||
valueToDataFunction: [this.setValueSettings?.valueToData?.valueToDataFunction, [Validators.required]], |
|||
}), |
|||
} |
|||
); |
|||
|
|||
merge(this.setValueSettingsFormGroup.get('action').valueChanges, |
|||
this.setValueSettingsFormGroup.get('valueToData').get('type').valueChanges, |
|||
this.setValueSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges).subscribe(() => { |
|||
this.updateValidators(); |
|||
}); |
|||
this.updateValidators(); |
|||
} |
|||
|
|||
cancel() { |
|||
this.popover?.hide(); |
|||
} |
|||
|
|||
applySetValueSettings() { |
|||
const setValueSettings: SetValueSettings = this.setValueSettingsFormGroup.getRawValue(); |
|||
this.setValueSettingsApplied.emit(setValueSettings); |
|||
} |
|||
|
|||
private updateValidators() { |
|||
const action: SetValueAction = this.setValueSettingsFormGroup.get('action').value; |
|||
let valueToDataType: ValueToDataType = this.setValueSettingsFormGroup.get('valueToData').get('type').value; |
|||
|
|||
this.setValueSettingsFormGroup.get('executeRpc').disable({emitEvent: false}); |
|||
this.setValueSettingsFormGroup.get('setAttribute').disable({emitEvent: false}); |
|||
this.setValueSettingsFormGroup.get('putTimeSeries').disable({emitEvent: false}); |
|||
switch (action) { |
|||
case SetValueAction.EXECUTE_RPC: |
|||
this.setValueSettingsFormGroup.get('executeRpc').enable({emitEvent: false}); |
|||
const requestPersistent: boolean = this.setValueSettingsFormGroup.get('executeRpc').get('requestPersistent').value; |
|||
if (requestPersistent) { |
|||
this.setValueSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').enable({emitEvent: false}); |
|||
} else { |
|||
this.setValueSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').disable({emitEvent: false}); |
|||
} |
|||
break; |
|||
case SetValueAction.SET_ATTRIBUTE: |
|||
case SetValueAction.ADD_TIME_SERIES: |
|||
if (valueToDataType === ValueToDataType.NONE) { |
|||
valueToDataType = ValueToDataType.CONSTANT; |
|||
this.setValueSettingsFormGroup.get('valueToData').get('type').patchValue(valueToDataType, {emitEvent: false}); |
|||
} |
|||
if (action === SetValueAction.SET_ATTRIBUTE) { |
|||
this.setValueSettingsFormGroup.get('setAttribute').enable({emitEvent: false}); |
|||
} else { |
|||
this.setValueSettingsFormGroup.get('putTimeSeries').enable({emitEvent: false}); |
|||
} |
|||
break; |
|||
} |
|||
switch (valueToDataType) { |
|||
case ValueToDataType.CONSTANT: |
|||
this.setValueSettingsFormGroup.get('valueToData').get('constantValue').enable({emitEvent: false}); |
|||
this.setValueSettingsFormGroup.get('valueToData').get('valueToDataFunction').disable({emitEvent: false}); |
|||
break; |
|||
case ValueToDataType.FUNCTION: |
|||
this.setValueSettingsFormGroup.get('valueToData').get('constantValue').disable({emitEvent: false}); |
|||
this.setValueSettingsFormGroup.get('valueToData').get('valueToDataFunction').enable({emitEvent: false}); |
|||
break; |
|||
case ValueToDataType.NONE: |
|||
this.setValueSettingsFormGroup.get('valueToData').get('constantValue').disable({emitEvent: false}); |
|||
this.setValueSettingsFormGroup.get('valueToData').get('valueToDataFunction').disable({emitEvent: false}); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
@ -1,176 +0,0 @@ |
|||
///
|
|||
/// Copyright © 2016-2024 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; |
|||
import { PageComponent } from '@shared/components/page.component'; |
|||
import { TbPopoverComponent } from '@shared/components/popover.component'; |
|||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { merge } from 'rxjs'; |
|||
import { |
|||
RpcDataToStateType, |
|||
RpcInitialStateAction, |
|||
rpcInitialStateActions, |
|||
RpcInitialStateSettings, |
|||
rpcInitialStateTranslations |
|||
} from '@shared/models/rpc-widget-settings.models'; |
|||
import { ValueType } from '@shared/models/constants'; |
|||
import { TargetDevice } 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'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-rpc-initial-state-settings-panel', |
|||
templateUrl: './rpc-initial-state-settings-panel.component.html', |
|||
providers: [], |
|||
styleUrls: ['./rpc-state-settings-panel.component.scss'], |
|||
encapsulation: ViewEncapsulation.None |
|||
}) |
|||
export class RpcInitialStateSettingsPanelComponent extends PageComponent implements OnInit { |
|||
|
|||
@Input() |
|||
initialState: RpcInitialStateSettings<any>; |
|||
|
|||
@Input() |
|||
stateValueType: ValueType; |
|||
|
|||
@Input() |
|||
aliasController: IAliasController; |
|||
|
|||
@Input() |
|||
targetDevice: TargetDevice; |
|||
|
|||
@Input() |
|||
popover: TbPopoverComponent<RpcInitialStateSettingsPanelComponent>; |
|||
|
|||
@Output() |
|||
initialStateSettingsApplied = new EventEmitter<RpcInitialStateSettings<any>>(); |
|||
|
|||
rpcInitialStateAction = RpcInitialStateAction; |
|||
|
|||
rpcInitialStateActions = rpcInitialStateActions; |
|||
|
|||
rpcInitialStateTranslationsMap = rpcInitialStateTranslations; |
|||
|
|||
telemetryTypeTranslationsMap = telemetryTypeTranslationsShort; |
|||
|
|||
attributeScopes = Object.keys(AttributeScope) as AttributeScope[]; |
|||
|
|||
dataKeyType = DataKeyType; |
|||
|
|||
dataToStateType = RpcDataToStateType; |
|||
|
|||
functionScopeVariables = this.widgetService.getWidgetScopeVariables(); |
|||
|
|||
valueType = ValueType; |
|||
|
|||
initialStateSettingsFormGroup: UntypedFormGroup; |
|||
|
|||
constructor(private fb: UntypedFormBuilder, |
|||
private widgetService: WidgetService, |
|||
protected store: Store<AppState>) { |
|||
super(store); |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
this.initialStateSettingsFormGroup = this.fb.group( |
|||
{ |
|||
action: [this.initialState?.action, []], |
|||
defaultValue: [this.initialState?.defaultValue, [Validators.required]], |
|||
executeRpc: this.fb.group({ |
|||
method: [this.initialState?.executeRpc?.method, [Validators.required]], |
|||
requestTimeout: [this.initialState?.executeRpc?.requestTimeout, [Validators.required, Validators.min(5000)]], |
|||
requestPersistent: [this.initialState?.executeRpc?.requestPersistent, []], |
|||
persistentPollingInterval: [this.initialState?.executeRpc?.persistentPollingInterval, [Validators.required, Validators.min(1000)]] |
|||
}), |
|||
getAttribute: this.fb.group({ |
|||
scope: [this.initialState?.getAttribute?.scope, []], |
|||
key: [this.initialState?.getAttribute?.key, [Validators.required]], |
|||
}), |
|||
getTimeSeries: this.fb.group({ |
|||
key: [this.initialState?.getTimeSeries?.key, [Validators.required]], |
|||
}), |
|||
dataToState: this.fb.group({ |
|||
type: [this.initialState?.dataToState?.type, [Validators.required]], |
|||
dataToStateFunction: [this.initialState?.dataToState?.dataToStateFunction, [Validators.required]], |
|||
}), |
|||
} |
|||
); |
|||
if (this.stateValueType === ValueType.BOOLEAN) { |
|||
(this.initialStateSettingsFormGroup.get('dataToState') as UntypedFormGroup).addControl( |
|||
'compareToValue', this.fb.control(this.initialState?.dataToState?.compareToValue, [Validators.required]) |
|||
); |
|||
} |
|||
|
|||
merge(this.initialStateSettingsFormGroup.get('action').valueChanges, |
|||
this.initialStateSettingsFormGroup.get('dataToState').get('type').valueChanges, |
|||
this.initialStateSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges).subscribe(() => { |
|||
this.updateValidators(); |
|||
}); |
|||
this.updateValidators(); |
|||
} |
|||
|
|||
cancel() { |
|||
this.popover?.hide(); |
|||
} |
|||
|
|||
applyInitialStateSettings() { |
|||
const initialStateSettings: RpcInitialStateSettings<any> = this.initialStateSettingsFormGroup.getRawValue(); |
|||
this.initialStateSettingsApplied.emit(initialStateSettings); |
|||
} |
|||
|
|||
private updateValidators() { |
|||
const action: RpcInitialStateAction = this.initialStateSettingsFormGroup.get('action').value; |
|||
const dataToStateType: RpcDataToStateType = this.initialStateSettingsFormGroup.get('dataToState').get('type').value; |
|||
|
|||
this.initialStateSettingsFormGroup.get('defaultValue').disable({emitEvent: false}); |
|||
this.initialStateSettingsFormGroup.get('executeRpc').disable({emitEvent: false}); |
|||
this.initialStateSettingsFormGroup.get('getAttribute').disable({emitEvent: false}); |
|||
this.initialStateSettingsFormGroup.get('getTimeSeries').disable({emitEvent: false}); |
|||
switch (action) { |
|||
case RpcInitialStateAction.DO_NOTHING: |
|||
this.initialStateSettingsFormGroup.get('defaultValue').enable({emitEvent: false}); |
|||
break; |
|||
case RpcInitialStateAction.EXECUTE_RPC: |
|||
this.initialStateSettingsFormGroup.get('executeRpc').enable({emitEvent: false}); |
|||
const requestPersistent: boolean = this.initialStateSettingsFormGroup.get('executeRpc').get('requestPersistent').value; |
|||
if (requestPersistent) { |
|||
this.initialStateSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').enable({emitEvent: false}); |
|||
} else { |
|||
this.initialStateSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').disable({emitEvent: false}); |
|||
} |
|||
break; |
|||
case RpcInitialStateAction.GET_ATTRIBUTE: |
|||
this.initialStateSettingsFormGroup.get('getAttribute').enable({emitEvent: false}); |
|||
break; |
|||
case RpcInitialStateAction.GET_TIME_SERIES: |
|||
this.initialStateSettingsFormGroup.get('getTimeSeries').enable({emitEvent: false}); |
|||
break; |
|||
} |
|||
if (action === RpcInitialStateAction.DO_NOTHING) { |
|||
this.initialStateSettingsFormGroup.get('dataToState').disable({emitEvent: false}); |
|||
} else { |
|||
this.initialStateSettingsFormGroup.get('dataToState').enable({emitEvent: false}); |
|||
if (dataToStateType === RpcDataToStateType.FUNCTION) { |
|||
this.initialStateSettingsFormGroup.get('dataToState').get('dataToStateFunction').enable({emitEvent: false}); |
|||
} else { |
|||
this.initialStateSettingsFormGroup.get('dataToState').get('dataToStateFunction').disable({emitEvent: false}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,180 +0,0 @@ |
|||
///
|
|||
/// Copyright © 2016-2024 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; |
|||
import { PageComponent } from '@shared/components/page.component'; |
|||
import { TbPopoverComponent } from '@shared/components/popover.component'; |
|||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { merge } from 'rxjs'; |
|||
import { |
|||
RpcStateToParamsType, |
|||
RpcUpdateStateAction, |
|||
rpcUpdateStateActions, |
|||
RpcUpdateStateSettings, |
|||
rpcUpdateStateTranslations |
|||
} from '@shared/models/rpc-widget-settings.models'; |
|||
import { ValueType } from '@shared/models/constants'; |
|||
import { TargetDevice } 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'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-rpc-update-state-settings-panel', |
|||
templateUrl: './rpc-update-state-settings-panel.component.html', |
|||
providers: [], |
|||
styleUrls: ['./rpc-state-settings-panel.component.scss'], |
|||
encapsulation: ViewEncapsulation.None |
|||
}) |
|||
export class RpcUpdateStateSettingsPanelComponent extends PageComponent implements OnInit { |
|||
|
|||
@Input() |
|||
panelTitle: string; |
|||
|
|||
@Input() |
|||
updateState: RpcUpdateStateSettings; |
|||
|
|||
@Input() |
|||
stateValueType: ValueType; |
|||
|
|||
@Input() |
|||
aliasController: IAliasController; |
|||
|
|||
@Input() |
|||
targetDevice: TargetDevice; |
|||
|
|||
@Input() |
|||
popover: TbPopoverComponent<RpcUpdateStateSettingsPanelComponent>; |
|||
|
|||
@Output() |
|||
updateStateSettingsApplied = new EventEmitter<RpcUpdateStateSettings>(); |
|||
|
|||
rpcUpdateStateAction = RpcUpdateStateAction; |
|||
|
|||
rpcUpdateStateActions = rpcUpdateStateActions; |
|||
|
|||
rpcUpdateStateTranslationsMap = rpcUpdateStateTranslations; |
|||
|
|||
telemetryTypeTranslationsMap = telemetryTypeTranslationsShort; |
|||
|
|||
attributeScopes = [AttributeScope.SERVER_SCOPE, AttributeScope.SHARED_SCOPE]; |
|||
|
|||
dataKeyType = DataKeyType; |
|||
|
|||
stateToParamsType = RpcStateToParamsType; |
|||
|
|||
functionScopeVariables = this.widgetService.getWidgetScopeVariables(); |
|||
|
|||
valueType = ValueType; |
|||
|
|||
updateStateSettingsFormGroup: UntypedFormGroup; |
|||
|
|||
constructor(private fb: UntypedFormBuilder, |
|||
private widgetService: WidgetService, |
|||
protected store: Store<AppState>) { |
|||
super(store); |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
this.updateStateSettingsFormGroup = this.fb.group( |
|||
{ |
|||
action: [this.updateState?.action, []], |
|||
executeRpc: this.fb.group({ |
|||
method: [this.updateState?.executeRpc?.method, [Validators.required]], |
|||
requestTimeout: [this.updateState?.executeRpc?.requestTimeout, [Validators.required, Validators.min(5000)]], |
|||
requestPersistent: [this.updateState?.executeRpc?.requestPersistent, []], |
|||
persistentPollingInterval: [this.updateState?.executeRpc?.persistentPollingInterval, [Validators.required, Validators.min(1000)]] |
|||
}), |
|||
setAttribute: this.fb.group({ |
|||
scope: [this.updateState?.setAttribute?.scope, []], |
|||
key: [this.updateState?.setAttribute?.key, [Validators.required]], |
|||
}), |
|||
putTimeSeries: this.fb.group({ |
|||
key: [this.updateState?.putTimeSeries?.key, [Validators.required]], |
|||
}), |
|||
stateToParams: this.fb.group({ |
|||
type: [this.updateState?.stateToParams?.type, [Validators.required]], |
|||
constantValue: [this.updateState?.stateToParams?.constantValue, [Validators.required]], |
|||
stateToParamsFunction: [this.updateState?.stateToParams?.stateToParamsFunction, [Validators.required]], |
|||
}), |
|||
} |
|||
); |
|||
|
|||
merge(this.updateStateSettingsFormGroup.get('action').valueChanges, |
|||
this.updateStateSettingsFormGroup.get('stateToParams').get('type').valueChanges, |
|||
this.updateStateSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges).subscribe(() => { |
|||
this.updateValidators(); |
|||
}); |
|||
this.updateValidators(); |
|||
} |
|||
|
|||
cancel() { |
|||
this.popover?.hide(); |
|||
} |
|||
|
|||
applyUpdateStateSettings() { |
|||
const updateStateSettings: RpcUpdateStateSettings = this.updateStateSettingsFormGroup.getRawValue(); |
|||
this.updateStateSettingsApplied.emit(updateStateSettings); |
|||
} |
|||
|
|||
private updateValidators() { |
|||
const action: RpcUpdateStateAction = this.updateStateSettingsFormGroup.get('action').value; |
|||
let stateToParamsType: RpcStateToParamsType = this.updateStateSettingsFormGroup.get('stateToParams').get('type').value; |
|||
|
|||
this.updateStateSettingsFormGroup.get('executeRpc').disable({emitEvent: false}); |
|||
this.updateStateSettingsFormGroup.get('setAttribute').disable({emitEvent: false}); |
|||
this.updateStateSettingsFormGroup.get('putTimeSeries').disable({emitEvent: false}); |
|||
switch (action) { |
|||
case RpcUpdateStateAction.EXECUTE_RPC: |
|||
this.updateStateSettingsFormGroup.get('executeRpc').enable({emitEvent: false}); |
|||
const requestPersistent: boolean = this.updateStateSettingsFormGroup.get('executeRpc').get('requestPersistent').value; |
|||
if (requestPersistent) { |
|||
this.updateStateSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').enable({emitEvent: false}); |
|||
} else { |
|||
this.updateStateSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').disable({emitEvent: false}); |
|||
} |
|||
break; |
|||
case RpcUpdateStateAction.SET_ATTRIBUTE: |
|||
case RpcUpdateStateAction.ADD_TIME_SERIES: |
|||
if (stateToParamsType === RpcStateToParamsType.NONE) { |
|||
stateToParamsType = RpcStateToParamsType.CONSTANT; |
|||
this.updateStateSettingsFormGroup.get('stateToParams').get('type').patchValue(stateToParamsType, {emitEvent: false}); |
|||
} |
|||
if (action === RpcUpdateStateAction.SET_ATTRIBUTE) { |
|||
this.updateStateSettingsFormGroup.get('setAttribute').enable({emitEvent: false}); |
|||
} else { |
|||
this.updateStateSettingsFormGroup.get('putTimeSeries').enable({emitEvent: false}); |
|||
} |
|||
break; |
|||
} |
|||
switch (stateToParamsType) { |
|||
case RpcStateToParamsType.CONSTANT: |
|||
this.updateStateSettingsFormGroup.get('stateToParams').get('constantValue').enable({emitEvent: false}); |
|||
this.updateStateSettingsFormGroup.get('stateToParams').get('stateToParamsFunction').disable({emitEvent: false}); |
|||
break; |
|||
case RpcStateToParamsType.FUNCTION: |
|||
this.updateStateSettingsFormGroup.get('stateToParams').get('constantValue').disable({emitEvent: false}); |
|||
this.updateStateSettingsFormGroup.get('stateToParams').get('stateToParamsFunction').enable({emitEvent: false}); |
|||
break; |
|||
case RpcStateToParamsType.NONE: |
|||
this.updateStateSettingsFormGroup.get('stateToParams').get('constantValue').disable({emitEvent: false}); |
|||
this.updateStateSettingsFormGroup.get('stateToParams').get('stateToParamsFunction').disable({emitEvent: false}); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,128 @@ |
|||
///
|
|||
/// Copyright © 2016-2024 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; |
|||
|
|||
export enum GetValueAction { |
|||
DO_NOTHING = 'DO_NOTHING', |
|||
EXECUTE_RPC = 'EXECUTE_RPC', |
|||
GET_ATTRIBUTE = 'GET_ATTRIBUTE', |
|||
GET_TIME_SERIES = 'GET_TIME_SERIES' |
|||
} |
|||
|
|||
export const getValueActions = Object.keys(GetValueAction) as GetValueAction[]; |
|||
|
|||
export const getValueActionTranslations = new Map<GetValueAction, string>( |
|||
[ |
|||
[GetValueAction.DO_NOTHING, 'widgets.value-action.do-nothing'], |
|||
[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'] |
|||
] |
|||
); |
|||
|
|||
export interface RpcSettings { |
|||
method: string; |
|||
requestTimeout: number; |
|||
requestPersistent: boolean; |
|||
persistentPollingInterval: number; |
|||
} |
|||
|
|||
export interface TelemetryValueSettings { |
|||
key: string; |
|||
} |
|||
|
|||
export interface GetTelemetryValueSettings extends TelemetryValueSettings { |
|||
subscribeForUpdates: boolean; |
|||
} |
|||
|
|||
export interface GetAttributeValueSettings extends GetTelemetryValueSettings { |
|||
scope: AttributeScope | null; |
|||
} |
|||
|
|||
export interface SetAttributeValueSettings extends TelemetryValueSettings { |
|||
scope: AttributeScope.SERVER_SCOPE | AttributeScope.SHARED_SCOPE; |
|||
} |
|||
|
|||
export enum DataToValueType { |
|||
NONE = 'NONE', |
|||
FUNCTION = 'FUNCTION' |
|||
} |
|||
|
|||
export interface DataToValueSettings { |
|||
type: DataToValueType; |
|||
dataToValueFunction: string; |
|||
compareToValue?: any; |
|||
} |
|||
|
|||
export interface ValueActionSettings { |
|||
actionLabel?: string; |
|||
} |
|||
|
|||
export interface GetValueSettings<V> extends ValueActionSettings { |
|||
action: GetValueAction; |
|||
defaultValue: V; |
|||
executeRpc: RpcSettings; |
|||
getAttribute: GetAttributeValueSettings; |
|||
getTimeSeries: GetTelemetryValueSettings; |
|||
dataToValue: DataToValueSettings; |
|||
} |
|||
|
|||
export enum SetValueAction { |
|||
EXECUTE_RPC = 'EXECUTE_RPC', |
|||
SET_ATTRIBUTE = 'SET_ATTRIBUTE', |
|||
ADD_TIME_SERIES = 'ADD_TIME_SERIES' |
|||
} |
|||
|
|||
export const setValueActions = Object.keys(SetValueAction) as SetValueAction[]; |
|||
|
|||
export const setValueActionTranslations = new Map<SetValueAction, string>( |
|||
[ |
|||
[SetValueAction.EXECUTE_RPC, 'widgets.value-action.execute-rpc'], |
|||
[SetValueAction.SET_ATTRIBUTE, 'widgets.value-action.set-attribute'], |
|||
[SetValueAction.ADD_TIME_SERIES, 'widgets.value-action.add-time-series'] |
|||
] |
|||
); |
|||
|
|||
export enum ValueToDataType { |
|||
CONSTANT = 'CONSTANT', |
|||
FUNCTION = 'FUNCTION', |
|||
NONE = 'NONE' |
|||
} |
|||
|
|||
export interface ValueToDataSettings { |
|||
type: ValueToDataType; |
|||
constantValue: any; |
|||
valueToDataFunction: string; |
|||
} |
|||
|
|||
export interface SetValueSettings extends ValueActionSettings { |
|||
action: SetValueAction; |
|||
executeRpc: RpcSettings; |
|||
setAttribute: SetAttributeValueSettings; |
|||
putTimeSeries: TelemetryValueSettings; |
|||
valueToData: ValueToDataSettings; |
|||
} |
|||
|
|||
/*export interface RpcStateBehaviourSettings<V> { |
|||
initialState: RpcInitialStateSettings<V>; |
|||
updateStateByValue: (value: V) => RpcUpdateStateSettings; |
|||
} |
|||
|
|||
export interface RpcStateWidgetSettings<V> { |
|||
initialState: RpcInitialStateSettings<V>; |
|||
background: BackgroundSettings; |
|||
}*/ |
|||
@ -1,125 +0,0 @@ |
|||
///
|
|||
/// Copyright © 2016-2024 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; |
|||
import { BackgroundSettings } from '@shared/models/widget-settings.models'; |
|||
|
|||
export enum RpcInitialStateAction { |
|||
DO_NOTHING = 'DO_NOTHING', |
|||
EXECUTE_RPC = 'EXECUTE_RPC', |
|||
GET_ATTRIBUTE = 'GET_ATTRIBUTE', |
|||
GET_TIME_SERIES = 'GET_TIME_SERIES' |
|||
} |
|||
|
|||
export const rpcInitialStateActions = Object.keys(RpcInitialStateAction) as RpcInitialStateAction[]; |
|||
|
|||
export const rpcInitialStateTranslations = new Map<RpcInitialStateAction, string>( |
|||
[ |
|||
[RpcInitialStateAction.DO_NOTHING, 'widgets.rpc-state.do-nothing'], |
|||
[RpcInitialStateAction.EXECUTE_RPC, 'widgets.rpc-state.execute-rpc'], |
|||
[RpcInitialStateAction.GET_ATTRIBUTE, 'widgets.rpc-state.get-attribute'], |
|||
[RpcInitialStateAction.GET_TIME_SERIES, 'widgets.rpc-state.get-time-series'] |
|||
] |
|||
); |
|||
|
|||
export interface RpcSettings { |
|||
method: string; |
|||
requestTimeout: number; |
|||
requestPersistent: boolean; |
|||
persistentPollingInterval: number; |
|||
} |
|||
|
|||
export interface RpcTelemetrySettings { |
|||
key: string; |
|||
} |
|||
|
|||
export interface RpcGetAttributeSettings extends RpcTelemetrySettings { |
|||
scope: AttributeScope | null; |
|||
} |
|||
|
|||
export interface RpcSetAttributeSettings extends RpcTelemetrySettings { |
|||
scope: AttributeScope.SERVER_SCOPE | AttributeScope.SHARED_SCOPE; |
|||
} |
|||
|
|||
export enum RpcDataToStateType { |
|||
NONE = 'NONE', |
|||
FUNCTION = 'FUNCTION' |
|||
} |
|||
|
|||
export interface RpcDataToStateSettings { |
|||
type: RpcDataToStateType; |
|||
dataToStateFunction: string; |
|||
compareToValue?: any; |
|||
} |
|||
|
|||
export interface RpcActionSettings { |
|||
actionLabel?: string; |
|||
} |
|||
|
|||
export interface RpcInitialStateSettings<V> extends RpcActionSettings { |
|||
action: RpcInitialStateAction; |
|||
defaultValue: V; |
|||
executeRpc: RpcSettings; |
|||
getAttribute: RpcGetAttributeSettings; |
|||
getTimeSeries: RpcTelemetrySettings; |
|||
dataToState: RpcDataToStateSettings; |
|||
} |
|||
|
|||
export enum RpcUpdateStateAction { |
|||
EXECUTE_RPC = 'EXECUTE_RPC', |
|||
SET_ATTRIBUTE = 'SET_ATTRIBUTE', |
|||
ADD_TIME_SERIES = 'ADD_TIME_SERIES' |
|||
} |
|||
|
|||
export const rpcUpdateStateActions = Object.keys(RpcUpdateStateAction) as RpcUpdateStateAction[]; |
|||
|
|||
export const rpcUpdateStateTranslations = new Map<RpcUpdateStateAction, string>( |
|||
[ |
|||
[RpcUpdateStateAction.EXECUTE_RPC, 'widgets.rpc-state.execute-rpc'], |
|||
[RpcUpdateStateAction.SET_ATTRIBUTE, 'widgets.rpc-state.set-attribute'], |
|||
[RpcUpdateStateAction.ADD_TIME_SERIES, 'widgets.rpc-state.add-time-series'] |
|||
] |
|||
); |
|||
|
|||
export enum RpcStateToParamsType { |
|||
CONSTANT = 'CONSTANT', |
|||
FUNCTION = 'FUNCTION', |
|||
NONE = 'NONE' |
|||
} |
|||
|
|||
export interface RpcStateToParamsSettings { |
|||
type: RpcStateToParamsType; |
|||
constantValue: any; |
|||
stateToParamsFunction: string; |
|||
} |
|||
|
|||
export interface RpcUpdateStateSettings extends RpcActionSettings { |
|||
action: RpcUpdateStateAction; |
|||
executeRpc: RpcSettings; |
|||
setAttribute: RpcSetAttributeSettings; |
|||
putTimeSeries: RpcTelemetrySettings; |
|||
stateToParams: RpcStateToParamsSettings; |
|||
} |
|||
|
|||
export interface RpcStateBehaviourSettings<V> { |
|||
initialState: RpcInitialStateSettings<V>; |
|||
updateStateByValue: (value: V) => RpcUpdateStateSettings; |
|||
} |
|||
|
|||
export interface RpcStateWidgetSettings<V> { |
|||
initialState: RpcInitialStateSettings<V>; |
|||
background: BackgroundSettings; |
|||
} |
|||
Loading…
Reference in new issue