UI: Add persistence settings in save ts rule node
This commit is contained in:
parent
b898ca4d15
commit
2d1ead5f54
@ -42,6 +42,12 @@ import { DeleteAttributesConfigComponent } from './delete-attributes-config.comp
|
|||||||
import { MathFunctionConfigComponent } from './math-function-config.component';
|
import { MathFunctionConfigComponent } from './math-function-config.component';
|
||||||
import { DeviceStateConfigComponent } from './device-state-config.component';
|
import { DeviceStateConfigComponent } from './device-state-config.component';
|
||||||
import { SendRestApiCallReplyConfigComponent } from './send-rest-api-call-reply-config.component';
|
import { SendRestApiCallReplyConfigComponent } from './send-rest-api-call-reply-config.component';
|
||||||
|
import {
|
||||||
|
AdvancedPersistenceSettingComponent
|
||||||
|
} from '@home/components/rule-node/action/advanced-persistence-setting.component';
|
||||||
|
import {
|
||||||
|
AdvancedPersistenceSettingRowComponent
|
||||||
|
} from '@home/components/rule-node/action/advanced-persistence-setting-row.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -67,7 +73,9 @@ import { SendRestApiCallReplyConfigComponent } from './send-rest-api-call-reply-
|
|||||||
PushToEdgeConfigComponent,
|
PushToEdgeConfigComponent,
|
||||||
PushToCloudConfigComponent,
|
PushToCloudConfigComponent,
|
||||||
MathFunctionConfigComponent,
|
MathFunctionConfigComponent,
|
||||||
DeviceStateConfigComponent
|
DeviceStateConfigComponent,
|
||||||
|
AdvancedPersistenceSettingComponent,
|
||||||
|
AdvancedPersistenceSettingRowComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<section [formGroup]="persistenceSettingRowForm" class="tb-form-panel stroked no-gap no-padding-bottom">
|
||||||
|
<div class="tb-form-panel-title mb-4">{{ title }}</div>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label translate>rule-node-config.save-time-series.strategy</mat-label>
|
||||||
|
<mat-select formControlName="type">
|
||||||
|
@for (strategy of persistenceStrategies; track strategy) {
|
||||||
|
<mat-option [value]="strategy">{{ PersistenceTypeTranslationMap.get(strategy) | translate }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
@if(persistenceSettingRowForm.get('type').value === PersistenceType.DEDUPLICATE) {
|
||||||
|
<tb-time-unit-input
|
||||||
|
required
|
||||||
|
labelText="{{ 'rule-node-config.save-time-series.deduplication-interval' | translate }}"
|
||||||
|
requiredText="{{ 'rule-node-config.save-time-series.deduplication-interval-required' | translate }}"
|
||||||
|
minErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}"
|
||||||
|
maxErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}"
|
||||||
|
[maxTime]="maxDeduplicateTime"
|
||||||
|
formControlName="deduplicationIntervalSecs">
|
||||||
|
</tb-time-unit-input>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
///
|
||||||
|
/// 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, forwardRef, Input } from '@angular/core';
|
||||||
|
import {
|
||||||
|
ControlValueAccessor,
|
||||||
|
FormBuilder,
|
||||||
|
NG_VALIDATORS,
|
||||||
|
NG_VALUE_ACCESSOR,
|
||||||
|
ValidationErrors,
|
||||||
|
Validator
|
||||||
|
} from '@angular/forms';
|
||||||
|
import {
|
||||||
|
AdvancedPersistenceConfig,
|
||||||
|
defaultAdvancedPersistenceConfig,
|
||||||
|
maxDeduplicateTimeSecs,
|
||||||
|
PersistenceType,
|
||||||
|
PersistenceTypeTranslationMap
|
||||||
|
} from '@home/components/rule-node/action/timeseries-config.models';
|
||||||
|
import { isDefinedAndNotNull } from '@core/utils';
|
||||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-advanced-persistence-setting-row',
|
||||||
|
templateUrl: './advanced-persistence-setting-row.component.html',
|
||||||
|
providers: [{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => AdvancedPersistenceSettingRowComponent),
|
||||||
|
multi: true
|
||||||
|
},{
|
||||||
|
provide: NG_VALIDATORS,
|
||||||
|
useExisting: forwardRef(() => AdvancedPersistenceSettingRowComponent),
|
||||||
|
multi: true
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
export class AdvancedPersistenceSettingRowComponent implements ControlValueAccessor, Validator {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
persistenceSettingRowForm = this.fb.group({
|
||||||
|
type: [defaultAdvancedPersistenceConfig.type],
|
||||||
|
deduplicationIntervalSecs: [{value: 60, disabled: true}]
|
||||||
|
});
|
||||||
|
|
||||||
|
PersistenceType = PersistenceType;
|
||||||
|
persistenceStrategies = [PersistenceType.ON_EVERY_MESSAGE, PersistenceType.DEDUPLICATE, PersistenceType.SKIP];
|
||||||
|
PersistenceTypeTranslationMap = PersistenceTypeTranslationMap;
|
||||||
|
|
||||||
|
maxDeduplicateTime = maxDeduplicateTimeSecs;
|
||||||
|
|
||||||
|
private propagateChange: (value: any) => void = () => {};
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder) {
|
||||||
|
this.persistenceSettingRowForm.get('type').valueChanges.pipe(
|
||||||
|
takeUntilDestroyed()
|
||||||
|
).subscribe(() => this.updatedValidation());
|
||||||
|
|
||||||
|
this.persistenceSettingRowForm.valueChanges.pipe(
|
||||||
|
takeUntilDestroyed()
|
||||||
|
).subscribe((value) => this.propagateChange(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any) {
|
||||||
|
this.propagateChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(_fn: any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean) {
|
||||||
|
if (isDisabled) {
|
||||||
|
this.persistenceSettingRowForm.disable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.persistenceSettingRowForm.enable({emitEvent: false});
|
||||||
|
this.updatedValidation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(): ValidationErrors | null {
|
||||||
|
return this.persistenceSettingRowForm.valid ? null : {
|
||||||
|
persistenceSettingRow: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(value: AdvancedPersistenceConfig) {
|
||||||
|
if (isDefinedAndNotNull(value)) {
|
||||||
|
this.persistenceSettingRowForm.patchValue(value, {emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.persistenceSettingRowForm.patchValue(defaultAdvancedPersistenceConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updatedValidation() {
|
||||||
|
if (this.persistenceSettingRowForm.get('type').value === PersistenceType.DEDUPLICATE) {
|
||||||
|
this.persistenceSettingRowForm.get('deduplicationIntervalSecs').enable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.persistenceSettingRowForm.get('deduplicationIntervalSecs').disable({emitEvent: false})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<section [formGroup]="persistenceForm" class="tb-form-panel no-border no-padding">
|
||||||
|
<tb-advanced-persistence-setting-row
|
||||||
|
formControlName="timeseries"
|
||||||
|
title="{{ 'rule-node-config.save-time-series.time-series' | translate }}"
|
||||||
|
></tb-advanced-persistence-setting-row>
|
||||||
|
<tb-advanced-persistence-setting-row
|
||||||
|
formControlName="latest"
|
||||||
|
title="{{ 'rule-node-config.save-time-series.latest' | translate }}"
|
||||||
|
></tb-advanced-persistence-setting-row>
|
||||||
|
<tb-advanced-persistence-setting-row
|
||||||
|
formControlName="webSockets"
|
||||||
|
title="{{ 'rule-node-config.save-time-series.web-sockets' | translate }}"
|
||||||
|
></tb-advanced-persistence-setting-row>
|
||||||
|
</section>
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
///
|
||||||
|
/// 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 {
|
||||||
|
ControlValueAccessor,
|
||||||
|
FormBuilder,
|
||||||
|
NG_VALIDATORS,
|
||||||
|
NG_VALUE_ACCESSOR,
|
||||||
|
ValidationErrors,
|
||||||
|
Validator
|
||||||
|
} from '@angular/forms';
|
||||||
|
import { Component, forwardRef } from '@angular/core';
|
||||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
import { AdvancedPersistenceStrategy } from '@home/components/rule-node/action/timeseries-config.models';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-advanced-persistence-settings',
|
||||||
|
templateUrl: './advanced-persistence-setting.component.html',
|
||||||
|
providers: [{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => AdvancedPersistenceSettingComponent),
|
||||||
|
multi: true
|
||||||
|
},{
|
||||||
|
provide: NG_VALIDATORS,
|
||||||
|
useExisting: forwardRef(() => AdvancedPersistenceSettingComponent),
|
||||||
|
multi: true
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
export class AdvancedPersistenceSettingComponent implements ControlValueAccessor, Validator {
|
||||||
|
|
||||||
|
persistenceForm = this.fb.group({
|
||||||
|
timeseries: [null],
|
||||||
|
latest: [null],
|
||||||
|
webSockets: [null]
|
||||||
|
});
|
||||||
|
|
||||||
|
private propagateChange: (value: any) => void = () => {};
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder) {
|
||||||
|
this.persistenceForm.valueChanges.pipe(
|
||||||
|
takeUntilDestroyed()
|
||||||
|
).subscribe(value => this.propagateChange(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any) {
|
||||||
|
this.propagateChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(_fn: any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean) {
|
||||||
|
if (isDisabled) {
|
||||||
|
this.persistenceForm.disable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.persistenceForm.enable({emitEvent: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(): ValidationErrors | null {
|
||||||
|
return this.persistenceForm.valid ? null : {
|
||||||
|
persistenceForm: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(value: AdvancedPersistenceStrategy) {
|
||||||
|
this.persistenceForm.patchValue(value, {emitEvent: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -16,6 +16,46 @@
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
<section [formGroup]="timeseriesConfigForm" class="tb-form-panel no-border no-padding">
|
<section [formGroup]="timeseriesConfigForm" class="tb-form-panel no-border no-padding">
|
||||||
|
<div class="tb-form-panel stroked no-padding-bottom no-gap" formGroupName="persistenceSettings">
|
||||||
|
<div class="mb-4 flex flex-row items-center justify-between">
|
||||||
|
<div class="tb-form-panel-title" tb-hint-tooltip-icon="{{ 'rule-node-config.save-time-series.persistence-settings-hint' | translate}}" translate>
|
||||||
|
rule-node-config.save-time-series.persistence-settings
|
||||||
|
</div>
|
||||||
|
<tb-toggle-select appearance="fill" selectMediaBreakpoint="xs"
|
||||||
|
formControlName="isAdvanced">
|
||||||
|
<tb-toggle-option [value]=false>{{ 'rule-node-config.basic-mode' | translate}}</tb-toggle-option>
|
||||||
|
<tb-toggle-option [value]=true>{{ 'rule-node-config.advanced-mode' | translate }}</tb-toggle-option>
|
||||||
|
</tb-toggle-select>
|
||||||
|
</div>
|
||||||
|
@if(!timeseriesConfigForm.get('persistenceSettings.isAdvanced').value) {
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label translate>rule-node-config.save-time-series.strategy</mat-label>
|
||||||
|
<mat-select formControlName="type">
|
||||||
|
@for (strategy of persistenceStrategies; track strategy) {
|
||||||
|
<mat-option [value]="strategy">{{ PersistenceTypeTranslationMap.get(strategy) | translate }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
@if(timeseriesConfigForm.get('persistenceSettings.type').value === PersistenceType.DEDUPLICATE) {
|
||||||
|
<tb-time-unit-input
|
||||||
|
required
|
||||||
|
labelText="{{ 'rule-node-config.save-time-series.deduplication-interval' | translate }}"
|
||||||
|
requiredText="{{ 'rule-node-config.save-time-series.deduplication-interval-required' | translate }}"
|
||||||
|
minErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}"
|
||||||
|
maxErrorText="{{ 'rule-node-config.save-time-series.deduplication-interval-min-max-range' | translate }}"
|
||||||
|
[maxTime]="maxDeduplicateTime"
|
||||||
|
formControlName="deduplicationIntervalSecs">
|
||||||
|
</tb-time-unit-input>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@else {
|
||||||
|
<tb-advanced-persistence-settings
|
||||||
|
class="mb-4"
|
||||||
|
formControlName="advanced"
|
||||||
|
></tb-advanced-persistence-settings>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<section class="tb-form-panel stroked">
|
<section class="tb-form-panel stroked">
|
||||||
<mat-expansion-panel class="tb-settings">
|
<mat-expansion-panel class="tb-settings">
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
|
|||||||
@ -15,8 +15,18 @@
|
|||||||
///
|
///
|
||||||
|
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models';
|
import { RuleNodeConfigurationComponent } from '@shared/models/rule-node.models';
|
||||||
|
import {
|
||||||
|
defaultAdvancedPersistenceStrategy,
|
||||||
|
maxDeduplicateTimeSecs,
|
||||||
|
PersistenceSettings,
|
||||||
|
PersistenceSettingsForm,
|
||||||
|
PersistenceType,
|
||||||
|
PersistenceTypeTranslationMap,
|
||||||
|
TimeseriesNodeConfiguration,
|
||||||
|
TimeseriesNodeConfigurationForm
|
||||||
|
} from '@home/components/rule-node/action/timeseries-config.models';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-action-node-timeseries-config',
|
selector: 'tb-action-node-timeseries-config',
|
||||||
@ -25,20 +35,98 @@ import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/m
|
|||||||
})
|
})
|
||||||
export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent {
|
export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent {
|
||||||
|
|
||||||
timeseriesConfigForm: UntypedFormGroup;
|
timeseriesConfigForm: FormGroup;
|
||||||
|
|
||||||
constructor(private fb: UntypedFormBuilder) {
|
PersistenceType = PersistenceType;
|
||||||
|
persistenceStrategies = [PersistenceType.ON_EVERY_MESSAGE, PersistenceType.DEDUPLICATE, PersistenceType.WEBSOCKETS_ONLY];
|
||||||
|
PersistenceTypeTranslationMap = PersistenceTypeTranslationMap;
|
||||||
|
|
||||||
|
maxDeduplicateTime = maxDeduplicateTimeSecs
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected configForm(): UntypedFormGroup {
|
protected configForm(): FormGroup {
|
||||||
return this.timeseriesConfigForm;
|
return this.timeseriesConfigForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onConfigurationSet(configuration: RuleNodeConfiguration) {
|
protected validatorTriggers(): string[] {
|
||||||
|
return ['persistenceSettings.isAdvanced', 'persistenceSettings.type'];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected prepareInputConfig(config: TimeseriesNodeConfiguration): TimeseriesNodeConfigurationForm {
|
||||||
|
let persistenceSettings: PersistenceSettingsForm;
|
||||||
|
if (config?.persistenceSettings) {
|
||||||
|
const isAdvanced = config?.persistenceSettings?.type === PersistenceType.ADVANCED;
|
||||||
|
persistenceSettings = {
|
||||||
|
...config.persistenceSettings,
|
||||||
|
isAdvanced: isAdvanced,
|
||||||
|
type: isAdvanced ? PersistenceType.ON_EVERY_MESSAGE : config.persistenceSettings.type,
|
||||||
|
advanced: isAdvanced ? config.persistenceSettings : defaultAdvancedPersistenceStrategy
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
persistenceSettings = {
|
||||||
|
type: PersistenceType.ON_EVERY_MESSAGE,
|
||||||
|
isAdvanced: false,
|
||||||
|
deduplicationIntervalSecs: 10,
|
||||||
|
advanced: defaultAdvancedPersistenceStrategy
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
persistenceSettings: persistenceSettings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected prepareOutputConfig(config: TimeseriesNodeConfigurationForm): TimeseriesNodeConfiguration {
|
||||||
|
let persistenceSettings: PersistenceSettings;
|
||||||
|
if (config.persistenceSettings.isAdvanced) {
|
||||||
|
persistenceSettings = {
|
||||||
|
...config.persistenceSettings.advanced,
|
||||||
|
type: PersistenceType.ADVANCED
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
persistenceSettings = {
|
||||||
|
type: config.persistenceSettings.type,
|
||||||
|
deduplicationIntervalSecs: config.persistenceSettings?.deduplicationIntervalSecs
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
persistenceSettings
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onConfigurationSet(config: TimeseriesNodeConfigurationForm) {
|
||||||
this.timeseriesConfigForm = this.fb.group({
|
this.timeseriesConfigForm = this.fb.group({
|
||||||
defaultTTL: [configuration ? configuration.defaultTTL : null, [Validators.required, Validators.min(0)]],
|
persistenceSettings: this.fb.group({
|
||||||
useServerTs: [configuration ? configuration.useServerTs : false, []]
|
isAdvanced: [config?.persistenceSettings?.isAdvanced ?? false],
|
||||||
|
type: [config?.persistenceSettings?.type ?? PersistenceType.ON_EVERY_MESSAGE],
|
||||||
|
deduplicationIntervalSecs: [
|
||||||
|
{value: config?.persistenceSettings?.deduplicationIntervalSecs ?? 10, disabled: true},
|
||||||
|
[Validators.required, Validators.max(maxDeduplicateTimeSecs)]
|
||||||
|
],
|
||||||
|
advanced: [{value: null, disabled: true}]
|
||||||
|
}),
|
||||||
|
defaultTTL: [config?.defaultTTL ?? null, [Validators.required, Validators.min(0)]],
|
||||||
|
useServerTs: [config?.useServerTs ?? false]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected updateValidators(emitEvent: boolean, _trigger?: string) {
|
||||||
|
const persistenceForm = this.timeseriesConfigForm.get('persistenceSettings') as FormGroup;
|
||||||
|
const isAdvanced: boolean = persistenceForm.get('isAdvanced').value;
|
||||||
|
const type: PersistenceType = persistenceForm.get('type').value;
|
||||||
|
if (!isAdvanced && type === PersistenceType.DEDUPLICATE) {
|
||||||
|
persistenceForm.get('deduplicationIntervalSecs').enable({emitEvent});
|
||||||
|
} else {
|
||||||
|
persistenceForm.get('deduplicationIntervalSecs').disable({emitEvent});
|
||||||
|
}
|
||||||
|
if (isAdvanced) {
|
||||||
|
persistenceForm.get('advanced').enable({emitEvent});
|
||||||
|
} else {
|
||||||
|
persistenceForm.get('advanced').disable({emitEvent});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,78 @@
|
|||||||
|
///
|
||||||
|
/// 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 { DAY, SECOND } from '@shared/models/time/time.models';
|
||||||
|
|
||||||
|
export const maxDeduplicateTimeSecs = DAY / SECOND + 1;
|
||||||
|
|
||||||
|
export interface TimeseriesNodeConfiguration {
|
||||||
|
persistenceSettings: PersistenceSettings;
|
||||||
|
defaultTTL: number;
|
||||||
|
useServerTs: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TimeseriesNodeConfigurationForm extends Omit<TimeseriesNodeConfiguration, 'persistenceSettings'> {
|
||||||
|
persistenceSettings: PersistenceSettingsForm
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PersistenceSettings = BasicPersistenceSettings & Partial<DeduplicatePersistenceStrategy> & Partial<AdvancedPersistenceStrategy>;
|
||||||
|
|
||||||
|
export type PersistenceSettingsForm = Omit<PersistenceSettings, keyof AdvancedPersistenceStrategy> & {
|
||||||
|
isAdvanced: boolean;
|
||||||
|
advanced?: Partial<AdvancedPersistenceStrategy>;
|
||||||
|
type: PersistenceType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum PersistenceType {
|
||||||
|
ON_EVERY_MESSAGE = 'ON_EVERY_MESSAGE',
|
||||||
|
DEDUPLICATE = 'DEDUPLICATE',
|
||||||
|
WEBSOCKETS_ONLY = 'WEBSOCKETS_ONLY',
|
||||||
|
ADVANCED = 'ADVANCED',
|
||||||
|
SKIP = 'SKIP'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PersistenceTypeTranslationMap = new Map<PersistenceType, string>([
|
||||||
|
[PersistenceType.ON_EVERY_MESSAGE, 'rule-node-config.save-time-series.strategy-type.every-message'],
|
||||||
|
[PersistenceType.DEDUPLICATE, 'rule-node-config.save-time-series.strategy-type.deduplicate'],
|
||||||
|
[PersistenceType.WEBSOCKETS_ONLY, 'rule-node-config.save-time-series.strategy-type.web-sockets-only'],
|
||||||
|
[PersistenceType.SKIP, 'rule-node-config.save-time-series.strategy-type.skip'],
|
||||||
|
])
|
||||||
|
|
||||||
|
export interface BasicPersistenceSettings {
|
||||||
|
type: PersistenceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeduplicatePersistenceStrategy extends BasicPersistenceSettings{
|
||||||
|
deduplicationIntervalSecs: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdvancedPersistenceStrategy extends BasicPersistenceSettings{
|
||||||
|
timeseries: AdvancedPersistenceConfig;
|
||||||
|
latest: AdvancedPersistenceConfig;
|
||||||
|
webSockets: AdvancedPersistenceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AdvancedPersistenceConfig = WithOptional<DeduplicatePersistenceStrategy, 'deduplicationIntervalSecs'>;
|
||||||
|
|
||||||
|
export const defaultAdvancedPersistenceConfig: AdvancedPersistenceConfig = {
|
||||||
|
type: PersistenceType.ON_EVERY_MESSAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultAdvancedPersistenceStrategy: Omit<AdvancedPersistenceStrategy, 'type'> = {
|
||||||
|
timeseries: defaultAdvancedPersistenceConfig,
|
||||||
|
latest: defaultAdvancedPersistenceConfig,
|
||||||
|
webSockets: defaultAdvancedPersistenceConfig,
|
||||||
|
}
|
||||||
@ -16,12 +16,13 @@
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
<section [formGroup]="timeInputForm" class="flex gap-4">
|
<section [formGroup]="timeInputForm" class="flex gap-4">
|
||||||
<mat-form-field class="max-w-66% flex-full">
|
<mat-form-field class="max-w-66% flex-full" subscriptSizing="dynamic">
|
||||||
<mat-label *ngIf="labelText">{{ labelText }}</mat-label>
|
<mat-label *ngIf="labelText">{{ labelText }}</mat-label>
|
||||||
<input type="number" min="0" step="1" matInput formControlName="time">
|
<input type="number" min="0" step="1" matInput formControlName="time">
|
||||||
<div matSuffix>
|
<div matSuffix>
|
||||||
<ng-content select="[matSuffix]"></ng-content>
|
<ng-content select="[matSuffix]"></ng-content>
|
||||||
</div>
|
</div>
|
||||||
|
<mat-hint></mat-hint>
|
||||||
<mat-error *ngIf="timeInputForm.get('time').hasError('required') && requiredText">
|
<mat-error *ngIf="timeInputForm.get('time').hasError('required') && requiredText">
|
||||||
{{ requiredText }}
|
{{ requiredText }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
|||||||
@ -5068,6 +5068,25 @@
|
|||||||
"units": "Units",
|
"units": "Units",
|
||||||
"tell-failure-aws-lambda": "Tell Failure if AWS Lambda function execution raises exception",
|
"tell-failure-aws-lambda": "Tell Failure if AWS Lambda function execution raises exception",
|
||||||
"tell-failure-aws-lambda-hint": "Rule node forces failure of message processing if AWS Lambda function execution raises exception.",
|
"tell-failure-aws-lambda-hint": "Rule node forces failure of message processing if AWS Lambda function execution raises exception.",
|
||||||
|
"basic-mode": "Basic",
|
||||||
|
"advanced-mode": "Advanced",
|
||||||
|
"save-time-series": {
|
||||||
|
"persistence-settings": "Persistence settings",
|
||||||
|
"persistence-settings-hint": "Persistence settings hint",
|
||||||
|
"strategy": "Strategy",
|
||||||
|
"deduplication-interval": "Deduplication interval",
|
||||||
|
"deduplication-interval-required": "Deduplication interval is required",
|
||||||
|
"deduplication-interval-min-max-range": "Deduplication interval should be at least 1 second and at most 1 day",
|
||||||
|
"strategy-type": {
|
||||||
|
"every-message": "On every message",
|
||||||
|
"skip": "Skip",
|
||||||
|
"deduplicate": "Deduplicate",
|
||||||
|
"web-sockets-only": "WebSockets only"
|
||||||
|
},
|
||||||
|
"time-series": "Time series",
|
||||||
|
"latest": "Latest",
|
||||||
|
"web-sockets": "WebSockets"
|
||||||
|
},
|
||||||
"key-val": {
|
"key-val": {
|
||||||
"key": "Key",
|
"key": "Key",
|
||||||
"value": "Value",
|
"value": "Value",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user