diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule.module.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule.module.ts
index c02c11a170..52d91188bc 100644
--- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule.module.ts
+++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule.module.ts
@@ -50,6 +50,9 @@ import { AlarmRuleFilterDialogComponent } from "@home/components/alarm-rules/fil
import { AlarmRuleDetailsDialogComponent } from "@home/components/alarm-rules/alarm-rule-details-dialog.component";
import { AlarmRuleFilterConfigComponent } from "@home/components/alarm-rules/alarm-rule-filter-config.component";
import { AlarmRuleTableHeaderComponent } from "@home/components/alarm-rules/alarm-rule-table-header.component";
+import {
+ AlarmRuleFilterPredicateNoDataValueComponent
+} from "@home/components/alarm-rules/filter/alarm-rule-filter-predicate-no-data-value.component";
@NgModule({
declarations: [
@@ -69,7 +72,8 @@ import { AlarmRuleTableHeaderComponent } from "@home/components/alarm-rules/alar
AlarmRuleComplexFilterPredicateDialogComponent,
AlarmRuleDetailsDialogComponent,
AlarmRuleFilterConfigComponent,
- AlarmRuleTableHeaderComponent
+ AlarmRuleTableHeaderComponent,
+ AlarmRuleFilterPredicateNoDataValueComponent
],
imports: [
CommonModule,
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition-dialog.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition-dialog.component.html
index feafcaa0a4..5d94476e6e 100644
--- a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition-dialog.component.html
+++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition-dialog.component.html
@@ -78,10 +78,14 @@
alarm-rule.condition-type
-
+
{{ alarmConditionTypeTranslation.get(alarmConditionType) | translate }}
+ @if (isNoData) {
+ alarm-rule.condition-type-hint
+ }
@if (conditionFormGroup.get('type').value == AlarmConditionType.DURATION) {
}
@case (filterPredicateType.NUMERIC) {
@@ -68,7 +70,14 @@
}
}
- @if (type !== filterPredicateType.COMPLEX) {
+ @if (filterPredicateFormGroup.get('operation').value === stringOperation.NO_DATA) {
+
+
+ } @else if (type !== filterPredicateType.COMPLEX) {
{ };
@@ -132,11 +134,45 @@ export class AlarmRuleFilterPredicateComponent implements ControlValueAccessor,
writeValue(predicate: AlarmRuleFilterPredicate): void {
this.type = predicate.type;
- this.filterPredicateFormGroup.patchValue(predicate, {emitEvent: false});
+ if (predicate.type === AlarmRuleFilterPredicateType.NO_DATA) {
+ this.type = AlarmRuleFilterPredicateType[this.valueType];
+ this.filterPredicateFormGroup.patchValue({operation: 'NO_DATA', duration: predicate}, {emitEvent: false});
+ } else {
+ this.filterPredicateFormGroup.patchValue(predicate, {emitEvent: false});
+ }
}
private updateModel() {
- this.propagateChange({type: this.type, ...this.filterPredicateFormGroup.value});
+ const predicate = this.filterPredicateFormGroup.value;
+ if (predicate.operation === 'NO_DATA') {
+ this.propagateChange(predicate.duration);
+ } else {
+ if (!predicate.value) {
+ switch (this.valueType) {
+ case EntityKeyValueType.STRING:
+ predicate.value = {
+ staticValue: ''
+ };
+ break;
+ case EntityKeyValueType.NUMERIC:
+ predicate.value = {
+ staticValue: 0
+ };
+ break;
+ case EntityKeyValueType.DATE_TIME:
+ predicate.value = {
+ staticValue: Date.now()
+ };
+ break;
+ case EntityKeyValueType.BOOLEAN:
+ predicate.value = {
+ staticValue: false
+ };
+ break;
+ }
+ }
+ this.propagateChange({type: this.type, ...predicate});
+ }
}
public openComplexFilterDialog() {
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-text.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-text.component.ts
index 498b2a0a81..f824fdc876 100644
--- a/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-text.component.ts
+++ b/ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-text.component.ts
@@ -16,25 +16,27 @@
import { Component, Input } from '@angular/core';
import {
- booleanOperationTranslationMap,
ComplexOperation,
complexOperationTranslationMap,
- EntityKeyValueType,
- FilterPredicateType,
- numericOperationTranslationMap,
- stringOperationTranslationMap
+ EntityKeyValueType
} from '@shared/models/query/query.models';
import { TranslateService } from '@ngx-translate/core';
import { DatePipe } from '@angular/common';
import {
+ alarmRuleBooleanOperationTranslationMap,
AlarmRuleExpression,
AlarmRuleExpressionType,
AlarmRuleFilter,
AlarmRuleFilterPredicate,
+ AlarmRuleFilterPredicateType,
+ alarmRuleNumericOperationTranslationMap,
+ AlarmRuleStringOperation,
+ alarmRuleStringOperationTranslationMap,
ComplexAlarmRuleFilterPredicate
} from "@shared/models/alarm-rule.models";
import { CalculatedFieldArgument } from "@shared/models/calculated-field.models";
import { coerceBoolean } from "@shared/decorators/coercion";
+import { timeUnitTranslationMap } from "@shared/models/time/time.models";
@Component({
selector: 'tb-alarm-rule-filter-text',
@@ -137,21 +139,21 @@ export class AlarmRuleFilterTextComponent {
const filterOperation: ComplexOperation = complexOperation ? complexOperation : (keyFilter.operation ?? ComplexOperation.AND);
const predicates = keyFilterPredicates.map((keyFilterPredicate: AlarmRuleFilterPredicate) => {
- if (keyFilterPredicate.type === FilterPredicateType.COMPLEX) {
+ if (keyFilterPredicate.type === AlarmRuleFilterPredicateType.COMPLEX) {
const complexPredicate = keyFilterPredicate as ComplexAlarmRuleFilterPredicate;
const complexOperation = complexPredicate.operation ?? ComplexOperation.AND;
return this.filterPredicateToText(translate, datePipe, keyFilter, complexPredicate.predicates, complexOperation);
} else {
let operation: string;
let value: string;
- const val = keyFilterPredicate.value;
+ const val = keyFilterPredicate.type === AlarmRuleFilterPredicateType.NO_DATA ? keyFilterPredicate.duration : keyFilterPredicate.value;
const dynamicValue = val?.dynamicValueArgument?.length;
if (dynamicValue) {
value = '' + val?.dynamicValueArgument + '';
}
switch (keyFilterPredicate.type) {
- case FilterPredicateType.STRING:
- operation = translate.instant(stringOperationTranslationMap.get(keyFilterPredicate.operation));
+ case AlarmRuleFilterPredicateType.STRING:
+ operation = translate.instant(alarmRuleStringOperationTranslationMap.get(keyFilterPredicate.operation));
if (keyFilterPredicate.ignoreCase) {
operation += ' ' + translate.instant('filter.ignore-case');
}
@@ -159,8 +161,8 @@ export class AlarmRuleFilterTextComponent {
value = `'${keyFilterPredicate.value.staticValue}'`;
}
break;
- case FilterPredicateType.NUMERIC:
- operation = translate.instant(numericOperationTranslationMap.get(keyFilterPredicate.operation));
+ case AlarmRuleFilterPredicateType.NUMERIC:
+ operation = translate.instant(alarmRuleNumericOperationTranslationMap.get(keyFilterPredicate.operation));
if (!dynamicValue) {
if (keyFilter.valueType === EntityKeyValueType.DATE_TIME) {
value = datePipe.transform(keyFilterPredicate.value.staticValue, 'yyyy-MM-dd HH:mm');
@@ -169,12 +171,18 @@ export class AlarmRuleFilterTextComponent {
}
}
break;
- case FilterPredicateType.BOOLEAN:
- operation = translate.instant(booleanOperationTranslationMap.get(keyFilterPredicate.operation));
+ case AlarmRuleFilterPredicateType.BOOLEAN:
+ operation = translate.instant(alarmRuleBooleanOperationTranslationMap.get(keyFilterPredicate.operation));
if (!dynamicValue) {
value = translate.instant(keyFilterPredicate.value.staticValue ? 'value.true' : 'value.false');
}
break;
+ case AlarmRuleFilterPredicateType.NO_DATA:
+ operation = translate.instant(alarmRuleStringOperationTranslationMap.get(AlarmRuleStringOperation.NO_DATA));
+ if (!dynamicValue) {
+ value = keyFilterPredicate.duration.staticValue + ' ' + translate.instant(timeUnitTranslationMap.get(keyFilterPredicate.unit)).toLowerCase();
+ }
+ break;
}
if (!dynamicValue) {
value = `${value}`;
diff --git a/ui-ngx/src/app/shared/models/alarm-rule.models.ts b/ui-ngx/src/app/shared/models/alarm-rule.models.ts
index c9b549d78a..b92bf8bea6 100644
--- a/ui-ngx/src/app/shared/models/alarm-rule.models.ts
+++ b/ui-ngx/src/app/shared/models/alarm-rule.models.ts
@@ -118,7 +118,8 @@ export interface AlarmRulePredicateInfo {
export type AlarmRuleFilterPredicate = StringAlarmRuleFilterPredicate |
NumericAlarmRuleFilterPredicate |
BooleanAlarmRuleFilterPredicate |
- ComplexAlarmRuleFilterPredicate;
+ ComplexAlarmRuleFilterPredicate |
+ NoDataAlarmRuleFilterPredicate;
export interface AlarmRuleValue {
dynamicValueArgument?: string;
@@ -126,26 +127,33 @@ export interface AlarmRuleValue {
}
export interface StringAlarmRuleFilterPredicate {
- type: FilterPredicateType.STRING;
- operation: StringOperation;
+ type: AlarmRuleFilterPredicateType.STRING;
+ operation: AlarmRuleStringOperation;
value: AlarmRuleValue;
ignoreCase: boolean;
}
export interface NumericAlarmRuleFilterPredicate {
- type: FilterPredicateType.NUMERIC;
- operation: NumericOperation;
+ type: AlarmRuleFilterPredicateType.NUMERIC;
+ operation: AlarmRuleNumericOperation;
value: AlarmRuleValue;
}
export interface BooleanAlarmRuleFilterPredicate {
- type: FilterPredicateType.BOOLEAN;
- operation: BooleanOperation;
+ type: AlarmRuleFilterPredicateType.BOOLEAN;
+ operation: AlarmRuleBooleanOperation;
value: AlarmRuleValue;
}
+export interface NoDataAlarmRuleFilterPredicate {
+ type: AlarmRuleFilterPredicateType.NO_DATA;
+ unit: TimeUnit,
+ operation: AlarmRuleStringOperation.NO_DATA | AlarmRuleNumericOperation.NO_DATA | AlarmRuleBooleanOperation.NO_DATA;
+ duration: AlarmRuleValue;
+}
+
export interface BaseComplexFilterPredicate {
- type: FilterPredicateType.COMPLEX;
+ type: AlarmRuleFilterPredicateType.COMPLEX;
operation: ComplexOperation;
predicates: Array;
}
@@ -157,3 +165,73 @@ export interface AlarmRuleFilterConfig {
entityType?: EntityType;
entities?: Array;
}
+
+export enum AlarmRuleFilterPredicateType {
+ STRING = 'STRING',
+ NUMERIC = 'NUMERIC',
+ BOOLEAN = 'BOOLEAN',
+ COMPLEX = 'COMPLEX',
+ NO_DATA = 'NO_DATA'
+}
+
+export enum AlarmRuleStringOperation {
+ EQUAL = 'EQUAL',
+ NOT_EQUAL = 'NOT_EQUAL',
+ NO_DATA = 'NO_DATA',
+ STARTS_WITH = 'STARTS_WITH',
+ ENDS_WITH = 'ENDS_WITH',
+ CONTAINS = 'CONTAINS',
+ NOT_CONTAINS = 'NOT_CONTAINS',
+ IN = 'IN',
+ NOT_IN = 'NOT_IN',
+}
+
+export const alarmRuleStringOperationTranslationMap = new Map(
+ [
+ [AlarmRuleStringOperation.EQUAL, 'filter.operation.equal'],
+ [AlarmRuleStringOperation.NOT_EQUAL, 'filter.operation.not-equal'],
+ [AlarmRuleStringOperation.STARTS_WITH, 'filter.operation.starts-with'],
+ [AlarmRuleStringOperation.ENDS_WITH, 'filter.operation.ends-with'],
+ [AlarmRuleStringOperation.CONTAINS, 'filter.operation.contains'],
+ [AlarmRuleStringOperation.NOT_CONTAINS, 'filter.operation.not-contains'],
+ [AlarmRuleStringOperation.IN, 'filter.operation.in'],
+ [AlarmRuleStringOperation.NOT_IN, 'filter.operation.not-in'],
+ [AlarmRuleStringOperation.NO_DATA, 'alarm-rule.missing-for']
+ ]
+);
+
+export enum AlarmRuleNumericOperation {
+ EQUAL = 'EQUAL',
+ NOT_EQUAL = 'NOT_EQUAL',
+ NO_DATA = 'NO_DATA',
+ GREATER = 'GREATER',
+ LESS = 'LESS',
+ GREATER_OR_EQUAL = 'GREATER_OR_EQUAL',
+ LESS_OR_EQUAL = 'LESS_OR_EQUAL'
+}
+
+export const alarmRuleNumericOperationTranslationMap = new Map(
+ [
+ [AlarmRuleNumericOperation.EQUAL, 'filter.operation.equal'],
+ [AlarmRuleNumericOperation.NOT_EQUAL, 'filter.operation.not-equal'],
+ [AlarmRuleNumericOperation.GREATER, 'filter.operation.greater'],
+ [AlarmRuleNumericOperation.LESS, 'filter.operation.less'],
+ [AlarmRuleNumericOperation.GREATER_OR_EQUAL, 'filter.operation.greater-or-equal'],
+ [AlarmRuleNumericOperation.LESS_OR_EQUAL, 'filter.operation.less-or-equal'],
+ [AlarmRuleNumericOperation.NO_DATA, 'alarm-rule.missing-for']
+ ]
+);
+
+export enum AlarmRuleBooleanOperation {
+ EQUAL = 'EQUAL',
+ NOT_EQUAL = 'NOT_EQUAL',
+ NO_DATA = 'NO_DATA',
+}
+
+export const alarmRuleBooleanOperationTranslationMap = new Map(
+ [
+ [AlarmRuleBooleanOperation.EQUAL, 'filter.operation.equal'],
+ [AlarmRuleBooleanOperation.NOT_EQUAL, 'filter.operation.not-equal'],
+ [AlarmRuleBooleanOperation.NO_DATA, 'alarm-rule.missing-for']
+ ]
+);
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json
index 538b3a8352..1899802715 100644
--- a/ui-ngx/src/assets/locale/locale.constant-en_US.json
+++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json
@@ -1466,6 +1466,7 @@
"enter-alarm-rule-condition-prompt": "Please add alarm rule condition",
"edit-alarm-rule-condition": "Edit alarm rule condition",
"condition-type": "Condition type",
+ "condition-type-hint": "\"Duration\" and \"Repeating\" options are not available when the \"Missing for\" operation is used in the filter.",
"select-alarm-severity": "Select alarm severity",
"add-create-alarm-rule-prompt": "Please add create alarm rule",
"add-create-alarm-rule": "Add create condition",
@@ -1497,7 +1498,12 @@
"no-alarm-rule-types-matching": "No alarm rule types matching '{{entitySubtype}}' were found.",
"alarm-rule-type-list-empty": "No alarm rule types selected.",
"alarm-rule-type-list": "Alarm rule type list",
- "alarm-rule-entity-list": "Entity list"
+ "alarm-rule-entity-list": "Entity list",
+ "missing-for": "missing for",
+ "time-unit": "Unit",
+ "value-required": "Value is required.",
+ "min-value": "Value must be 0 or greater.",
+ "argument-in-use": "Argument is used as general argument."
},
"ai-models": {
"ai-models": "AI models",