-
- {{ 'rule-node-config.skip-latest-persistence' | translate }}
-
+
+
diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts
index d480ec4d39..d5e1d998a8 100644
--- a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts
+++ b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.component.ts
@@ -15,8 +15,18 @@
///
import { Component } from '@angular/core';
-import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
-import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { RuleNodeConfigurationComponent } from '@shared/models/rule-node.models';
+import {
+ defaultAdvancedPersistenceStrategy,
+ maxDeduplicateTimeSecs,
+ ProcessingSettings,
+ ProcessingSettingsForm,
+ ProcessingType,
+ ProcessingTypeTranslationMap,
+ TimeseriesNodeConfiguration,
+ TimeseriesNodeConfigurationForm
+} from '@home/components/rule-node/action/timeseries-config.models';
@Component({
selector: 'tb-action-node-timeseries-config',
@@ -25,21 +35,98 @@ import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/m
})
export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent {
- timeseriesConfigForm: UntypedFormGroup;
+ timeseriesConfigForm: FormGroup;
- constructor(private fb: UntypedFormBuilder) {
+ PersistenceType = ProcessingType;
+ persistenceStrategies = [ProcessingType.ON_EVERY_MESSAGE, ProcessingType.DEDUPLICATE, ProcessingType.WEBSOCKETS_ONLY];
+ PersistenceTypeTranslationMap = ProcessingTypeTranslationMap;
+
+ maxDeduplicateTime = maxDeduplicateTimeSecs
+
+ constructor(private fb: FormBuilder) {
super();
}
- protected configForm(): UntypedFormGroup {
+ protected configForm(): FormGroup {
return this.timeseriesConfigForm;
}
- protected onConfigurationSet(configuration: RuleNodeConfiguration) {
+ protected validatorTriggers(): string[] {
+ return ['processingSettings.isAdvanced', 'processingSettings.type'];
+ }
+
+ protected prepareInputConfig(config: TimeseriesNodeConfiguration): TimeseriesNodeConfigurationForm {
+ let processingSettings: ProcessingSettingsForm;
+ if (config?.processingSettings) {
+ const isAdvanced = config?.processingSettings?.type === ProcessingType.ADVANCED;
+ processingSettings = {
+ type: isAdvanced ? ProcessingType.ON_EVERY_MESSAGE : config.processingSettings.type,
+ isAdvanced: isAdvanced,
+ deduplicationIntervalSecs: config.processingSettings?.deduplicationIntervalSecs ?? 60,
+ advanced: isAdvanced ? config.processingSettings : defaultAdvancedPersistenceStrategy
+ }
+ } else {
+ processingSettings = {
+ type: ProcessingType.ON_EVERY_MESSAGE,
+ isAdvanced: false,
+ deduplicationIntervalSecs: 60,
+ advanced: defaultAdvancedPersistenceStrategy
+ };
+ }
+ return {
+ ...config,
+ processingSettings: processingSettings
+ }
+ }
+
+ protected prepareOutputConfig(config: TimeseriesNodeConfigurationForm): TimeseriesNodeConfiguration {
+ let processingSettings: ProcessingSettings;
+ if (config.processingSettings.isAdvanced) {
+ processingSettings = {
+ ...config.processingSettings.advanced,
+ type: ProcessingType.ADVANCED
+ };
+ } else {
+ processingSettings = {
+ type: config.processingSettings.type,
+ deduplicationIntervalSecs: config.processingSettings?.deduplicationIntervalSecs
+ };
+ }
+ return {
+ ...config,
+ processingSettings
+ };
+ }
+
+ protected onConfigurationSet(config: TimeseriesNodeConfigurationForm) {
this.timeseriesConfigForm = this.fb.group({
- defaultTTL: [configuration ? configuration.defaultTTL : null, [Validators.required, Validators.min(0)]],
- skipLatestPersistence: [configuration ? configuration.skipLatestPersistence : false, []],
- useServerTs: [configuration ? configuration.useServerTs : false, []]
+ processingSettings: this.fb.group({
+ isAdvanced: [config?.processingSettings?.isAdvanced ?? false],
+ type: [config?.processingSettings?.type ?? ProcessingType.ON_EVERY_MESSAGE],
+ deduplicationIntervalSecs: [
+ {value: config?.processingSettings?.deduplicationIntervalSecs ?? 60, 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 processingForm = this.timeseriesConfigForm.get('processingSettings') as FormGroup;
+ const isAdvanced: boolean = processingForm.get('isAdvanced').value;
+ const type: ProcessingType = processingForm.get('type').value;
+ if (!isAdvanced && type === ProcessingType.DEDUPLICATE) {
+ processingForm.get('deduplicationIntervalSecs').enable({emitEvent});
+ } else {
+ processingForm.get('deduplicationIntervalSecs').disable({emitEvent});
+ }
+ if (isAdvanced) {
+ processingForm.get('advanced').enable({emitEvent});
+ } else {
+ processingForm.get('advanced').disable({emitEvent});
+ }
+ }
}
diff --git a/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.models.ts b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.models.ts
new file mode 100644
index 0000000000..e9d024663d
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/rule-node/action/timeseries-config.models.ts
@@ -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;
+
+export interface TimeseriesNodeConfiguration {
+ processingSettings: ProcessingSettings;
+ defaultTTL: number;
+ useServerTs: boolean;
+}
+
+export interface TimeseriesNodeConfigurationForm extends Omit
{
+ processingSettings: ProcessingSettingsForm
+}
+
+export type ProcessingSettings = BasicProcessingSettings & Partial & Partial;
+
+export type ProcessingSettingsForm = Omit & {
+ isAdvanced: boolean;
+ advanced?: Partial;
+ type: ProcessingType;
+};
+
+export enum ProcessingType {
+ ON_EVERY_MESSAGE = 'ON_EVERY_MESSAGE',
+ DEDUPLICATE = 'DEDUPLICATE',
+ WEBSOCKETS_ONLY = 'WEBSOCKETS_ONLY',
+ ADVANCED = 'ADVANCED',
+ SKIP = 'SKIP'
+}
+
+export const ProcessingTypeTranslationMap = new Map([
+ [ProcessingType.ON_EVERY_MESSAGE, 'rule-node-config.save-time-series.strategy-type.every-message'],
+ [ProcessingType.DEDUPLICATE, 'rule-node-config.save-time-series.strategy-type.deduplicate'],
+ [ProcessingType.WEBSOCKETS_ONLY, 'rule-node-config.save-time-series.strategy-type.web-sockets-only'],
+ [ProcessingType.SKIP, 'rule-node-config.save-time-series.strategy-type.skip'],
+])
+
+export interface BasicProcessingSettings {
+ type: ProcessingType;
+}
+
+export interface DeduplicateProcessingStrategy extends BasicProcessingSettings{
+ deduplicationIntervalSecs: number;
+}
+
+export interface AdvancedProcessingStrategy extends BasicProcessingSettings{
+ timeseries: AdvancedProcessingConfig;
+ latest: AdvancedProcessingConfig;
+ webSockets: AdvancedProcessingConfig;
+}
+
+export type AdvancedProcessingConfig = WithOptional;
+
+export const defaultAdvancedProcessingConfig: AdvancedProcessingConfig = {
+ type: ProcessingType.ON_EVERY_MESSAGE
+}
+
+export const defaultAdvancedPersistenceStrategy: Omit = {
+ timeseries: defaultAdvancedProcessingConfig,
+ latest: defaultAdvancedProcessingConfig,
+ webSockets: defaultAdvancedProcessingConfig,
+}
diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/common-rule-node-config.module.ts b/ui-ngx/src/app/modules/home/components/rule-node/common/common-rule-node-config.module.ts
index c72b9901c9..b6370be318 100644
--- a/ui-ngx/src/app/modules/home/components/rule-node/common/common-rule-node-config.module.ts
+++ b/ui-ngx/src/app/modules/home/components/rule-node/common/common-rule-node-config.module.ts
@@ -33,6 +33,7 @@ import { RelationsQueryConfigOldComponent } from './relations-query-config-old.c
import { SelectAttributesComponent } from './select-attributes.component';
import { AlarmStatusSelectComponent } from './alarm-status-select.component';
import { ExampleHintComponent } from './example-hint.component';
+import { TimeUnitInputComponent } from './time-unit-input.component';
@NgModule({
declarations: [
@@ -50,7 +51,8 @@ import { ExampleHintComponent } from './example-hint.component';
RelationsQueryConfigOldComponent,
SelectAttributesComponent,
AlarmStatusSelectComponent,
- ExampleHintComponent
+ ExampleHintComponent,
+ TimeUnitInputComponent
],
imports: [
CommonModule,
@@ -72,7 +74,8 @@ import { ExampleHintComponent } from './example-hint.component';
RelationsQueryConfigOldComponent,
SelectAttributesComponent,
AlarmStatusSelectComponent,
- ExampleHintComponent
+ ExampleHintComponent,
+ TimeUnitInputComponent
]
})
diff --git a/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.html b/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.html
index 238fdce6af..0981daa5cb 100644
--- a/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.html
+++ b/ui-ngx/src/app/modules/home/components/rule-node/common/example-hint.component.html
@@ -15,11 +15,11 @@
limitations under the License.
-->
-