fixed conflicts
This commit is contained in:
commit
3e6e3b9015
@ -28,6 +28,8 @@ export interface SysParamsState {
|
|||||||
userSettings: UserSettings;
|
userSettings: UserSettings;
|
||||||
maxResourceSize: number;
|
maxResourceSize: number;
|
||||||
maxDebugModeDurationMinutes: number;
|
maxDebugModeDurationMinutes: number;
|
||||||
|
maxDataPointsPerRollingArg: number;
|
||||||
|
maxArgumentsPerCF: number;
|
||||||
ruleChainDebugPerTenantLimitsConfiguration?: string;
|
ruleChainDebugPerTenantLimitsConfiguration?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,8 @@ const emptyUserAuthState: AuthPayload = {
|
|||||||
persistDeviceStateToTelemetry: false,
|
persistDeviceStateToTelemetry: false,
|
||||||
mobileQrEnabled: false,
|
mobileQrEnabled: false,
|
||||||
maxResourceSize: 0,
|
maxResourceSize: 0,
|
||||||
|
maxArgumentsPerCF: 0,
|
||||||
|
maxDataPointsPerRollingArg: 0,
|
||||||
maxDebugModeDurationMinutes: 0,
|
maxDebugModeDurationMinutes: 0,
|
||||||
userSettings: initialUserSettings
|
userSettings: initialUserSettings
|
||||||
};
|
};
|
||||||
|
|||||||
@ -119,9 +119,22 @@
|
|||||||
<tb-error noMargin [error]="errorText | translate" class="flex h-9 items-center pl-3"/>
|
<tb-error noMargin [error]="errorText | translate" class="flex h-9 items-center pl-3"/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="flex h-9 justify-between">
|
||||||
<button type="button" mat-stroked-button color="primary" #button (click)="manageArgument($event, button)">
|
<button
|
||||||
|
type="button"
|
||||||
|
mat-stroked-button
|
||||||
|
color="primary"
|
||||||
|
#button
|
||||||
|
(click)="manageArgument($event, button)"
|
||||||
|
[disabled]="maxArgumentsPerCF > 0 && argumentsFormArray.length >= maxArgumentsPerCF"
|
||||||
|
>
|
||||||
{{ 'calculated-fields.add-argument' | translate }}
|
{{ 'calculated-fields.add-argument' | translate }}
|
||||||
</button>
|
</button>
|
||||||
|
@if (maxArgumentsPerCF && argumentsFormArray.length >= maxArgumentsPerCF) {
|
||||||
|
<div class="tb-form-hint tb-primary-fill max-args-warning flex items-center gap-2">
|
||||||
|
<mat-icon>warning</mat-icon>
|
||||||
|
<span>{{ 'calculated-fields.hint.max-args' | translate }}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -30,6 +30,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.max-args-warning {
|
||||||
|
.mat-icon {
|
||||||
|
color: #FAA405;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.tb-form-table-row-cell-buttons {
|
.tb-form-table-row-cell-buttons {
|
||||||
--mat-badge-legacy-small-size-container-size: 8px;
|
--mat-badge-legacy-small-size-container-size: 8px;
|
||||||
--mat-badge-small-size-container-overlap-offset: -5px;
|
--mat-badge-small-size-container-overlap-offset: -5px;
|
||||||
|
|||||||
@ -55,6 +55,9 @@ import { TbPopoverComponent } from '@shared/components/popover.component';
|
|||||||
import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract';
|
import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract';
|
||||||
import { EntityService } from '@core/http/entity.service';
|
import { EntityService } from '@core/http/entity.service';
|
||||||
import { MatSort } from '@angular/material/sort';
|
import { MatSort } from '@angular/material/sort';
|
||||||
|
import { getCurrentAuthState } from '@core/auth/auth.selectors';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { AppState } from '@core/core.state';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-calculated-field-arguments-table',
|
selector: 'tb-calculated-field-arguments-table',
|
||||||
@ -93,6 +96,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
|
|||||||
readonly ArgumentEntityType = ArgumentEntityType;
|
readonly ArgumentEntityType = ArgumentEntityType;
|
||||||
readonly ArgumentType = ArgumentType;
|
readonly ArgumentType = ArgumentType;
|
||||||
readonly CalculatedFieldType = CalculatedFieldType;
|
readonly CalculatedFieldType = CalculatedFieldType;
|
||||||
|
readonly maxArgumentsPerCF = getCurrentAuthState(this.store).maxArgumentsPerCF;
|
||||||
|
|
||||||
private popoverComponent: TbPopoverComponent<CalculatedFieldArgumentPanelComponent>;
|
private popoverComponent: TbPopoverComponent<CalculatedFieldArgumentPanelComponent>;
|
||||||
private propagateChange: (argumentsObj: Record<string, CalculatedFieldArgument>) => void = () => {};
|
private propagateChange: (argumentsObj: Record<string, CalculatedFieldArgument>) => void = () => {};
|
||||||
@ -105,6 +109,7 @@ export class CalculatedFieldArgumentsTableComponent implements ControlValueAcces
|
|||||||
private renderer: Renderer2,
|
private renderer: Renderer2,
|
||||||
private entityService: EntityService,
|
private entityService: EntityService,
|
||||||
private destroyRef: DestroyRef,
|
private destroyRef: DestroyRef,
|
||||||
|
private store: Store<AppState>
|
||||||
) {
|
) {
|
||||||
this.argumentsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(value => {
|
this.argumentsFormArray.valueChanges.pipe(takeUntilDestroyed()).subscribe(value => {
|
||||||
this.updateEntityNameMap(value);
|
this.updateEntityNameMap(value);
|
||||||
|
|||||||
@ -78,7 +78,7 @@
|
|||||||
<div class="tb-form-panel-title tb-required">{{ 'calculated-fields.expression' | translate }}</div>
|
<div class="tb-form-panel-title tb-required">{{ 'calculated-fields.expression' | translate }}</div>
|
||||||
@if (fieldFormGroup.get('type').value === CalculatedFieldType.SIMPLE) {
|
@if (fieldFormGroup.get('type').value === CalculatedFieldType.SIMPLE) {
|
||||||
<mat-form-field class="mt-3" appearance="outline" subscriptSizing="dynamic">
|
<mat-form-field class="mt-3" appearance="outline" subscriptSizing="dynamic">
|
||||||
<input matInput formControlName="expressionSIMPLE" maxlength="255" [placeholder]="'action.set' | translate" required>
|
<input matInput formControlName="expressionSIMPLE" maxlength="255" [placeholder]="'(temperature - 32) / 1.8'" required>
|
||||||
@if (configFormGroup.get('expressionSIMPLE').errors && configFormGroup.get('expressionSIMPLE').touched) {
|
@if (configFormGroup.get('expressionSIMPLE').errors && configFormGroup.get('expressionSIMPLE').touched) {
|
||||||
<mat-error>
|
<mat-error>
|
||||||
@if (configFormGroup.get('expressionSIMPLE').hasError('required')) {
|
@if (configFormGroup.get('expressionSIMPLE').hasError('required')) {
|
||||||
@ -89,6 +89,8 @@
|
|||||||
{{ 'calculated-fields.hint.expression-max-length' | translate }}
|
{{ 'calculated-fields.hint.expression-max-length' | translate }}
|
||||||
}
|
}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
} @else {
|
||||||
|
<mat-hint>{{ 'calculated-fields.hint.expression' | translate }}</mat-hint>
|
||||||
}
|
}
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
} @else {
|
} @else {
|
||||||
|
|||||||
@ -40,7 +40,7 @@
|
|||||||
&-key {
|
&-key {
|
||||||
color: #C52F00;
|
color: #C52F00;
|
||||||
}
|
}
|
||||||
&-ts, &-time-window, &-values, &-value {
|
&-ts, &-time-window, &-values, &-value, &-func {
|
||||||
color: #7214D0;
|
color: #7214D0;
|
||||||
}
|
}
|
||||||
&-start-ts, &-end-ts, &-limit {
|
&-start-ts, &-end-ts, &-limit {
|
||||||
|
|||||||
@ -166,10 +166,19 @@
|
|||||||
formControlName="timeWindow"
|
formControlName="timeWindow"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="tb-form-row limit-field-row">
|
@if (maxDataPointsPerRollingArg) {
|
||||||
<div class="fixed-title-width">{{ 'calculated-fields.limit' | translate }}</div>
|
<div class="tb-form-row limit-field-row">
|
||||||
<tb-datapoints-limit class="w-full flex-1" formControlName="limit"/>
|
<div class="fixed-title-width">{{ 'calculated-fields.limit' | translate }}</div>
|
||||||
</div>
|
<div class="limit-slider-container flex w-full flex-1 flex-row items-center justify-start">
|
||||||
|
<mat-slider class="flex-1" min="1" max="{{maxDataPointsPerRollingArg}}">
|
||||||
|
<input matSliderThumb formControlName="limit" [value]="argumentFormGroup.get('limit').value"/>
|
||||||
|
</mat-slider>
|
||||||
|
<mat-form-field class="limit-slider-value" subscriptSizing="dynamic" appearance="outline">
|
||||||
|
<input matInput formControlName="limit" type="number" step="1" [value]="argumentFormGroup.get('limit').value" min="1" max="{{maxDataPointsPerRollingArg}}"/>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -28,9 +28,7 @@ $panel-width: 520px;
|
|||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
:host ::ng-deep {
|
|
||||||
.limit-field-row {
|
.limit-field-row {
|
||||||
@media screen and (max-width: $panel-width) {
|
@media screen and (max-width: $panel-width) {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -42,7 +40,9 @@ $panel-width: 520px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::ng-deep {
|
||||||
.time-interval-field {
|
.time-interval-field {
|
||||||
.advanced-input {
|
.advanced-input {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@ -38,7 +38,9 @@ import { EntityFilter } from '@shared/models/query/query.models';
|
|||||||
import { AliasFilterType } from '@shared/models/alias.models';
|
import { AliasFilterType } from '@shared/models/alias.models';
|
||||||
import { merge } from 'rxjs';
|
import { merge } from 'rxjs';
|
||||||
import { MINUTE } from '@shared/models/time/time.models';
|
import { MINUTE } from '@shared/models/time/time.models';
|
||||||
import { TimeService } from '@core/services/time.service';
|
import { getCurrentAuthState } from '@core/auth/auth.selectors';
|
||||||
|
import { AppState } from '@core/core.state';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-calculated-field-argument-panel',
|
selector: 'tb-calculated-field-argument-panel',
|
||||||
@ -58,7 +60,8 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit {
|
|||||||
|
|
||||||
argumentsDataApplied = output<{ value: CalculatedFieldArgumentValue, index: number }>();
|
argumentsDataApplied = output<{ value: CalculatedFieldArgumentValue, index: number }>();
|
||||||
|
|
||||||
readonly defaultLimit = Math.max(this.timeService.getMinDatapointsLimit(), Math.floor(this.timeService.getMaxDatapointsLimit() / 10));
|
readonly maxDataPointsPerRollingArg = getCurrentAuthState(this.store).maxDataPointsPerRollingArg;
|
||||||
|
readonly defaultLimit = Math.floor(this.maxDataPointsPerRollingArg / 10);
|
||||||
|
|
||||||
argumentFormGroup = this.fb.group({
|
argumentFormGroup = this.fb.group({
|
||||||
argumentName: ['', [Validators.required, this.uniqNameRequired(), Validators.pattern(charsWithNumRegex), Validators.maxLength(255)]],
|
argumentName: ['', [Validators.required, this.uniqNameRequired(), Validators.pattern(charsWithNumRegex), Validators.maxLength(255)]],
|
||||||
@ -72,7 +75,7 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit {
|
|||||||
scope: [{ value: AttributeScope.SERVER_SCOPE, disabled: true }, [Validators.required]],
|
scope: [{ value: AttributeScope.SERVER_SCOPE, disabled: true }, [Validators.required]],
|
||||||
}),
|
}),
|
||||||
defaultValue: ['', [Validators.pattern(noLeadTrailSpacesRegex)]],
|
defaultValue: ['', [Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||||
limit: [this.defaultLimit],
|
limit: [{ value: this.defaultLimit, disabled: !this.maxDataPointsPerRollingArg }],
|
||||||
timeWindow: [MINUTE * 15],
|
timeWindow: [MINUTE * 15],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -96,7 +99,7 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit {
|
|||||||
private fb: FormBuilder,
|
private fb: FormBuilder,
|
||||||
private cd: ChangeDetectorRef,
|
private cd: ChangeDetectorRef,
|
||||||
private popover: TbPopoverComponent<CalculatedFieldArgumentPanelComponent>,
|
private popover: TbPopoverComponent<CalculatedFieldArgumentPanelComponent>,
|
||||||
private timeService: TimeService
|
private store: Store<AppState>
|
||||||
) {
|
) {
|
||||||
this.observeEntityFilterChanges();
|
this.observeEntityFilterChanges();
|
||||||
this.observeEntityTypeChanges()
|
this.observeEntityTypeChanges()
|
||||||
|
|||||||
@ -45,7 +45,7 @@
|
|||||||
[scriptLanguage]="ScriptLanguage.TBEL"
|
[scriptLanguage]="ScriptLanguage.TBEL"
|
||||||
[editorCompleter]="data.argumentsEditorCompleter"
|
[editorCompleter]="data.argumentsEditorCompleter"
|
||||||
resultType="object"
|
resultType="object"
|
||||||
helpId="calculated-field/test-expression_fn"
|
helpId="calculated-field/expression_fn"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -78,7 +78,7 @@
|
|||||||
&-key {
|
&-key {
|
||||||
color: #C52F00;
|
color: #C52F00;
|
||||||
}
|
}
|
||||||
&-ts, &-time-window, &-values, &-value {
|
&-ts, &-time-window, &-values, &-value, &-func {
|
||||||
color: #7214D0;
|
color: #7214D0;
|
||||||
}
|
}
|
||||||
&-start-ts, &-end-ts, &-limit {
|
&-start-ts, &-end-ts, &-limit {
|
||||||
|
|||||||
@ -229,6 +229,92 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<fieldset class="fields-group">
|
||||||
|
<legend class="group-title">
|
||||||
|
{{ 'tenant-profile.calculated-fields' | translate }} <span translate>tenant-profile.unlimited</span>
|
||||||
|
</legend>
|
||||||
|
<div class="fields-element flex flex-1 flex-row xs:flex-col gt-xs:gap-4">
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="fill" subscriptSizing="dynamic">
|
||||||
|
<mat-label translate>tenant-profile.max-calculated-fields</mat-label>
|
||||||
|
<input matInput required min="0" step="1"
|
||||||
|
formControlName="maxCalculatedFieldsPerEntity"
|
||||||
|
type="number">
|
||||||
|
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxCalculatedFieldsPerEntity').hasError('required')">
|
||||||
|
{{ 'tenant-profile.max-calculated-fields-required' | translate}}
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxCalculatedFieldsPerEntity').hasError('min')">
|
||||||
|
{{ 'tenant-profile.max-calculated-fields-range' | translate}}
|
||||||
|
</mat-error>
|
||||||
|
<mat-hint></mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="fill" subscriptSizing="dynamic">
|
||||||
|
<mat-label translate>tenant-profile.max-data-points-per-rolling-arg</mat-label>
|
||||||
|
<input matInput required min="0" step="1"
|
||||||
|
formControlName="maxDataPointsPerRollingArg"
|
||||||
|
type="number">
|
||||||
|
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDataPointsPerRollingArg').hasError('required')">
|
||||||
|
{{ 'tenant-profile.max-data-points-per-rolling-arg-required' | translate}}
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDataPointsPerRollingArg').hasError('min')">
|
||||||
|
{{ 'tenant-profile.max-data-points-per-rolling-arg-range' | translate}}
|
||||||
|
</mat-error>
|
||||||
|
<mat-hint></mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-1 flex-row xs:flex-col gt-xs:gap-4">
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="fill" subscriptSizing="dynamic">
|
||||||
|
<mat-label translate>tenant-profile.max-arguments-per-cf</mat-label>
|
||||||
|
<input matInput required min="0" step="1"
|
||||||
|
formControlName="maxArgumentsPerCF"
|
||||||
|
type="number">
|
||||||
|
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxArgumentsPerCF').hasError('required')">
|
||||||
|
{{ 'tenant-profile.max-arguments-per-cf-required' | translate}}
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxArgumentsPerCF').hasError('min')">
|
||||||
|
{{ 'tenant-profile.max-arguments-per-cf-range' | translate}}
|
||||||
|
</mat-error>
|
||||||
|
<mat-hint></mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
<div class="flex-1"></div>
|
||||||
|
</div>
|
||||||
|
<mat-expansion-panel class="configuration-panel">
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-description class="flex items-stretch justify-end" translate>
|
||||||
|
tenant-profile.advanced-settings
|
||||||
|
</mat-panel-description>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<ng-template matExpansionPanelContent>
|
||||||
|
<div class="flex flex-1 flex-row xs:flex-col gt-xs:gap-4">
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="fill" subscriptSizing="dynamic">
|
||||||
|
<mat-label translate>tenant-profile.max-state-size</mat-label>
|
||||||
|
<input matInput required min="0" step="1"
|
||||||
|
formControlName="maxStateSizeInKBytes"
|
||||||
|
type="number">
|
||||||
|
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxStateSizeInKBytes').hasError('required')">
|
||||||
|
{{ 'tenant-profile.max-state-size-required' | translate}}
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxStateSizeInKBytes').hasError('min')">
|
||||||
|
{{ 'tenant-profile.max-state-size-range' | translate}}
|
||||||
|
</mat-error>
|
||||||
|
<mat-hint></mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="fill" subscriptSizing="dynamic">
|
||||||
|
<mat-label translate>tenant-profile.max-value-argument-size</mat-label>
|
||||||
|
<input matInput required min="0" step="1"
|
||||||
|
formControlName="maxSingleValueArgumentSizeInKBytes"
|
||||||
|
type="number">
|
||||||
|
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxSingleValueArgumentSizeInKBytes').hasError('required')">
|
||||||
|
{{ 'tenant-profile.max-value-argument-size-required' | translate}}
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxSingleValueArgumentSizeInKBytes').hasError('min')">
|
||||||
|
{{ 'tenant-profile.max-value-argument-size-range' | translate}}
|
||||||
|
</mat-error>
|
||||||
|
<mat-hint></mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<fieldset class="fields-group">
|
<fieldset class="fields-group">
|
||||||
<legend class="group-title">
|
<legend class="group-title">
|
||||||
@ -638,6 +724,12 @@
|
|||||||
[type]="rateLimitsType.EDGE_UPLINK_MESSAGES_PER_EDGE_RATE_LIMIT">
|
[type]="rateLimitsType.EDGE_UPLINK_MESSAGES_PER_EDGE_RATE_LIMIT">
|
||||||
</tb-rate-limits>
|
</tb-rate-limits>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex flex-1 flex-row xs:flex-col gt-xs:gap-4">
|
||||||
|
<tb-rate-limits class="flex-1" formControlName="calculatedFieldDebugEventsRateLimit"
|
||||||
|
[type]="rateLimitsType.CALCULATED_FIELD_DEBUG_EVENT_RATE_LIMIT">
|
||||||
|
</tb-rate-limits>
|
||||||
|
<div class="flex-1"></div>
|
||||||
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|||||||
@ -118,7 +118,13 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA
|
|||||||
edgeEventRateLimits: [null, []],
|
edgeEventRateLimits: [null, []],
|
||||||
edgeEventRateLimitsPerEdge: [null, []],
|
edgeEventRateLimitsPerEdge: [null, []],
|
||||||
edgeUplinkMessagesRateLimits: [null, []],
|
edgeUplinkMessagesRateLimits: [null, []],
|
||||||
edgeUplinkMessagesRateLimitsPerEdge: [null, []]
|
edgeUplinkMessagesRateLimitsPerEdge: [null, []],
|
||||||
|
maxCalculatedFieldsPerEntity: [null, [Validators.required, Validators.min(0)]],
|
||||||
|
maxArgumentsPerCF: [null, [Validators.required, Validators.min(0)]],
|
||||||
|
maxDataPointsPerRollingArg: [null, [Validators.required, Validators.min(0)]],
|
||||||
|
maxStateSizeInKBytes: [null, [Validators.required, Validators.min(0)]],
|
||||||
|
calculatedFieldDebugEventsRateLimit: [null, []],
|
||||||
|
maxSingleValueArgumentSizeInKBytes: [null, [Validators.required, Validators.min(0)]],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.defaultTenantProfileConfigurationFormGroup.get('smsEnabled').valueChanges.pipe(
|
this.defaultTenantProfileConfigurationFormGroup.get('smsEnabled').valueChanges.pipe(
|
||||||
|
|||||||
@ -45,7 +45,8 @@ export enum RateLimitsType {
|
|||||||
EDGE_EVENTS_RATE_LIMIT = 'EDGE_EVENTS_RATE_LIMIT',
|
EDGE_EVENTS_RATE_LIMIT = 'EDGE_EVENTS_RATE_LIMIT',
|
||||||
EDGE_EVENTS_PER_EDGE_RATE_LIMIT = 'EDGE_EVENTS_PER_EDGE_RATE_LIMIT',
|
EDGE_EVENTS_PER_EDGE_RATE_LIMIT = 'EDGE_EVENTS_PER_EDGE_RATE_LIMIT',
|
||||||
EDGE_UPLINK_MESSAGES_RATE_LIMIT = 'EDGE_UPLINK_MESSAGES_RATE_LIMIT',
|
EDGE_UPLINK_MESSAGES_RATE_LIMIT = 'EDGE_UPLINK_MESSAGES_RATE_LIMIT',
|
||||||
EDGE_UPLINK_MESSAGES_PER_EDGE_RATE_LIMIT = 'EDGE_UPLINK_MESSAGES_PER_EDGE_RATE_LIMIT'
|
EDGE_UPLINK_MESSAGES_PER_EDGE_RATE_LIMIT = 'EDGE_UPLINK_MESSAGES_PER_EDGE_RATE_LIMIT',
|
||||||
|
CALCULATED_FIELD_DEBUG_EVENT_RATE_LIMIT = 'CALCULATED_FIELD_DEBUG_EVENT_RATE_LIMIT',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const rateLimitsLabelTranslationMap = new Map<RateLimitsType, string>(
|
export const rateLimitsLabelTranslationMap = new Map<RateLimitsType, string>(
|
||||||
@ -74,6 +75,7 @@ export const rateLimitsLabelTranslationMap = new Map<RateLimitsType, string>(
|
|||||||
[RateLimitsType.EDGE_EVENTS_PER_EDGE_RATE_LIMIT, 'tenant-profile.rate-limits.edge-events-per-edge-rate-limit'],
|
[RateLimitsType.EDGE_EVENTS_PER_EDGE_RATE_LIMIT, 'tenant-profile.rate-limits.edge-events-per-edge-rate-limit'],
|
||||||
[RateLimitsType.EDGE_UPLINK_MESSAGES_RATE_LIMIT, 'tenant-profile.rate-limits.edge-uplink-messages-rate-limit'],
|
[RateLimitsType.EDGE_UPLINK_MESSAGES_RATE_LIMIT, 'tenant-profile.rate-limits.edge-uplink-messages-rate-limit'],
|
||||||
[RateLimitsType.EDGE_UPLINK_MESSAGES_PER_EDGE_RATE_LIMIT, 'tenant-profile.rate-limits.edge-uplink-messages-per-edge-rate-limit'],
|
[RateLimitsType.EDGE_UPLINK_MESSAGES_PER_EDGE_RATE_LIMIT, 'tenant-profile.rate-limits.edge-uplink-messages-per-edge-rate-limit'],
|
||||||
|
[RateLimitsType.CALCULATED_FIELD_DEBUG_EVENT_RATE_LIMIT, 'tenant-profile.rate-limits.calculated-field-debug-event-rate-limit'],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -103,6 +105,7 @@ export const rateLimitsDialogTitleTranslationMap = new Map<RateLimitsType, strin
|
|||||||
[RateLimitsType.EDGE_EVENTS_PER_EDGE_RATE_LIMIT, 'tenant-profile.rate-limits.edit-edge-events-per-edge-rate-limit'],
|
[RateLimitsType.EDGE_EVENTS_PER_EDGE_RATE_LIMIT, 'tenant-profile.rate-limits.edit-edge-events-per-edge-rate-limit'],
|
||||||
[RateLimitsType.EDGE_UPLINK_MESSAGES_RATE_LIMIT, 'tenant-profile.rate-limits.edit-edge-uplink-messages-rate-limit'],
|
[RateLimitsType.EDGE_UPLINK_MESSAGES_RATE_LIMIT, 'tenant-profile.rate-limits.edit-edge-uplink-messages-rate-limit'],
|
||||||
[RateLimitsType.EDGE_UPLINK_MESSAGES_PER_EDGE_RATE_LIMIT, 'tenant-profile.rate-limits.edit-edge-uplink-messages-per-edge-rate-limit'],
|
[RateLimitsType.EDGE_UPLINK_MESSAGES_PER_EDGE_RATE_LIMIT, 'tenant-profile.rate-limits.edit-edge-uplink-messages-per-edge-rate-limit'],
|
||||||
|
[RateLimitsType.CALCULATED_FIELD_DEBUG_EVENT_RATE_LIMIT, 'tenant-profile.rate-limits.edit-calculated-field-debug-event-rate-limit']
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import { AliasFilterType } from '@shared/models/alias.models';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { TbEditorCompleter } from '@shared/models/ace/completion.models';
|
import { TbEditorCompleter } from '@shared/models/ace/completion.models';
|
||||||
import {
|
import {
|
||||||
|
AceHighlightRule,
|
||||||
AceHighlightRules,
|
AceHighlightRules,
|
||||||
dotOperatorHighlightRule,
|
dotOperatorHighlightRule,
|
||||||
endGroupHighlightRule
|
endGroupHighlightRule
|
||||||
@ -268,12 +269,159 @@ export const CalculatedFieldAttributeValueArgumentAutocomplete = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
export const CalculatedFieldRollingValueArgumentFunctionsAutocomplete = {
|
||||||
|
max: {
|
||||||
|
meta: 'function',
|
||||||
|
description: 'Computes the maximum value in the list of rolling argument values. Returns NaN if any value is NaN and ignoreNaN is false.',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: 'ignoreNaN',
|
||||||
|
description: 'Whether to ignore NaN values. Equals true by default.',
|
||||||
|
type: 'boolean',
|
||||||
|
optional: true,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
return: {
|
||||||
|
description: 'The maximum value, or NaN if applicable',
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
min: {
|
||||||
|
meta: 'function',
|
||||||
|
description: 'Computes the minimum value in the list of rolling argument values. Returns NaN if any value is NaN and ignoreNaN is false.',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: 'ignoreNaN',
|
||||||
|
description: 'Whether to ignore NaN values. Equals true by default.',
|
||||||
|
type: 'boolean',
|
||||||
|
optional: true,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
return: {
|
||||||
|
description: 'The minimum value, or NaN if applicable',
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mean: {
|
||||||
|
meta: 'function',
|
||||||
|
description: 'Computes the mean value of the rolling argument values list. Returns NaN if any value is NaN and ignoreNaN is false.',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: 'ignoreNaN',
|
||||||
|
description: 'Whether to ignore NaN values. Equals true by default.',
|
||||||
|
type: 'boolean',
|
||||||
|
optional: true,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
return: {
|
||||||
|
description: 'The mean value, or NaN if applicable',
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
std: {
|
||||||
|
meta: 'function',
|
||||||
|
description: 'Computes the standard deviation in the list of rolling argument values. Returns NaN if any value is NaN and ignoreNaN is false.',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: 'ignoreNaN',
|
||||||
|
description: 'Whether to ignore NaN values. Equals true by default.',
|
||||||
|
type: 'boolean',
|
||||||
|
optional: true,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
return: {
|
||||||
|
description: 'The standard deviation, or NaN if applicable',
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
median: {
|
||||||
|
meta: 'function',
|
||||||
|
description: 'Computes the median value of the rolling argument values list. Returns NaN if any value is NaN and ignoreNaN is false.',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: 'ignoreNaN',
|
||||||
|
description: 'Whether to ignore NaN values. Equals true by default.',
|
||||||
|
type: 'boolean',
|
||||||
|
optional: true,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
return: {
|
||||||
|
description: 'The median value, or NaN if applicable',
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
count: {
|
||||||
|
meta: 'function',
|
||||||
|
description: 'Counts values in the list of rolling argument values. Counts non-NaN values if ignoreNaN is true, otherwise - total size.',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: 'ignoreNaN',
|
||||||
|
description: 'Whether to ignore NaN values. Equals true by default.',
|
||||||
|
type: 'boolean',
|
||||||
|
optional: true,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
return: {
|
||||||
|
description: 'The count of values',
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
last: {
|
||||||
|
meta: 'function',
|
||||||
|
description: 'Returns the last non-NaN value in the list of rolling argument values if ignoreNaN is true, otherwise - the last value.',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: 'ignoreNaN',
|
||||||
|
description: 'Whether to ignore NaN values. Equals true by default.',
|
||||||
|
type: 'boolean',
|
||||||
|
optional: true,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
return: {
|
||||||
|
description: 'The last value, or NaN if applicable',
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
first: {
|
||||||
|
meta: 'function',
|
||||||
|
description: 'Returns the first non-NaN value in the list of rolling argument values if ignoreNaN is true, otherwise - the first value.',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: 'ignoreNaN',
|
||||||
|
description: 'Whether to ignore NaN values. Equals true by default.',
|
||||||
|
type: 'boolean',
|
||||||
|
optional: true,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
return: {
|
||||||
|
description: 'The first value, or NaN if applicable',
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sum: {
|
||||||
|
meta: 'function',
|
||||||
|
description: 'Computes the sum of values in the list of rolling argument values. Returns NaN if any value is NaN and ignoreNaN is false.',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: 'ignoreNaN',
|
||||||
|
description: 'Whether to ignore NaN values. Equals true by default.',
|
||||||
|
type: 'boolean',
|
||||||
|
optional: true,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
return: {
|
||||||
|
description: 'The sum of values, or NaN if applicable',
|
||||||
|
type: 'number'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const CalculatedFieldRollingValueArgumentAutocomplete = {
|
export const CalculatedFieldRollingValueArgumentAutocomplete = {
|
||||||
meta: 'object',
|
meta: 'object',
|
||||||
type: '{ values: { ts: number; value: any; }[]; timeWindow: { startTs: number; endTs: number; limit: number } }; }',
|
type: '{ values: { ts: number; value: any; }[]; timeWindow: { startTs: number; endTs: number; limit: number } }; }',
|
||||||
description: 'Calculated field rolling value argument.',
|
description: 'Calculated field rolling value argument.',
|
||||||
children: {
|
children: {
|
||||||
|
...CalculatedFieldRollingValueArgumentFunctionsAutocomplete,
|
||||||
values: {
|
values: {
|
||||||
meta: 'array',
|
meta: 'array',
|
||||||
type: '{ ts: number; value: any; }[]',
|
type: '{ ts: number; value: any; }[]',
|
||||||
@ -355,6 +503,13 @@ const calculatedFieldSingleArgumentValueHighlightRules: AceHighlightRules = {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const calculatedFieldRollingArgumentValueFunctionsHighlightRules: Array<AceHighlightRule> =
|
||||||
|
['max', 'min', 'mean', 'std', 'median', 'count', 'last', 'first', 'sum'].map(funcName => ({
|
||||||
|
token: 'tb.calculated-field-func',
|
||||||
|
regex: `\\b${funcName}\\b`,
|
||||||
|
next: 'no_regex'
|
||||||
|
}));
|
||||||
|
|
||||||
const calculatedFieldRollingArgumentValueHighlightRules: AceHighlightRules = {
|
const calculatedFieldRollingArgumentValueHighlightRules: AceHighlightRules = {
|
||||||
calculatedFieldRollingArgumentValue: [
|
calculatedFieldRollingArgumentValue: [
|
||||||
dotOperatorHighlightRule,
|
dotOperatorHighlightRule,
|
||||||
@ -368,6 +523,7 @@ const calculatedFieldRollingArgumentValueHighlightRules: AceHighlightRules = {
|
|||||||
regex: /timeWindow/,
|
regex: /timeWindow/,
|
||||||
next: 'calculatedFieldRollingArgumentTimeWindow'
|
next: 'calculatedFieldRollingArgumentTimeWindow'
|
||||||
},
|
},
|
||||||
|
...calculatedFieldRollingArgumentValueFunctionsHighlightRules,
|
||||||
endGroupHighlightRule
|
endGroupHighlightRule
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@ -95,6 +95,13 @@ export interface DefaultTenantProfileConfiguration {
|
|||||||
rpcTtlDays: number;
|
rpcTtlDays: number;
|
||||||
queueStatsTtlDays: number;
|
queueStatsTtlDays: number;
|
||||||
ruleEngineExceptionsTtlDays: number;
|
ruleEngineExceptionsTtlDays: number;
|
||||||
|
|
||||||
|
maxCalculatedFieldsPerEntity: number;
|
||||||
|
maxArgumentsPerCF: number;
|
||||||
|
maxDataPointsPerRollingArg: number;
|
||||||
|
maxStateSizeInKBytes: number;
|
||||||
|
maxSingleValueArgumentSizeInKBytes: number;
|
||||||
|
calculatedFieldDebugEventsRateLimit: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TenantProfileConfigurations = DefaultTenantProfileConfiguration;
|
export type TenantProfileConfigurations = DefaultTenantProfileConfiguration;
|
||||||
@ -148,7 +155,13 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan
|
|||||||
alarmsTtlDays: 0,
|
alarmsTtlDays: 0,
|
||||||
rpcTtlDays: 0,
|
rpcTtlDays: 0,
|
||||||
queueStatsTtlDays: 0,
|
queueStatsTtlDays: 0,
|
||||||
ruleEngineExceptionsTtlDays: 0
|
ruleEngineExceptionsTtlDays: 0,
|
||||||
|
maxCalculatedFieldsPerEntity: 0,
|
||||||
|
maxArgumentsPerCF: 0,
|
||||||
|
maxDataPointsPerRollingArg: 0,
|
||||||
|
maxStateSizeInKBytes: 0,
|
||||||
|
maxSingleValueArgumentSizeInKBytes: 0,
|
||||||
|
calculatedFieldDebugEventsRateLimit: ''
|
||||||
};
|
};
|
||||||
configuration = {...defaultConfiguration, type: TenantProfileType.DEFAULT};
|
configuration = {...defaultConfiguration, type: TenantProfileType.DEFAULT};
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
<!-- [TODO]: [Calculated Fields] add content -->
|
|
||||||
@ -1068,7 +1068,9 @@
|
|||||||
"argument-name-pattern": "Argument name is invalid.",
|
"argument-name-pattern": "Argument name is invalid.",
|
||||||
"argument-name-duplicate": "Argument with such name already exists.",
|
"argument-name-duplicate": "Argument with such name already exists.",
|
||||||
"argument-name-max-length": "Argument name should be less than 256 characters.",
|
"argument-name-max-length": "Argument name should be less than 256 characters.",
|
||||||
"argument-type-required": "Argument type is required."
|
"argument-type-required": "Argument type is required.",
|
||||||
|
"max-args": "Maximum number of arguments reached.",
|
||||||
|
"expression": "Default expression demonstrates how to transform a temperature from Fahrenheit to Celsius."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"confirm-on-exit": {
|
"confirm-on-exit": {
|
||||||
@ -5474,6 +5476,7 @@
|
|||||||
"entities": "Entities",
|
"entities": "Entities",
|
||||||
"rule-engine": "Rule Engine",
|
"rule-engine": "Rule Engine",
|
||||||
"time-to-live": "Time-to-live",
|
"time-to-live": "Time-to-live",
|
||||||
|
"calculated-fields": "Calculated fields",
|
||||||
"alarms-and-notifications": "Alarms and notifications",
|
"alarms-and-notifications": "Alarms and notifications",
|
||||||
"ota-files-in-bytes": "Files",
|
"ota-files-in-bytes": "Files",
|
||||||
"ws-title": "WS",
|
"ws-title": "WS",
|
||||||
@ -5526,6 +5529,21 @@
|
|||||||
"tenant-entity-import-rate-limit": "Entity version load",
|
"tenant-entity-import-rate-limit": "Entity version load",
|
||||||
"tenant-notification-request-rate-limit": "Notification requests",
|
"tenant-notification-request-rate-limit": "Notification requests",
|
||||||
"tenant-notification-requests-per-rule-rate-limit": "Notification requests per notification rule",
|
"tenant-notification-requests-per-rule-rate-limit": "Notification requests per notification rule",
|
||||||
|
"max-calculated-fields": "Maximum number of calculated fields per entity",
|
||||||
|
"max-calculated-fields-range": "Maximum number of calculated fields per entity can't be negative",
|
||||||
|
"max-calculated-fields-required": "Maximum number of calculated fields per entity is required",
|
||||||
|
"max-data-points-per-rolling-arg": "Maximum number of data points in a time series rolling arguments",
|
||||||
|
"max-data-points-per-rolling-arg-range": "Maximum number of data points in a time series rolling arguments can't be negative",
|
||||||
|
"max-data-points-per-rolling-arg-required": "Maximum number of data points in a time series rolling arguments is required",
|
||||||
|
"max-arguments-per-cf": "Maximum number of arguments per calculated field",
|
||||||
|
"max-arguments-per-cf-range": "Maximum number of arguments per calculated field can't be negative",
|
||||||
|
"max-arguments-per-cf-required": "Maximum number of arguments per calculated field is required",
|
||||||
|
"max-state-size": "Maximum size of the state in KB",
|
||||||
|
"max-state-size-range": "Maximum size of the state in KB can't be negative",
|
||||||
|
"max-state-size-required": "Maximum size of the state in KB is required",
|
||||||
|
"max-value-argument-size": "Maximum size of the single value argument in KB",
|
||||||
|
"max-value-argument-size-range": "Maximum size of the single value argument in KB can't be negative",
|
||||||
|
"max-value-argument-size-required": "Maximum size of the single value argument in KB is required",
|
||||||
"max-transport-messages": "Transport messages maximum number",
|
"max-transport-messages": "Transport messages maximum number",
|
||||||
"max-transport-messages-required": "Transport messages maximum number is required.",
|
"max-transport-messages-required": "Transport messages maximum number is required.",
|
||||||
"max-transport-messages-range": "Transport messages maximum number can't be negative",
|
"max-transport-messages-range": "Transport messages maximum number can't be negative",
|
||||||
@ -5597,6 +5615,8 @@
|
|||||||
"advanced-settings": "Advanced settings",
|
"advanced-settings": "Advanced settings",
|
||||||
"edit-limit": "Edit limit",
|
"edit-limit": "Edit limit",
|
||||||
"but-less-than": "but less than",
|
"but-less-than": "but less than",
|
||||||
|
"calculated-field-debug-event-rate-limit": "Calculated field debug events",
|
||||||
|
"edit-calculated-field-debug-event-rate-limit": "Edit calculated field debug events rate limits",
|
||||||
"edit-transport-tenant-msg-title": "Edit transport tenant messages rate limits",
|
"edit-transport-tenant-msg-title": "Edit transport tenant messages rate limits",
|
||||||
"edit-transport-tenant-telemetry-msg-title": "Edit transport tenant telemetry messages rate limits",
|
"edit-transport-tenant-telemetry-msg-title": "Edit transport tenant telemetry messages rate limits",
|
||||||
"edit-transport-tenant-telemetry-data-points-title": "Edit transport tenant telemetry data points rate limits",
|
"edit-transport-tenant-telemetry-data-points-title": "Edit transport tenant telemetry data points rate limits",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user