10 changed files with 511 additions and 10 deletions
@ -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}); |
|||
} |
|||
|
|||
} |
|||
@ -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, |
|||
} |
|||
Loading…
Reference in new issue