thingsboard/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts

960 lines
34 KiB
TypeScript
Raw Normal View History

2019-09-25 19:37:29 +03:00
///
2024-01-09 10:46:16 +02:00
/// Copyright © 2016-2024 The Thingsboard Authors
2019-09-25 19:37:29 +03:00
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {
ChangeDetectorRef,
Component,
ComponentFactoryResolver,
ComponentRef,
forwardRef,
Input,
OnDestroy,
OnInit,
ViewChild,
ViewContainerRef
} from '@angular/core';
2019-09-25 19:37:29 +03:00
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import {
DataKey,
2023-01-30 16:35:19 +02:00
datasourcesHasAggregation,
datasourcesHasOnlyComparisonAggregation,
GroupInfo,
2022-03-30 11:14:54 +03:00
JsonSchema,
JsonSettingsSchema,
Widget,
WidgetConfigMode,
2019-10-24 19:52:19 +03:00
widgetType
2019-09-25 19:37:29 +03:00
} from '@shared/models/widget.models';
import {
AsyncValidator,
ControlValueAccessor,
NG_ASYNC_VALIDATORS,
NG_VALUE_ACCESSOR,
2023-02-02 15:55:06 +02:00
UntypedFormBuilder,
UntypedFormControl,
UntypedFormGroup,
ValidationErrors,
Validators
} from '@angular/forms';
2019-09-25 19:37:29 +03:00
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
2023-01-30 16:35:19 +02:00
import { deepClone, genNextLabel, isDefined, isObject } from '@app/core/utils';
import { alarmFields, AlarmSearchStatus } from '@shared/models/alarm.models';
2019-09-25 19:37:29 +03:00
import { IAliasController } from '@core/api/widget-api.models';
import { EntityAlias } from '@shared/models/alias.models';
import { UtilsService } from '@core/services/utils.service';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { TranslateService } from '@ngx-translate/core';
import { EntityType } from '@shared/models/entity-type.models';
import { Observable, of, Subject, Subscription } from 'rxjs';
import {
IBasicWidgetConfigComponent,
WidgetConfigCallbacks
} from '@home/components/widget/config/widget-config.component.models';
import {
EntityAliasDialogComponent,
EntityAliasDialogData
} from '@home/components/alias/entity-alias-dialog.component';
import { catchError, map, mergeMap, tap } from 'rxjs/operators';
2023-02-17 19:24:01 +02:00
import { MatDialog } from '@angular/material/dialog';
import { EntityService } from '@core/http/entity.service';
2019-10-21 19:57:18 +03:00
import { JsonFormComponentData } from '@shared/components/json-form/json-form-component.models';
import { Dashboard } from '@shared/models/dashboard.models';
2020-02-21 19:04:49 +02:00
import { entityFields } from '@shared/models/entity.models';
import { Filter, singleEntityFilterFromDeviceId } from '@shared/models/query/query.models';
import { FilterDialogComponent, FilterDialogData } from '@home/components/filter/filter-dialog.component';
2023-05-16 20:00:53 +03:00
import { ToggleHeaderOption } from '@shared/components/toggle-header.component';
import { coerceBoolean } from '@shared/decorators/coercion';
import { basicWidgetConfigComponentsMap } from '@home/components/widget/config/basic/basic-widget-config.module';
import { TimewindowConfigData } from '@home/components/widget/config/timewindow-config-panel.component';
import Timeout = NodeJS.Timeout;
2019-10-21 19:57:18 +03:00
2020-02-24 17:16:02 +02:00
const emptySettingsSchema: JsonSchema = {
2019-10-21 19:57:18 +03:00
type: 'object',
properties: {}
};
2020-02-24 17:16:02 +02:00
const emptySettingsGroupInfoes: GroupInfo[] = [];
2019-10-21 19:57:18 +03:00
const defaultSettingsForm = [
'*'
];
2019-09-25 19:37:29 +03:00
@Component({
selector: 'tb-widget-config',
templateUrl: './widget-config.component.html',
styleUrls: ['./widget-config.component.scss'],
2019-09-25 19:37:29 +03:00
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => WidgetConfigComponent),
multi: true
},
{
provide: NG_ASYNC_VALIDATORS,
2019-09-25 19:37:29 +03:00
useExisting: forwardRef(() => WidgetConfigComponent),
multi: true,
}
]
})
export class WidgetConfigComponent extends PageComponent implements OnInit, OnDestroy, ControlValueAccessor, AsyncValidator {
@ViewChild('basicModeContainer', {read: ViewContainerRef, static: false}) basicModeContainer: ViewContainerRef;
2019-09-25 19:37:29 +03:00
widgetTypes = widgetType;
widgetConfigModes = WidgetConfigMode;
entityTypes = EntityType;
2019-09-25 19:37:29 +03:00
@Input()
forceExpandDatasources: boolean;
@Input()
aliasController: IAliasController;
@Input()
dashboard: Dashboard;
@Input()
widget: Widget;
2019-09-25 19:37:29 +03:00
@Input()
functionsOnly: boolean;
@Input()
@coerceBoolean()
hideHeader = false;
@Input()
@coerceBoolean()
hideToggleHeader = false;
@Input()
@coerceBoolean()
isAdd = false;
2019-09-25 19:37:29 +03:00
@Input() disabled: boolean;
widgetConfigMode = WidgetConfigMode.advanced;
widgetType: widgetType;
widgetConfigCallbacks: WidgetConfigCallbacks = {
createEntityAlias: this.createEntityAlias.bind(this),
createFilter: this.createFilter.bind(this),
generateDataKey: this.generateDataKey.bind(this),
fetchEntityKeysForDevice: this.fetchEntityKeysForDevice.bind(this),
2019-10-24 19:52:19 +03:00
fetchEntityKeys: this.fetchEntityKeys.bind(this),
fetchDashboardStates: this.fetchDashboardStates.bind(this)
};
widgetEditMode = this.utils.widgetEditMode;
basicModeDirectiveError: string;
2019-12-23 14:36:44 +02:00
modelValue: WidgetConfigComponentData;
2019-09-25 19:37:29 +03:00
private propagateChange = null;
2023-05-16 20:00:53 +03:00
headerOptions: ToggleHeaderOption[] = [];
selectedOption: string;
2023-02-02 15:55:06 +02:00
public dataSettings: UntypedFormGroup;
public targetDeviceSettings: UntypedFormGroup;
public widgetSettings: UntypedFormGroup;
public layoutSettings: UntypedFormGroup;
public advancedSettings: UntypedFormGroup;
public actionsSettings: UntypedFormGroup;
private createBasicModeComponentTimeout: Timeout;
private basicModeComponentRef: ComponentRef<IBasicWidgetConfigComponent>;
private basicModeComponent: IBasicWidgetConfigComponent;
private basicModeComponent$: Subject<IBasicWidgetConfigComponent> = null;
private basicModeComponentChangeSubscription: Subscription;
private dataSettingsChangesSubscription: Subscription;
private targetDeviceSettingsSubscription: Subscription;
2019-10-24 19:52:19 +03:00
private widgetSettingsSubscription: Subscription;
private layoutSettingsSubscription: Subscription;
2019-10-21 19:57:18 +03:00
private advancedSettingsSubscription: Subscription;
2019-10-24 19:52:19 +03:00
private actionsSettingsSubscription: Subscription;
private defaultConfigFormsType: widgetType;
constructor(protected store: Store<AppState>,
private utils: UtilsService,
private entityService: EntityService,
private dialog: MatDialog,
2023-05-19 17:54:27 +03:00
public translate: TranslateService,
private cfr: ComponentFactoryResolver,
private fb: UntypedFormBuilder,
private cd: ChangeDetectorRef) {
2019-09-25 19:37:29 +03:00
super(store);
}
ngOnInit(): void {
2019-10-31 18:33:51 +02:00
this.dataSettings = this.fb.group({});
this.targetDeviceSettings = this.fb.group({});
this.advancedSettings = this.fb.group({});
2019-10-24 19:52:19 +03:00
this.widgetSettings = this.fb.group({
title: [null, []],
titleFont: [null, []],
titleColor: [null, []],
2019-10-24 19:52:19 +03:00
showTitleIcon: [null, []],
titleIcon: [null, []],
iconColor: [null, []],
iconSize: [null, []],
2020-02-25 19:11:25 +02:00
titleTooltip: [null, []],
2019-10-24 19:52:19 +03:00
showTitle: [null, []],
dropShadow: [null, []],
enableFullscreen: [null, []],
backgroundColor: [null, []],
color: [null, []],
padding: [null, []],
margin: [null, []],
borderRadius: [null, []],
2019-10-24 19:52:19 +03:00
widgetStyle: [null, []],
widgetCss: [null, []],
2019-10-24 19:52:19 +03:00
titleStyle: [null, []],
pageSize: [1024, [Validators.min(1), Validators.pattern(/^\d*$/)]],
2019-10-24 19:52:19 +03:00
units: [null, []],
decimals: [null, [Validators.min(0), Validators.max(15), Validators.pattern(/^\d*$/)]],
2023-05-16 20:00:53 +03:00
noDataDisplayMessage: [null, []]
2019-10-24 19:52:19 +03:00
});
2021-10-27 13:33:51 +03:00
2023-05-19 17:54:27 +03:00
this.widgetSettings.get('showTitle').valueChanges.subscribe(() => {
this.updateWidgetSettingsEnabledState();
});
this.widgetSettings.get('showTitleIcon').valueChanges.subscribe(() => {
this.updateWidgetSettingsEnabledState();
2019-10-24 19:52:19 +03:00
});
2023-05-19 17:54:27 +03:00
2019-10-24 19:52:19 +03:00
this.layoutSettings = this.fb.group({
mobileOrder: [null, [Validators.pattern(/^-?[0-9]+$/)]],
mobileHeight: [null, [Validators.min(1), Validators.pattern(/^\d*$/)]],
mobileHide: [false],
desktopHide: [false]
2019-10-24 19:52:19 +03:00
});
this.actionsSettings = this.fb.group({
actions: [null, []]
2019-10-24 19:52:19 +03:00
});
}
2021-10-27 13:33:51 +03:00
ngOnDestroy(): void {
this.destroyBasicModeComponent();
2021-10-27 13:33:51 +03:00
this.removeChangeSubscriptions();
}
private removeChangeSubscriptions() {
if (this.dataSettingsChangesSubscription) {
this.dataSettingsChangesSubscription.unsubscribe();
this.dataSettingsChangesSubscription = null;
}
if (this.targetDeviceSettingsSubscription) {
this.targetDeviceSettingsSubscription.unsubscribe();
this.targetDeviceSettingsSubscription = null;
}
2019-10-24 19:52:19 +03:00
if (this.widgetSettingsSubscription) {
this.widgetSettingsSubscription.unsubscribe();
this.widgetSettingsSubscription = null;
}
if (this.layoutSettingsSubscription) {
this.layoutSettingsSubscription.unsubscribe();
this.layoutSettingsSubscription = null;
}
2019-10-21 19:57:18 +03:00
if (this.advancedSettingsSubscription) {
this.advancedSettingsSubscription.unsubscribe();
this.advancedSettingsSubscription = null;
}
2019-10-24 19:52:19 +03:00
if (this.actionsSettingsSubscription) {
this.actionsSettingsSubscription.unsubscribe();
this.actionsSettingsSubscription = null;
}
}
private createChangeSubscriptions() {
this.dataSettingsChangesSubscription = this.dataSettings.valueChanges.subscribe(
() => this.updateDataSettings()
);
this.targetDeviceSettingsSubscription = this.targetDeviceSettings.valueChanges.subscribe(
() => this.updateTargetDeviceSettings()
);
2019-10-24 19:52:19 +03:00
this.widgetSettingsSubscription = this.widgetSettings.valueChanges.subscribe(
() => this.updateWidgetSettings()
);
this.layoutSettingsSubscription = this.layoutSettings.valueChanges.subscribe(
() => this.updateLayoutSettings()
);
2019-10-21 19:57:18 +03:00
this.advancedSettingsSubscription = this.advancedSettings.valueChanges.subscribe(
() => this.updateAdvancedSettings()
);
2019-10-24 19:52:19 +03:00
this.actionsSettingsSubscription = this.actionsSettings.valueChanges.subscribe(
() => this.updateActionSettings()
);
}
2023-05-19 17:54:27 +03:00
private buildHeader() {
this.headerOptions.length = 0;
if (this.widgetType !== widgetType.static) {
this.headerOptions.push(
{
name: this.translate.instant('widget-config.data'),
value: 'data'
}
);
2021-03-02 12:04:45 +02:00
}
2023-05-19 17:54:27 +03:00
if (this.displayAppearance) {
this.headerOptions.push(
{
name: this.translate.instant('widget-config.appearance'),
value: 'appearance'
}
);
}
this.headerOptions.push(
{
name: this.translate.instant('widget-config.widget-card'),
value: 'card'
}
);
this.headerOptions.push(
{
name: this.translate.instant('widget-config.actions'),
value: 'actions'
}
);
this.headerOptions.push(
{
name: this.translate.instant('widget-config.mobile'),
value: 'mobile'
}
);
if (!this.selectedOption || !this.headerOptions.find(o => o.value === this.selectedOption)) {
this.selectedOption = this.headerOptions[0].value;
}
2023-05-19 17:54:27 +03:00
}
2023-05-19 17:54:27 +03:00
private buildForms() {
this.dataSettings = this.fb.group({});
this.targetDeviceSettings = this.fb.group({});
2019-10-21 19:57:18 +03:00
this.advancedSettings = this.fb.group({});
if (this.widgetType === widgetType.timeseries || this.widgetType === widgetType.alarm || this.widgetType === widgetType.latest) {
this.dataSettings.addControl('timewindowConfig', this.fb.control({
useDashboardTimewindow: true,
displayTimewindow: true,
timewindow: null,
timewindowStyle: null
}));
if (this.widgetType === widgetType.alarm) {
this.dataSettings.addControl('alarmFilterConfig', this.fb.control(null));
}
}
2019-10-21 19:57:18 +03:00
if (this.modelValue.isDataEnabled) {
if (this.widgetType !== widgetType.rpc &&
this.widgetType !== widgetType.alarm &&
this.widgetType !== widgetType.static) {
2023-05-19 17:54:27 +03:00
this.dataSettings.addControl('datasources', this.fb.control(null));
} else if (this.widgetType === widgetType.rpc) {
this.targetDeviceSettings.addControl('targetDeviceAliasId',
this.fb.control(null,
this.widgetEditMode ? [] : [Validators.required]));
2019-10-24 19:52:19 +03:00
} else if (this.widgetType === widgetType.alarm) {
2023-05-19 17:54:27 +03:00
this.dataSettings.addControl('alarmSource', this.fb.control(null));
}
}
2019-10-21 19:57:18 +03:00
this.advancedSettings.addControl('settings',
this.fb.control(null, []));
2019-09-25 19:37:29 +03:00
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
writeValue(value: WidgetConfigComponentData): void {
this.modelValue = value;
this.widgetType = this.modelValue?.widgetType;
this.widgetConfigMode = this.modelValue?.hasBasicMode ?
(this.modelValue?.config?.configMode || WidgetConfigMode.advanced) : WidgetConfigMode.advanced;
this.setupConfig(this.isAdd);
}
setWidgetConfigMode(widgetConfigMode: WidgetConfigMode) {
if (this.modelValue?.hasBasicMode && this.widgetConfigMode !== widgetConfigMode) {
this.widgetConfigMode = widgetConfigMode;
this.modelValue.config.configMode = widgetConfigMode;
if (this.hasBasicModeDirective) {
this.setupConfig();
}
this.propagateChange(this.modelValue);
}
}
private setupConfig(isAdd = false) {
if (this.modelValue) {
this.destroyBasicModeComponent();
this.removeChangeSubscriptions();
if (this.hasBasicModeDirective && this.widgetConfigMode === WidgetConfigMode.basic) {
this.setupBasicModeConfig(isAdd);
} else {
this.setupDefaultConfig();
}
}
}
private setupBasicModeConfig(isAdd = false) {
const componentType = basicWidgetConfigComponentsMap[this.modelValue.basicModeDirective];
if (!componentType) {
this.basicModeDirectiveError = this.translate.instant('widget-config.settings-component-not-found',
{selector: this.modelValue.basicModeDirective});
} else {
const factory = this.cfr.resolveComponentFactory(componentType);
this.createBasicModeComponentTimeout = setTimeout(() => {
this.createBasicModeComponentTimeout = null;
this.basicModeComponentRef = this.basicModeContainer.createComponent(factory);
this.basicModeComponent = this.basicModeComponentRef.instance;
this.basicModeComponent.isAdd = isAdd;
this.basicModeComponent.widgetConfig = this.modelValue;
this.basicModeComponentChangeSubscription = this.basicModeComponent.widgetConfigChanged.subscribe((data) => {
this.modelValue = data;
this.propagateChange(this.modelValue);
2023-10-10 17:23:12 +03:00
this.cd.markForCheck();
});
if (this.basicModeComponent$) {
this.basicModeComponent$.next(this.basicModeComponent);
this.basicModeComponent$.complete();
this.basicModeComponent$ = null;
}
this.cd.markForCheck();
}, 0);
}
}
private destroyBasicModeComponent() {
this.basicModeDirectiveError = null;
if (this.basicModeComponentChangeSubscription) {
this.basicModeComponentChangeSubscription.unsubscribe();
this.basicModeComponentChangeSubscription = null;
}
if (this.createBasicModeComponentTimeout) {
clearTimeout(this.createBasicModeComponentTimeout);
this.createBasicModeComponentTimeout = null;
}
if (this.basicModeComponentRef) {
this.basicModeComponentRef.destroy();
this.basicModeComponentRef = null;
this.basicModeComponent = null;
}
if (this.basicModeContainer) {
this.basicModeContainer.clear();
}
}
private setupDefaultConfig() {
if (this.defaultConfigFormsType !== this.widgetType) {
this.defaultConfigFormsType = this.widgetType;
this.buildForms();
}
this.buildHeader();
const config = this.modelValue.config;
const layout = this.modelValue.layout;
if (config) {
const displayWidgetTitle = isDefined(config.showTitle) ? config.showTitle : false;
this.widgetSettings.patchValue({
title: config.title,
titleFont: config.titleFont,
titleColor: config.titleColor,
showTitleIcon: isDefined(config.showTitleIcon) && displayWidgetTitle ? config.showTitleIcon : false,
titleIcon: isDefined(config.titleIcon) ? config.titleIcon : '',
iconColor: isDefined(config.iconColor) ? config.iconColor : 'rgba(0, 0, 0, 0.87)',
iconSize: isDefined(config.iconSize) ? config.iconSize : '24px',
titleTooltip: isDefined(config.titleTooltip) ? config.titleTooltip : '',
showTitle: displayWidgetTitle,
dropShadow: isDefined(config.dropShadow) ? config.dropShadow : true,
enableFullscreen: isDefined(config.enableFullscreen) ? config.enableFullscreen : true,
backgroundColor: config.backgroundColor,
color: config.color,
padding: config.padding,
margin: config.margin,
borderRadius: config.borderRadius,
widgetStyle: isDefined(config.widgetStyle) ? config.widgetStyle : {},
widgetCss: isDefined(config.widgetCss) ? config.widgetCss : '',
titleStyle: isDefined(config.titleStyle) ? config.titleStyle : {
fontSize: '16px',
fontWeight: 400
},
pageSize: isDefined(config.pageSize) ? config.pageSize : 1024,
units: config.units,
decimals: config.decimals,
noDataDisplayMessage: isDefined(config.noDataDisplayMessage) ? config.noDataDisplayMessage : ''
},
{emitEvent: false}
);
this.updateWidgetSettingsEnabledState();
this.actionsSettings.patchValue(
{
actions: config.actions || {}
},
{emitEvent: false}
);
if (this.widgetType === widgetType.timeseries || this.widgetType === widgetType.alarm || this.widgetType === widgetType.latest) {
const useDashboardTimewindow = isDefined(config.useDashboardTimewindow) ?
config.useDashboardTimewindow : true;
this.dataSettings.get('timewindowConfig').patchValue({
useDashboardTimewindow,
displayTimewindow: isDefined(config.displayTimewindow) ?
config.displayTimewindow : true,
timewindow: config.timewindow,
timewindowStyle: config.timewindowStyle
}, {emitEvent: false});
}
if (this.modelValue.isDataEnabled) {
if (this.widgetType !== widgetType.rpc &&
this.widgetType !== widgetType.alarm &&
this.widgetType !== widgetType.static) {
this.dataSettings.patchValue({ datasources: config.datasources},
{emitEvent: false});
} else if (this.widgetType === widgetType.rpc) {
let targetDeviceAliasId: string;
if (config.targetDeviceAliasIds && config.targetDeviceAliasIds.length > 0) {
const aliasId = config.targetDeviceAliasIds[0];
const entityAliases = this.aliasController.getEntityAliases();
if (entityAliases[aliasId]) {
targetDeviceAliasId = entityAliases[aliasId].id;
} else {
targetDeviceAliasId = null;
}
} else {
targetDeviceAliasId = null;
}
this.targetDeviceSettings.patchValue({
targetDeviceAliasId
}, {emitEvent: false});
} else if (this.widgetType === widgetType.alarm) {
this.dataSettings.patchValue(
{ alarmFilterConfig: isDefined(config.alarmFilterConfig) ?
config.alarmFilterConfig :
{ statusList: [AlarmSearchStatus.ACTIVE], searchPropagatedAlarms: true },
alarmSource: config.alarmSource }, {emitEvent: false}
);
}
}
this.updateSchemaForm(config.settings);
if (layout) {
this.layoutSettings.patchValue(
{
mobileOrder: layout.mobileOrder,
mobileHeight: layout.mobileHeight,
mobileHide: layout.mobileHide,
desktopHide: layout.desktopHide
2019-10-24 19:52:19 +03:00
},
{emitEvent: false}
);
} else {
this.layoutSettings.patchValue(
2019-10-24 19:52:19 +03:00
{
mobileOrder: null,
mobileHeight: null,
mobileHide: false,
desktopHide: false
2019-10-24 19:52:19 +03:00
},
{emitEvent: false}
);
2019-09-25 19:37:29 +03:00
}
}
this.createChangeSubscriptions();
2019-09-25 19:37:29 +03:00
}
2023-05-19 17:54:27 +03:00
private updateWidgetSettingsEnabledState() {
const showTitle: boolean = this.widgetSettings.get('showTitle').value;
const showTitleIcon: boolean = this.widgetSettings.get('showTitleIcon').value;
if (showTitle) {
this.widgetSettings.get('title').enable({emitEvent: false});
this.widgetSettings.get('titleFont').enable({emitEvent: false});
this.widgetSettings.get('titleColor').enable({emitEvent: false});
2023-05-19 17:54:27 +03:00
this.widgetSettings.get('titleTooltip').enable({emitEvent: false});
this.widgetSettings.get('titleStyle').enable({emitEvent: false});
this.widgetSettings.get('showTitleIcon').enable({emitEvent: false});
} else {
2023-05-19 17:54:27 +03:00
this.widgetSettings.get('title').disable({emitEvent: false});
this.widgetSettings.get('titleFont').disable({emitEvent: false});
this.widgetSettings.get('titleColor').disable({emitEvent: false});
2023-05-19 17:54:27 +03:00
this.widgetSettings.get('titleTooltip').disable({emitEvent: false});
this.widgetSettings.get('titleStyle').disable({emitEvent: false});
this.widgetSettings.get('showTitleIcon').disable({emitEvent: false});
}
if (showTitle && showTitleIcon) {
this.widgetSettings.get('titleIcon').enable({emitEvent: false});
this.widgetSettings.get('iconColor').enable({emitEvent: false});
this.widgetSettings.get('iconSize').enable({emitEvent: false});
} else {
this.widgetSettings.get('titleIcon').disable({emitEvent: false});
this.widgetSettings.get('iconColor').disable({emitEvent: false});
this.widgetSettings.get('iconSize').disable({emitEvent: false});
}
}
2019-10-21 19:57:18 +03:00
private updateSchemaForm(settings?: any) {
const widgetSettingsFormData: JsonFormComponentData = {};
if (this.modelValue.settingsSchema && this.modelValue.settingsSchema.schema) {
widgetSettingsFormData.schema = this.modelValue.settingsSchema.schema;
widgetSettingsFormData.form = this.modelValue.settingsSchema.form || deepClone(defaultSettingsForm);
widgetSettingsFormData.groupInfoes = this.modelValue.settingsSchema.groupInfoes;
widgetSettingsFormData.model = settings;
2019-09-25 19:37:29 +03:00
} else {
2019-10-21 19:57:18 +03:00
widgetSettingsFormData.schema = deepClone(emptySettingsSchema);
widgetSettingsFormData.form = deepClone(defaultSettingsForm);
widgetSettingsFormData.groupInfoes = deepClone(emptySettingsGroupInfoes);
widgetSettingsFormData.model = settings || {};
2019-09-25 19:37:29 +03:00
}
2022-05-11 15:58:15 +03:00
widgetSettingsFormData.settingsDirective = this.modelValue.settingsDirective;
2019-10-21 19:57:18 +03:00
this.advancedSettings.patchValue({ settings: widgetSettingsFormData }, {emitEvent: false});
2019-09-25 19:37:29 +03:00
}
private updateDataSettings() {
2019-09-25 19:37:29 +03:00
if (this.modelValue) {
if (this.modelValue.config) {
let data = this.dataSettings.value;
if (data.timewindowConfig) {
const timewindowConfig: TimewindowConfigData = data.timewindowConfig;
data = {...data, ...timewindowConfig};
delete data.timewindowConfig;
}
Object.assign(this.modelValue.config, data);
2019-09-25 19:37:29 +03:00
}
this.propagateChange(this.modelValue);
}
}
private updateTargetDeviceSettings() {
if (this.modelValue) {
if (this.modelValue.config) {
const targetDeviceAliasId: string = this.targetDeviceSettings.get('targetDeviceAliasId').value;
if (targetDeviceAliasId) {
this.modelValue.config.targetDeviceAliasIds = [targetDeviceAliasId];
} else {
this.modelValue.config.targetDeviceAliasIds = [];
}
}
this.propagateChange(this.modelValue);
}
}
2019-10-24 19:52:19 +03:00
private updateWidgetSettings() {
if (this.modelValue) {
if (this.modelValue.config) {
Object.assign(this.modelValue.config, this.widgetSettings.value);
}
this.propagateChange(this.modelValue);
}
}
private updateLayoutSettings() {
if (this.modelValue) {
if (this.modelValue.layout) {
Object.assign(this.modelValue.layout, this.layoutSettings.value);
}
this.propagateChange(this.modelValue);
}
}
2019-10-21 19:57:18 +03:00
private updateAdvancedSettings() {
if (this.modelValue) {
if (this.modelValue.config) {
2023-05-19 17:54:27 +03:00
this.modelValue.config.settings = this.advancedSettings.get('settings').value?.model;
2019-10-21 19:57:18 +03:00
}
this.propagateChange(this.modelValue);
}
}
2019-10-24 19:52:19 +03:00
private updateActionSettings() {
if (this.modelValue) {
if (this.modelValue.config) {
this.modelValue.config.actions = this.actionsSettings.get('actions').value;
2019-10-24 19:52:19 +03:00
}
this.propagateChange(this.modelValue);
}
}
public get hasBasicModeDirective(): boolean {
return this.modelValue?.basicModeDirective?.length > 0;
}
public get useDefinedBasicModeDirective(): boolean {
return this.modelValue?.basicModeDirective?.length && !this.basicModeDirectiveError;
}
2023-05-19 17:54:27 +03:00
public get displayAppearance(): boolean {
return this.displayAppearanceDataSettings || this.displayAdvancedAppearance;
}
public get displayAdvancedAppearance(): boolean {
return !!this.modelValue && (!!this.modelValue.settingsSchema && !!this.modelValue.settingsSchema.schema ||
!!this.modelValue.settingsDirective && !!this.modelValue.settingsDirective.length);
2019-09-25 19:37:29 +03:00
}
2023-05-19 17:54:27 +03:00
public get displayTimewindowConfig(): boolean {
if (this.widgetType === widgetType.timeseries || this.widgetType === widgetType.alarm) {
return true;
} else if (this.widgetType === widgetType.latest) {
const datasources = this.dataSettings.get('datasources').value;
return datasourcesHasAggregation(datasources);
}
}
2023-05-19 17:54:27 +03:00
public get displayLimits(): boolean {
return this.widgetType !== widgetType.rpc && this.widgetType !== widgetType.alarm &&
this.modelValue?.isDataEnabled && !this.modelValue?.typeParameters?.singleEntity;
}
public get displayAppearanceDataSettings(): boolean {
return !this.modelValue?.typeParameters?.hideDataSettings && (this.displayUnitsConfig || this.displayNoDataDisplayMessageConfig);
}
2023-05-19 17:54:27 +03:00
public get displayUnitsConfig(): boolean {
return this.widgetType === widgetType.latest || this.widgetType === widgetType.timeseries;
2021-03-02 12:04:45 +02:00
}
2023-05-19 17:54:27 +03:00
public get displayNoDataDisplayMessageConfig(): boolean {
return this.widgetType !== widgetType.static && !this.modelValue?.typeParameters?.processNoDataByWidget;
}
2023-05-19 17:54:27 +03:00
public onlyHistoryTimewindow(): boolean {
if (this.widgetType === widgetType.latest) {
const datasources = this.dataSettings.get('datasources').value;
return datasourcesHasOnlyComparisonAggregation(datasources);
} else {
2023-05-19 17:54:27 +03:00
return false;
}
}
public generateDataKey(chip: any, type: DataKeyType, datakeySettingsSchema: JsonSettingsSchema): DataKey {
if (isObject(chip)) {
(chip as DataKey)._hash = Math.random();
return chip;
} else {
let label: string = chip;
2020-02-21 19:04:49 +02:00
if (type === DataKeyType.alarm || type === DataKeyType.entityField) {
2020-07-02 15:42:58 +03:00
const keyField = type === DataKeyType.alarm ? alarmFields[label] : entityFields[chip];
2020-02-21 19:04:49 +02:00
if (keyField) {
label = this.translate.instant(keyField.name);
}
}
2023-01-30 16:35:19 +02:00
const datasources = this.widgetType === widgetType.alarm ? [this.modelValue.config.alarmSource] : this.modelValue.config.datasources;
label = genNextLabel(label, datasources);
const result: DataKey = {
name: chip,
type,
label,
color: this.genNextColor(),
settings: {},
_hash: Math.random()
};
if (type === DataKeyType.function) {
result.name = 'f(x)';
result.funcBody = this.utils.getPredefinedFunctionBody(chip);
if (!result.funcBody) {
result.funcBody = 'return prevValue + 1;';
}
2021-03-02 12:04:45 +02:00
} else if (type === DataKeyType.count) {
result.name = 'count';
}
if (datakeySettingsSchema && isDefined(datakeySettingsSchema.schema)) {
result.settings = this.utils.generateObjectFromJsonSchema(datakeySettingsSchema.schema);
}
return result;
}
}
private genNextColor(): string {
let i = 0;
const datasources = this.widgetType === widgetType.alarm ? [this.modelValue.config.alarmSource] : this.modelValue.config.datasources;
if (datasources) {
datasources.forEach((datasource) => {
if (datasource && (datasource.dataKeys || datasource.latestDataKeys)) {
i += ((datasource.dataKeys ? datasource.dataKeys.length : 0) +
(datasource.latestDataKeys ? datasource.latestDataKeys.length : 0));
}
});
}
return this.utils.getMaterialColor(i);
}
private createEntityAlias(alias: string, allowedEntityTypes: Array<EntityType>): Observable<EntityAlias> {
const singleEntityAlias: EntityAlias = {id: null, alias, filter: {resolveMultiple: false}};
return this.dialog.open<EntityAliasDialogComponent, EntityAliasDialogData,
EntityAlias>(EntityAliasDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
isAdd: true,
allowedEntityTypes,
entityAliases: this.dashboard.configuration.entityAliases,
alias: singleEntityAlias
}
}).afterClosed().pipe(
tap((entityAlias) => {
if (entityAlias) {
this.dashboard.configuration.entityAliases[entityAlias.id] = entityAlias;
this.aliasController.updateEntityAliases(this.dashboard.configuration.entityAliases);
}
})
);
}
private createFilter(filter: string): Observable<Filter> {
2020-07-01 20:09:25 +03:00
const singleFilter: Filter = {id: null, filter, keyFilters: [], editable: true};
return this.dialog.open<FilterDialogComponent, FilterDialogData,
Filter>(FilterDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
isAdd: true,
filters: this.dashboard.configuration.filters,
2020-07-02 15:42:58 +03:00
filter: singleFilter
}
}).afterClosed().pipe(
tap((result) => {
if (result) {
this.dashboard.configuration.filters[result.id] = result;
this.aliasController.updateFilters(this.dashboard.configuration.filters);
}
})
);
}
private fetchEntityKeysForDevice(deviceId: string, dataKeyTypes: Array<DataKeyType>): Observable<Array<DataKey>> {
const entityFilter = singleEntityFilterFromDeviceId(deviceId);
return this.entityService.getEntityKeysByEntityFilter(
entityFilter,
dataKeyTypes, [EntityType.DEVICE],
{ignoreLoading: true, ignoreErrors: true}
).pipe(
catchError(() => of([]))
);
}
private fetchEntityKeys(entityAliasId: string, dataKeyTypes: Array<DataKeyType>): Observable<Array<DataKey>> {
return this.aliasController.getAliasInfo(entityAliasId).pipe(
2023-05-16 20:00:53 +03:00
mergeMap((aliasInfo) => this.entityService.getEntityKeysByEntityFilter(
aliasInfo.entityFilter,
dataKeyTypes, [],
{ignoreLoading: true, ignoreErrors: true}
).pipe(
catchError(() => of([]))
2023-05-16 20:00:53 +03:00
)),
catchError(() => of([] as Array<DataKey>))
);
}
2019-10-24 19:52:19 +03:00
private fetchDashboardStates(query: string): Array<string> {
const stateIds = Object.keys(this.dashboard.configuration.states);
2019-10-24 19:52:19 +03:00
const result = query ? stateIds.filter(this.createFilterForDashboardState(query)) : stateIds;
if (result && result.length) {
return result;
} else {
return [query];
}
}
private createFilterForDashboardState(query: string): (stateId: string) => boolean {
const lowercaseQuery = query.toLowerCase();
return stateId => stateId.toLowerCase().indexOf(lowercaseQuery) === 0;
}
public validate(c: UntypedFormControl): Observable<ValidationErrors | null> {
const basicComponentMode = this.hasBasicModeDirective && this.widgetConfigMode === WidgetConfigMode.basic;
let comp$: Observable<IBasicWidgetConfigComponent>;
if (basicComponentMode) {
if (this.basicModeComponent) {
comp$ = of(this.basicModeComponent);
} else {
if (this.useDefinedBasicModeDirective) {
this.basicModeComponent$ = new Subject();
comp$ = this.basicModeComponent$;
} else {
comp$ = of(null);
}
}
} else {
comp$ = of(null);
}
return comp$.pipe(
map((comp) => this.doValidate(basicComponentMode, comp))
);
}
private doValidate(basicComponentMode: boolean, basicModeComponent?: IBasicWidgetConfigComponent): ValidationErrors | null {
if (basicComponentMode) {
if (!basicModeComponent || !basicModeComponent.validateConfig()) {
return {
basicWidgetConfig: {
valid: false
}
};
}
} else {
if (!this.dataSettings.valid) {
return {
dataSettings: {
valid: false
}
};
} else if (!this.widgetSettings.valid) {
return {
widgetSettings: {
valid: false
}
};
} else if (!this.layoutSettings.valid) {
return {
widgetSettings: {
valid: false
}
};
} else if (!this.advancedSettings.valid) {
return {
advancedSettings: {
valid: false
}
};
}
}
if (this.modelValue) {
const config = this.modelValue.config;
2019-10-21 19:57:18 +03:00
if (this.widgetType === widgetType.rpc && this.modelValue.isDataEnabled) {
2022-02-04 20:06:23 +02:00
if (!this.widgetEditMode && (!config.targetDeviceAliasIds || !config.targetDeviceAliasIds.length)) {
return {
targetDeviceAliasIds: {
valid: false
}
};
}
}
}
return null;
2019-09-25 19:37:29 +03:00
}
2019-09-25 19:37:29 +03:00
}