|
|
|
@ -14,11 +14,11 @@ |
|
|
|
/// limitations under the License.
|
|
|
|
///
|
|
|
|
|
|
|
|
import { Component, DestroyRef, Inject, ViewChild, ViewEncapsulation } from '@angular/core'; |
|
|
|
import { Component, DestroyRef, Inject, ViewEncapsulation } from '@angular/core'; |
|
|
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; |
|
|
|
import { Store } from '@ngrx/store'; |
|
|
|
import { AppState } from '@core/core.state'; |
|
|
|
import { FormGroup } from '@angular/forms'; |
|
|
|
import { FormControl, FormGroup, Validators } from '@angular/forms'; |
|
|
|
import { Router } from '@angular/router'; |
|
|
|
import { DialogComponent } from '@shared/components/dialog.component'; |
|
|
|
import { |
|
|
|
@ -41,16 +41,16 @@ import { |
|
|
|
AlarmRuleTestScriptFn |
|
|
|
} from "@shared/models/alarm-rule.models"; |
|
|
|
import { deepTrim } from "@core/utils"; |
|
|
|
import { combineLatest, Observable } from "rxjs"; |
|
|
|
import { debounceTime, startWith } from "rxjs/operators"; |
|
|
|
import { combineLatest, from, Observable, of } from 'rxjs'; |
|
|
|
import { catchError, debounceTime, map, mergeMap, startWith, switchMap, toArray } from 'rxjs/operators'; |
|
|
|
import { RelationTypes } from "@shared/models/relation.models"; |
|
|
|
import { StringItemsOption } from "@shared/components/string-items-list.component"; |
|
|
|
import { BaseData } from "@shared/models/base-data"; |
|
|
|
import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service'; |
|
|
|
import { EntitySelectComponent } from '@shared/components/entity/entity-select.component'; |
|
|
|
import { NULL_UUID } from '@shared/models/id/has-uuid'; |
|
|
|
import { AssetInfo } from '@shared/models/asset.models'; |
|
|
|
import { DeviceInfo } from '@shared/models/device.models'; |
|
|
|
import { DeviceProfileService } from '@core/http/device-profile.service'; |
|
|
|
import { AssetProfileService } from '@core/http/asset-profile.service'; |
|
|
|
import { EntityInfoData } from '@shared/models/entity.models'; |
|
|
|
import { TranslateService } from '@ngx-translate/core'; |
|
|
|
import { ActionNotificationShow } from '@core/notification/notification.actions'; |
|
|
|
|
|
|
|
export interface AlarmRuleDialogData { |
|
|
|
value?: CalculatedFieldAlarmRule; |
|
|
|
@ -73,8 +73,6 @@ export interface AlarmRuleDialogData { |
|
|
|
}) |
|
|
|
export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogComponent, CalculatedFieldAlarmRule> { |
|
|
|
|
|
|
|
@ViewChild('entitySelect') entitySelect!: EntitySelectComponent; |
|
|
|
|
|
|
|
fieldFormGroup: FormGroup ; |
|
|
|
|
|
|
|
additionalDebugActionConfig = this.data.value?.id ? { |
|
|
|
@ -97,11 +95,18 @@ export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogCom |
|
|
|
disabledArguments = false; |
|
|
|
isLoading = false; |
|
|
|
|
|
|
|
entityTypeControl = new FormControl<EntityType>(EntityType.DEVICE_PROFILE, { nonNullable: true, validators: Validators.required }); |
|
|
|
|
|
|
|
argsEntityId: EntityId | EntityId[]; |
|
|
|
|
|
|
|
constructor(protected store: Store<AppState>, |
|
|
|
protected router: Router, |
|
|
|
@Inject(MAT_DIALOG_DATA) public data: AlarmRuleDialogData, |
|
|
|
protected dialogRef: MatDialogRef<AlarmRuleDialogComponent, CalculatedFieldAlarmRule>, |
|
|
|
private alarmRulesService: AlarmRulesService, |
|
|
|
private deviceProfileService: DeviceProfileService, |
|
|
|
private assetProfileService: AssetProfileService, |
|
|
|
private translate: TranslateService, |
|
|
|
private destroyRef: DestroyRef, |
|
|
|
private cfFormService: CalculatedFieldFormService) { |
|
|
|
super(store, router, dialogRef); |
|
|
|
@ -120,6 +125,14 @@ export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogCom |
|
|
|
}); |
|
|
|
|
|
|
|
if (!this.data.entityId) { |
|
|
|
const copiedEntityType = this.data.value?.entityId?.entityType as EntityType; |
|
|
|
if (alarmRuleEntityTypeList.includes(copiedEntityType)) { |
|
|
|
this.entityTypeControl.setValue(copiedEntityType, { emitEvent: false }); |
|
|
|
} |
|
|
|
this.entityTypeControl.valueChanges.pipe( |
|
|
|
takeUntilDestroyed() |
|
|
|
).subscribe(() => this.fieldFormGroup.get('entityId')!.reset(null)); |
|
|
|
|
|
|
|
combineLatest([ |
|
|
|
this.fieldFormGroup.get('entityId')!.valueChanges.pipe(startWith(this.fieldFormGroup.get('entityId')!.value)), |
|
|
|
this.fieldFormGroup.get('name')!.valueChanges.pipe(startWith(this.fieldFormGroup.get('name')!.value)) |
|
|
|
@ -127,7 +140,9 @@ export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogCom |
|
|
|
debounceTime(50), |
|
|
|
takeUntilDestroyed() |
|
|
|
).subscribe(([entityId, name]) => { |
|
|
|
this.disabledArguments = !entityId || !name?.length; |
|
|
|
const hasEntity = Array.isArray(entityId) ? !!entityId.length : !!entityId; |
|
|
|
this.disabledArguments = !hasEntity || !name?.length; |
|
|
|
this.argsEntityId = Array.isArray(entityId) ? entityId.map(id => ({ entityType: this.entityTypeControl.value, id })) : entityId; |
|
|
|
const argsControl = this.fieldFormGroup.get('configuration.arguments')!; |
|
|
|
if (this.disabledArguments) { |
|
|
|
argsControl.disable({ emitEvent: false }); |
|
|
|
@ -174,24 +189,84 @@ export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogCom |
|
|
|
add(): void { |
|
|
|
if (this.fieldFormGroup.valid && Object.keys(this.arguments ?? {}).length > 0) { |
|
|
|
this.isLoading = true; |
|
|
|
const alarmRule = { entityId: this.data.entityId, ...(this.data.value ?? {}), ...this.fromGroupValue}; |
|
|
|
alarmRule.configuration.type = CalculatedFieldType.ALARM; |
|
|
|
|
|
|
|
this.alarmRulesService.saveAlarmRule(alarmRule) |
|
|
|
.pipe(takeUntilDestroyed(this.destroyRef)) |
|
|
|
.subscribe({ |
|
|
|
next: calculatedField => this.dialogRef.close(calculatedField), |
|
|
|
error: () => this.isLoading = false |
|
|
|
}); |
|
|
|
const value = this.fromGroupValue; |
|
|
|
value.configuration.type = CalculatedFieldType.ALARM; |
|
|
|
this.resolveEntityIds().pipe( |
|
|
|
switchMap(entityIds => |
|
|
|
entityIds.length |
|
|
|
? from(entityIds).pipe( |
|
|
|
mergeMap(entityId => |
|
|
|
this.alarmRulesService.saveAlarmRule({ ...(this.data.value ?? {}), ...value, entityId }).pipe( |
|
|
|
catchError(() => of(null as CalculatedFieldAlarmRule)) |
|
|
|
), 4), |
|
|
|
toArray() |
|
|
|
) |
|
|
|
: of([] as CalculatedFieldAlarmRule[])), |
|
|
|
takeUntilDestroyed(this.destroyRef) |
|
|
|
).subscribe((results: CalculatedFieldAlarmRule[]) => { |
|
|
|
const saved = results.filter(Boolean); |
|
|
|
const failedCount = results.length - saved.length; |
|
|
|
if (failedCount) { |
|
|
|
this.showNotification( |
|
|
|
this.translate.instant('alarm-rule.alarm-rules-creation-failed', { count: failedCount, total: results.length }), 'error'); |
|
|
|
} else if (saved.length > 1) { |
|
|
|
this.showNotification(this.translate.instant('alarm-rule.alarm-rules-created', { count: saved.length }), 'success'); |
|
|
|
} |
|
|
|
if (saved.length) { |
|
|
|
this.dialogRef.close(saved[0]); |
|
|
|
} else { |
|
|
|
this.isLoading = false; |
|
|
|
} |
|
|
|
}); |
|
|
|
} else { |
|
|
|
this.fieldFormGroup.get('name').markAsTouched(); |
|
|
|
this.entitySelect?.entityAutocompleteMarkAsTouched(); |
|
|
|
this.fieldFormGroup.get('entityId')?.markAsTouched(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private resolveEntityIds(): Observable<EntityId[]> { |
|
|
|
const value = this.fieldFormGroup.get('entityId').value; |
|
|
|
if (!Array.isArray(value)) { |
|
|
|
return of([value ?? this.data.entityId]); |
|
|
|
} |
|
|
|
switch (this.entityTypeControl.value) { |
|
|
|
case EntityType.DEVICE_PROFILE: |
|
|
|
return this.deviceProfileService.getDeviceProfileNames(false).pipe( |
|
|
|
map(profiles => this.toProfileEntityIds(profiles, value)) |
|
|
|
); |
|
|
|
case EntityType.ASSET_PROFILE: |
|
|
|
return this.assetProfileService.getAssetProfileNames(false).pipe( |
|
|
|
map(profiles => this.toProfileEntityIds(profiles, value)) |
|
|
|
); |
|
|
|
default: |
|
|
|
return of(value.map(id => ({ entityType: this.entityTypeControl.value, id }))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private toProfileEntityIds(profiles: EntityInfoData[], names: string[]): EntityId[] { |
|
|
|
const idByName = new Map(profiles.map(profile => [profile.name, profile.id])); |
|
|
|
const notFoundNames = names.filter(name => !idByName.has(name)); |
|
|
|
if (notFoundNames.length) { |
|
|
|
this.showNotification( |
|
|
|
this.translate.instant('alarm-rule.profiles-not-found', { names: notFoundNames.join(', ') }), 'error'); |
|
|
|
return []; |
|
|
|
} |
|
|
|
return names.map(name => idByName.get(name)); |
|
|
|
} |
|
|
|
|
|
|
|
private showNotification(message: string, type: 'success' | 'error'): void { |
|
|
|
this.store.dispatch(new ActionNotificationShow({ |
|
|
|
message, |
|
|
|
type, |
|
|
|
verticalPosition: 'top', |
|
|
|
horizontalPosition: 'left', |
|
|
|
duration: 5000 |
|
|
|
})); |
|
|
|
} |
|
|
|
|
|
|
|
private applyDialogData(): void { |
|
|
|
const { configuration = {}, type = CalculatedFieldType.ALARM, debugSettings = { failuresEnabled: true, allEnabled: true }, entityId = this.data.entityId, ...value } = this.data.value ?? {}; |
|
|
|
this.fieldFormGroup.patchValue({ configuration, type, debugSettings, entityId, ...value }, {emitEvent: false}); |
|
|
|
this.fieldFormGroup.patchValue({ configuration, type, debugSettings, entityId: this.data.entityId ? entityId : null, ...value }, {emitEvent: false}); |
|
|
|
} |
|
|
|
|
|
|
|
onTestScript(expression: string): Observable<string> { |
|
|
|
@ -230,14 +305,11 @@ export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogCom |
|
|
|
})); |
|
|
|
} |
|
|
|
|
|
|
|
changeEntity(entity: BaseData<EntityId>): void { |
|
|
|
this.entityName = entity.name; |
|
|
|
if (this.isAssignedToCustomer(entity as AssetInfo | DeviceInfo)) { |
|
|
|
this.ownerId = (entity as AssetInfo | DeviceInfo).customerId; |
|
|
|
} |
|
|
|
get isProfileEntityType(): boolean { |
|
|
|
return this.entityTypeControl.value === EntityType.DEVICE_PROFILE || this.entityTypeControl.value === EntityType.ASSET_PROFILE; |
|
|
|
} |
|
|
|
|
|
|
|
private isAssignedToCustomer(entity: AssetInfo | DeviceInfo): boolean { |
|
|
|
return entity && entity.customerId && entity.customerId.id !== NULL_UUID; |
|
|
|
get subtypeListEntityType(): EntityType { |
|
|
|
return this.entityTypeControl.value === EntityType.DEVICE_PROFILE ? EntityType.DEVICE : EntityType.ASSET; |
|
|
|
} |
|
|
|
} |
|
|
|
|