Commit 034af204f02aedbf5440ed4fe34b2ccddc88c746

Authored by Vladyslav_Prykhodko
1 parent 5d30243e

UI: Added key name autocomplete in alarms rules to device profile

Showing 20 changed files with 188 additions and 45 deletions
@@ -69,4 +69,20 @@ export class DeviceProfileService { @@ -69,4 +69,20 @@ export class DeviceProfileService {
69 return this.http.get<PageData<DeviceProfileInfo>>(url, defaultHttpOptionsFromConfig(config)); 69 return this.http.get<PageData<DeviceProfileInfo>>(url, defaultHttpOptionsFromConfig(config));
70 } 70 }
71 71
  72 + public getDeviceProfileDevicesAttributesKeys(deviceProfileId?: string, config?: RequestConfig): Observable<Array<string>> {
  73 + let url = `/api/deviceProfile/devices/keys/attributes`;
  74 + if (isDefinedAndNotNull(deviceProfileId)) {
  75 + url += `?deviceProfileId=${deviceProfileId}`;
  76 + }
  77 + return this.http.get<Array<string>>(url, defaultHttpOptionsFromConfig(config));
  78 + }
  79 +
  80 + public getDeviceProfileDevicesTimeseriesKeys(deviceProfileId?: string, config?: RequestConfig): Observable<Array<string>> {
  81 + let url = `/api/deviceProfile/devices/keys/timeseries`;
  82 + if (isDefinedAndNotNull(deviceProfileId)) {
  83 + url += `?deviceProfileId=${deviceProfileId}`;
  84 + }
  85 + return this.http.get<Array<string>>(url, defaultHttpOptionsFromConfig(config));
  86 + }
  87 +
72 } 88 }
@@ -40,11 +40,19 @@ @@ -40,11 +40,19 @@
40 <mat-form-field fxFlex="60" class="mat-block"> 40 <mat-form-field fxFlex="60" class="mat-block">
41 <mat-label translate>filter.key-name</mat-label> 41 <mat-label translate>filter.key-name</mat-label>
42 <input matInput required formControlName="key" 42 <input matInput required formControlName="key"
43 - [matAutocomplete]="auto"  
44 - [matAutocompleteDisabled]="keyFilterFormGroup.get('key.type').value !== entityField">  
45 - <mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">  
46 - <mat-option *ngFor="let option of filteredEntityFields | async" [value]="option">  
47 - {{option}} 43 + #keyNameInput
  44 + (focusin)="onFocus()"
  45 + [matAutocomplete]="keyName"
  46 + [matAutocompleteDisabled]="!showAutocomplete">
  47 + <button *ngIf="keyFilterFormGroup.get('key.key').value && showAutocomplete"
  48 + type="button"
  49 + matSuffix mat-button mat-icon-button aria-label="Clear"
  50 + (click)="clear()">
  51 + <mat-icon class="material-icons">close</mat-icon>
  52 + </button>
  53 + <mat-autocomplete autoActiveFirstOption #keyName="matAutocomplete">
  54 + <mat-option *ngFor="let keyName of filteredKeysName | async" [value]="keyName">
  55 + <span [innerHTML]="keyName | highlight:searchText"></span>
48 </mat-option> 56 </mat-option>
49 </mat-autocomplete> 57 </mat-autocomplete>
50 <mat-error *ngIf="keyFilterFormGroup.get('key.key').hasError('required')"> 58 <mat-error *ngIf="keyFilterFormGroup.get('key.key').hasError('required')">
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; 17 +import { Component, ElementRef, Inject, OnDestroy, OnInit, SkipSelf, ViewChild } from '@angular/core';
18 import { ErrorStateMatcher } from '@angular/material/core'; 18 import { ErrorStateMatcher } from '@angular/material/core';
19 import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; 19 import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
20 import { Store } from '@ngrx/store'; 20 import { Store } from '@ngrx/store';
@@ -32,9 +32,12 @@ import { @@ -32,9 +32,12 @@ import {
32 } from '@shared/models/query/query.models'; 32 } from '@shared/models/query/query.models';
33 import { DialogService } from '@core/services/dialog.service'; 33 import { DialogService } from '@core/services/dialog.service';
34 import { TranslateService } from '@ngx-translate/core'; 34 import { TranslateService } from '@ngx-translate/core';
35 -import { EntityField, entityFields } from '@shared/models/entity.models';  
36 -import { Observable } from 'rxjs';  
37 -import { filter, map, startWith } from 'rxjs/operators'; 35 +import { entityFields } from '@shared/models/entity.models';
  36 +import { Observable, of, Subject } from 'rxjs';
  37 +import { filter, map, mergeMap, publishReplay, refCount, startWith, takeUntil } from 'rxjs/operators';
  38 +import { isDefined } from '@core/utils';
  39 +import { EntityId } from '@shared/models/id/entity-id';
  40 +import { DeviceProfileService } from '@core/http/device-profile.service';
38 41
39 export interface KeyFilterDialogData { 42 export interface KeyFilterDialogData {
40 keyFilter: KeyFilterInfo; 43 keyFilter: KeyFilterInfo;
@@ -43,6 +46,7 @@ export interface KeyFilterDialogData { @@ -43,6 +46,7 @@ export interface KeyFilterDialogData {
43 allowUserDynamicSource: boolean; 46 allowUserDynamicSource: boolean;
44 readonly: boolean; 47 readonly: boolean;
45 telemetryKeysOnly: boolean; 48 telemetryKeysOnly: boolean;
  49 + entityId?: EntityId;
46 } 50 }
47 51
48 @Component({ 52 @Component({
@@ -53,7 +57,13 @@ export interface KeyFilterDialogData { @@ -53,7 +57,13 @@ export interface KeyFilterDialogData {
53 }) 57 })
54 export class KeyFilterDialogComponent extends 58 export class KeyFilterDialogComponent extends
55 DialogComponent<KeyFilterDialogComponent, KeyFilterInfo> 59 DialogComponent<KeyFilterDialogComponent, KeyFilterInfo>
56 - implements OnInit, ErrorStateMatcher { 60 + implements OnInit, OnDestroy, ErrorStateMatcher {
  61 +
  62 + @ViewChild('keyNameInput', {static: true}) private keyNameInput: ElementRef;
  63 +
  64 + private dirty = false;
  65 + private entityKeysName: Observable<Array<string>>;
  66 + private destroy$ = new Subject();
57 67
58 keyFilterFormGroup: FormGroup; 68 keyFilterFormGroup: FormGroup;
59 69
@@ -72,19 +82,18 @@ export class KeyFilterDialogComponent extends @@ -72,19 +82,18 @@ export class KeyFilterDialogComponent extends
72 82
73 submitted = false; 83 submitted = false;
74 84
75 - entityFields: { [fieldName: string]: EntityField };  
76 -  
77 - entityFieldsList: string[]; 85 + showAutocomplete = false;
78 86
79 - readonly entityField = EntityKeyType.ENTITY_FIELD; 87 + filteredKeysName: Observable<Array<string>>;
80 88
81 - filteredEntityFields: Observable<string[]>; 89 + searchText = '';
82 90
83 constructor(protected store: Store<AppState>, 91 constructor(protected store: Store<AppState>,
84 protected router: Router, 92 protected router: Router,
85 @Inject(MAT_DIALOG_DATA) public data: KeyFilterDialogData, 93 @Inject(MAT_DIALOG_DATA) public data: KeyFilterDialogData,
86 @SkipSelf() private errorStateMatcher: ErrorStateMatcher, 94 @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
87 public dialogRef: MatDialogRef<KeyFilterDialogComponent, KeyFilterInfo>, 95 public dialogRef: MatDialogRef<KeyFilterDialogComponent, KeyFilterInfo>,
  96 + private deviceProfileService: DeviceProfileService,
88 private dialogs: DialogService, 97 private dialogs: DialogService,
89 private translate: TranslateService, 98 private translate: TranslateService,
90 private fb: FormBuilder) { 99 private fb: FormBuilder) {
@@ -104,7 +113,9 @@ export class KeyFilterDialogComponent extends @@ -104,7 +113,9 @@ export class KeyFilterDialogComponent extends
104 ); 113 );
105 114
106 if (!this.data.readonly) { 115 if (!this.data.readonly) {
107 - this.keyFilterFormGroup.get('valueType').valueChanges.subscribe((valueType: EntityKeyValueType) => { 116 + this.keyFilterFormGroup.get('valueType').valueChanges.pipe(
  117 + takeUntil(this.destroy$)
  118 + ).subscribe((valueType: EntityKeyValueType) => {
108 const prevValue: EntityKeyValueType = this.keyFilterFormGroup.value.valueType; 119 const prevValue: EntityKeyValueType = this.keyFilterFormGroup.value.valueType;
109 const predicates: KeyFilterPredicate[] = this.keyFilterFormGroup.get('predicates').value; 120 const predicates: KeyFilterPredicate[] = this.keyFilterFormGroup.get('predicates').value;
110 if (prevValue && prevValue !== valueType && predicates && predicates.length) { 121 if (prevValue && prevValue !== valueType && predicates && predicates.length) {
@@ -121,11 +132,26 @@ export class KeyFilterDialogComponent extends @@ -121,11 +132,26 @@ export class KeyFilterDialogComponent extends
121 } 132 }
122 }); 133 });
123 134
  135 + this.keyFilterFormGroup.get('key.type').valueChanges.pipe(
  136 + startWith(this.data.keyFilter.key.type),
  137 + takeUntil(this.destroy$)
  138 + ).subscribe((type: EntityKeyType) => {
  139 + if (type === EntityKeyType.ENTITY_FIELD || isDefined(this.data.entityId)) {
  140 + this.entityKeysName = null;
  141 + this.dirty = false;
  142 + this.showAutocomplete = true;
  143 + } else {
  144 + this.showAutocomplete = false;
  145 + }
  146 + });
  147 +
124 this.keyFilterFormGroup.get('key.key').valueChanges.pipe( 148 this.keyFilterFormGroup.get('key.key').valueChanges.pipe(
125 - filter((keyName) => this.keyFilterFormGroup.get('key.type').value === this.entityField && this.entityFields.hasOwnProperty(keyName)) 149 + filter((keyName) =>
  150 + this.keyFilterFormGroup.get('key.type').value === EntityKeyType.ENTITY_FIELD && entityFields.hasOwnProperty(keyName)),
  151 + takeUntil(this.destroy$)
126 ).subscribe((keyName: string) => { 152 ).subscribe((keyName: string) => {
127 const prevValueType: EntityKeyValueType = this.keyFilterFormGroup.value.valueType; 153 const prevValueType: EntityKeyValueType = this.keyFilterFormGroup.value.valueType;
128 - const newValueType = this.entityFields[keyName]?.time ? EntityKeyValueType.DATE_TIME : EntityKeyValueType.STRING; 154 + const newValueType = entityFields[keyName]?.time ? EntityKeyValueType.DATE_TIME : EntityKeyValueType.STRING;
129 if (prevValueType !== newValueType) { 155 if (prevValueType !== newValueType) {
130 this.keyFilterFormGroup.get('valueType').patchValue(newValueType, {emitEvent: false}); 156 this.keyFilterFormGroup.get('valueType').patchValue(newValueType, {emitEvent: false});
131 } 157 }
@@ -133,18 +159,20 @@ export class KeyFilterDialogComponent extends @@ -133,18 +159,20 @@ export class KeyFilterDialogComponent extends
133 } else { 159 } else {
134 this.keyFilterFormGroup.disable({emitEvent: false}); 160 this.keyFilterFormGroup.disable({emitEvent: false});
135 } 161 }
  162 + }
136 163
137 - this.entityFields = entityFields;  
138 - this.entityFieldsList = Object.values(entityFields).map(entityField => entityField.keyName).sort(); 164 + ngOnInit() {
  165 + this.filteredKeysName = this.keyFilterFormGroup.get('key.key').valueChanges
  166 + .pipe(
  167 + map(value => value ? value : ''),
  168 + mergeMap(name => this.fetchEntityName(name))
  169 + );
139 } 170 }
140 171
141 - ngOnInit(): void {  
142 - this.filteredEntityFields = this.keyFilterFormGroup.get('key.key').valueChanges.pipe(  
143 - startWith(''),  
144 - map(value => {  
145 - return this.entityFieldsList.filter(option => option.startsWith(value));  
146 - })  
147 - ); 172 + ngOnDestroy() {
  173 + super.ngOnDestroy();
  174 + this.destroy$.next();
  175 + this.destroy$.complete();
148 } 176 }
149 177
150 isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { 178 isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
@@ -157,6 +185,21 @@ export class KeyFilterDialogComponent extends @@ -157,6 +185,21 @@ export class KeyFilterDialogComponent extends
157 this.dialogRef.close(null); 185 this.dialogRef.close(null);
158 } 186 }
159 187
  188 + clear() {
  189 + this.keyFilterFormGroup.get('key.key').patchValue('', {emitEvent: true});
  190 + setTimeout(() => {
  191 + this.keyNameInput.nativeElement.blur();
  192 + this.keyNameInput.nativeElement.focus();
  193 + }, 0);
  194 + }
  195 +
  196 + onFocus() {
  197 + if (!this.dirty && this.showAutocomplete) {
  198 + this.keyFilterFormGroup.get('key.key').updateValueAndValidity({onlySelf: true, emitEvent: true});
  199 + this.dirty = true;
  200 + }
  201 + }
  202 +
160 save(): void { 203 save(): void {
161 this.submitted = true; 204 this.submitted = true;
162 if (this.keyFilterFormGroup.valid) { 205 if (this.keyFilterFormGroup.valid) {
@@ -164,4 +207,41 @@ export class KeyFilterDialogComponent extends @@ -164,4 +207,41 @@ export class KeyFilterDialogComponent extends
164 this.dialogRef.close(keyFilter); 207 this.dialogRef.close(keyFilter);
165 } 208 }
166 } 209 }
  210 +
  211 + private fetchEntityName(searchText?: string): Observable<Array<string>> {
  212 + this.searchText = searchText;
  213 + return this.getEntityKeys().pipe(
  214 + map(keys => searchText ? keys.filter(key => key.toUpperCase().startsWith(searchText.toUpperCase())) : keys)
  215 + );
  216 + }
  217 +
  218 + private getEntityKeys(): Observable<Array<string>> {
  219 + if (!this.entityKeysName) {
  220 + let keyNameObservable: Observable<Array<string>>;
  221 + switch (this.keyFilterFormGroup.get('key.type').value) {
  222 + case EntityKeyType.ENTITY_FIELD:
  223 + keyNameObservable = of(Object.values(entityFields).map(entityField => entityField.keyName).sort());
  224 + break;
  225 + case EntityKeyType.ATTRIBUTE:
  226 + keyNameObservable = this.deviceProfileService.getDeviceProfileDevicesAttributesKeys(
  227 + this.data.entityId?.id,
  228 + {ignoreLoading: true}
  229 + );
  230 + break;
  231 + case EntityKeyType.TIME_SERIES:
  232 + keyNameObservable = this.deviceProfileService.getDeviceProfileDevicesTimeseriesKeys(
  233 + this.data.entityId?.id,
  234 + {ignoreLoading: true}
  235 + );
  236 + break;
  237 + default:
  238 + keyNameObservable = of([]);
  239 + }
  240 + this.entityKeysName = keyNameObservable.pipe(
  241 + publishReplay(1),
  242 + refCount()
  243 + );
  244 + }
  245 + return this.entityKeysName;
  246 + }
167 } 247 }
@@ -19,7 +19,8 @@ import { @@ -19,7 +19,8 @@ import {
19 AbstractControl, 19 AbstractControl,
20 ControlValueAccessor, 20 ControlValueAccessor,
21 FormArray, 21 FormArray,
22 - FormBuilder, FormControl, 22 + FormBuilder,
  23 + FormControl,
23 FormGroup, 24 FormGroup,
24 NG_VALUE_ACCESSOR, 25 NG_VALUE_ACCESSOR,
25 Validators 26 Validators
@@ -28,12 +29,13 @@ import { Observable, Subscription } from 'rxjs'; @@ -28,12 +29,13 @@ import { Observable, Subscription } from 'rxjs';
28 import { 29 import {
29 EntityKeyType, 30 EntityKeyType,
30 entityKeyTypeTranslationMap, 31 entityKeyTypeTranslationMap,
31 - KeyFilter,  
32 - KeyFilterInfo, keyFilterInfosToKeyFilters 32 + KeyFilterInfo,
  33 + keyFilterInfosToKeyFilters
33 } from '@shared/models/query/query.models'; 34 } from '@shared/models/query/query.models';
34 import { MatDialog } from '@angular/material/dialog'; 35 import { MatDialog } from '@angular/material/dialog';
35 import { deepClone } from '@core/utils'; 36 import { deepClone } from '@core/utils';
36 import { KeyFilterDialogComponent, KeyFilterDialogData } from '@home/components/filter/key-filter-dialog.component'; 37 import { KeyFilterDialogComponent, KeyFilterDialogData } from '@home/components/filter/key-filter-dialog.component';
  38 +import { EntityId } from '@shared/models/id/entity-id';
37 39
38 @Component({ 40 @Component({
39 selector: 'tb-key-filter-list', 41 selector: 'tb-key-filter-list',
@@ -57,6 +59,8 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { @@ -57,6 +59,8 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
57 59
58 @Input() telemetryKeysOnly = false; 60 @Input() telemetryKeysOnly = false;
59 61
  62 + @Input() entityId: EntityId;
  63 +
60 keyFilterListFormGroup: FormGroup; 64 keyFilterListFormGroup: FormGroup;
61 65
62 entityKeyTypeTranslations = entityKeyTypeTranslationMap; 66 entityKeyTypeTranslations = entityKeyTypeTranslationMap;
@@ -170,7 +174,8 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { @@ -170,7 +174,8 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
170 readonly: this.disabled, 174 readonly: this.disabled,
171 displayUserParameters: this.displayUserParameters, 175 displayUserParameters: this.displayUserParameters,
172 allowUserDynamicSource: this.allowUserDynamicSource, 176 allowUserDynamicSource: this.allowUserDynamicSource,
173 - telemetryKeysOnly: this.telemetryKeysOnly 177 + telemetryKeysOnly: this.telemetryKeysOnly,
  178 + entityId: this.entityId
174 } 179 }
175 }).afterClosed(); 180 }).afterClosed();
176 } 181 }
@@ -96,7 +96,8 @@ @@ -96,7 +96,8 @@
96 {count: alarmRulesFormGroup.get('alarms').value ? 96 {count: alarmRulesFormGroup.get('alarms').value ?
97 alarmRulesFormGroup.get('alarms').value.length : 0} }}</ng-template> 97 alarmRulesFormGroup.get('alarms').value.length : 0} }}</ng-template>
98 <tb-device-profile-alarms 98 <tb-device-profile-alarms
99 - formControlName="alarms"> 99 + formControlName="alarms"
  100 + [deviceProfileId]="null">
100 </tb-device-profile-alarms> 101 </tb-device-profile-alarms>
101 </form> 102 </form>
102 </mat-step> 103 </mat-step>
@@ -34,6 +34,7 @@ @@ -34,6 +34,7 @@
34 [displayUserParameters]="false" 34 [displayUserParameters]="false"
35 [allowUserDynamicSource]="false" 35 [allowUserDynamicSource]="false"
36 [telemetryKeysOnly]="true" 36 [telemetryKeysOnly]="true"
  37 + [entityId]="entityId"
37 formControlName="keyFilters"> 38 formControlName="keyFilters">
38 </tb-key-filter-list> 39 </tb-key-filter-list>
39 <section formGroupName="spec" class="row"> 40 <section formGroupName="spec" class="row">
@@ -22,15 +22,16 @@ import { AppState } from '@core/core.state'; @@ -22,15 +22,16 @@ import { AppState } from '@core/core.state';
22 import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; 22 import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
23 import { Router } from '@angular/router'; 23 import { Router } from '@angular/router';
24 import { DialogComponent } from '@app/shared/components/dialog.component'; 24 import { DialogComponent } from '@app/shared/components/dialog.component';
25 -import { UtilsService } from '@core/services/utils.service';  
26 import { TranslateService } from '@ngx-translate/core'; 25 import { TranslateService } from '@ngx-translate/core';
27 -import { KeyFilter, keyFilterInfosToKeyFilters, keyFiltersToKeyFilterInfos } from '@shared/models/query/query.models'; 26 +import { keyFilterInfosToKeyFilters, keyFiltersToKeyFilterInfos } from '@shared/models/query/query.models';
28 import { AlarmCondition, AlarmConditionType, AlarmConditionTypeTranslationMap } from '@shared/models/device.models'; 27 import { AlarmCondition, AlarmConditionType, AlarmConditionTypeTranslationMap } from '@shared/models/device.models';
29 import { TimeUnit, timeUnitTranslationMap } from '@shared/models/time/time.models'; 28 import { TimeUnit, timeUnitTranslationMap } from '@shared/models/time/time.models';
  29 +import { EntityId } from '@shared/models/id/entity-id';
30 30
31 export interface AlarmRuleConditionDialogData { 31 export interface AlarmRuleConditionDialogData {
32 readonly: boolean; 32 readonly: boolean;
33 condition: AlarmCondition; 33 condition: AlarmCondition;
  34 + entityId?: EntityId;
34 } 35 }
35 36
36 @Component({ 37 @Component({
@@ -50,6 +51,7 @@ export class AlarmRuleConditionDialogComponent extends DialogComponent<AlarmRule @@ -50,6 +51,7 @@ export class AlarmRuleConditionDialogComponent extends DialogComponent<AlarmRule
50 51
51 readonly = this.data.readonly; 52 readonly = this.data.readonly;
52 condition = this.data.condition; 53 condition = this.data.condition;
  54 + entityId = this.data.entityId;
53 55
54 conditionFormGroup: FormGroup; 56 conditionFormGroup: FormGroup;
55 57
@@ -61,7 +63,6 @@ export class AlarmRuleConditionDialogComponent extends DialogComponent<AlarmRule @@ -61,7 +63,6 @@ export class AlarmRuleConditionDialogComponent extends DialogComponent<AlarmRule
61 @SkipSelf() private errorStateMatcher: ErrorStateMatcher, 63 @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
62 public dialogRef: MatDialogRef<AlarmRuleConditionDialogComponent, AlarmCondition>, 64 public dialogRef: MatDialogRef<AlarmRuleConditionDialogComponent, AlarmCondition>,
63 private fb: FormBuilder, 65 private fb: FormBuilder,
64 - private utils: UtilsService,  
65 public translate: TranslateService) { 66 public translate: TranslateService) {
66 super(store, router, dialogRef); 67 super(store, router, dialogRef);
67 68
@@ -33,6 +33,7 @@ import { @@ -33,6 +33,7 @@ import {
33 AlarmRuleConditionDialogData 33 AlarmRuleConditionDialogData
34 } from '@home/components/profile/alarm/alarm-rule-condition-dialog.component'; 34 } from '@home/components/profile/alarm/alarm-rule-condition-dialog.component';
35 import { TimeUnit } from '@shared/models/time/time.models'; 35 import { TimeUnit } from '@shared/models/time/time.models';
  36 +import { EntityId } from '@shared/models/id/entity-id';
36 37
37 @Component({ 38 @Component({
38 selector: 'tb-alarm-rule-condition', 39 selector: 'tb-alarm-rule-condition',
@@ -56,6 +57,9 @@ export class AlarmRuleConditionComponent implements ControlValueAccessor, OnInit @@ -56,6 +57,9 @@ export class AlarmRuleConditionComponent implements ControlValueAccessor, OnInit
56 @Input() 57 @Input()
57 disabled: boolean; 58 disabled: boolean;
58 59
  60 + @Input()
  61 + deviceProfileId: EntityId;
  62 +
59 alarmRuleConditionFormGroup: FormGroup; 63 alarmRuleConditionFormGroup: FormGroup;
60 64
61 specText = ''; 65 specText = '';
@@ -123,7 +127,8 @@ export class AlarmRuleConditionComponent implements ControlValueAccessor, OnInit @@ -123,7 +127,8 @@ export class AlarmRuleConditionComponent implements ControlValueAccessor, OnInit
123 panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], 127 panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
124 data: { 128 data: {
125 readonly: this.disabled, 129 readonly: this.disabled,
126 - condition: this.disabled ? this.modelValue : deepClone(this.modelValue) 130 + condition: this.disabled ? this.modelValue : deepClone(this.modelValue),
  131 + entityId: this.deviceProfileId
127 } 132 }
128 }).afterClosed().subscribe((result) => { 133 }).afterClosed().subscribe((result) => {
129 if (result) { 134 if (result) {
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 16
17 --> 17 -->
18 <div fxLayout="column" [formGroup]="alarmRuleFormGroup"> 18 <div fxLayout="column" [formGroup]="alarmRuleFormGroup">
19 - <tb-alarm-rule-condition formControlName="condition"> 19 + <tb-alarm-rule-condition formControlName="condition" [deviceProfileId]="deviceProfileId">
20 </tb-alarm-rule-condition> 20 </tb-alarm-rule-condition>
21 <tb-alarm-schedule-info formControlName="schedule"> 21 <tb-alarm-schedule-info formControlName="schedule">
22 </tb-alarm-schedule-info> 22 </tb-alarm-schedule-info>
@@ -33,6 +33,7 @@ import { @@ -33,6 +33,7 @@ import {
33 EditAlarmDetailsDialogComponent, 33 EditAlarmDetailsDialogComponent,
34 EditAlarmDetailsDialogData 34 EditAlarmDetailsDialogData
35 } from '@home/components/profile/alarm/edit-alarm-details-dialog.component'; 35 } from '@home/components/profile/alarm/edit-alarm-details-dialog.component';
  36 +import { EntityId } from '@shared/models/id/entity-id';
36 37
37 @Component({ 38 @Component({
38 selector: 'tb-alarm-rule', 39 selector: 'tb-alarm-rule',
@@ -65,6 +66,9 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat @@ -65,6 +66,9 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat
65 this.requiredValue = coerceBooleanProperty(value); 66 this.requiredValue = coerceBooleanProperty(value);
66 } 67 }
67 68
  69 + @Input()
  70 + deviceProfileId: EntityId;
  71 +
68 private modelValue: AlarmRule; 72 private modelValue: AlarmRule;
69 73
70 alarmRuleFormGroup: FormGroup; 74 alarmRuleFormGroup: FormGroup;
@@ -35,7 +35,7 @@ @@ -35,7 +35,7 @@
35 </mat-error> 35 </mat-error>
36 </mat-form-field> 36 </mat-form-field>
37 <mat-divider vertical></mat-divider> 37 <mat-divider vertical></mat-divider>
38 - <tb-alarm-rule formControlName="alarmRule" required fxFlex> 38 + <tb-alarm-rule formControlName="alarmRule" [deviceProfileId]="deviceProfileId" required fxFlex>
39 </tb-alarm-rule> 39 </tb-alarm-rule>
40 </div> 40 </div>
41 <button *ngIf="!disabled" 41 <button *ngIf="!disabled"
@@ -30,7 +30,8 @@ import { @@ -30,7 +30,8 @@ import {
30 import { AlarmRule, alarmRuleValidator } from '@shared/models/device.models'; 30 import { AlarmRule, alarmRuleValidator } from '@shared/models/device.models';
31 import { MatDialog } from '@angular/material/dialog'; 31 import { MatDialog } from '@angular/material/dialog';
32 import { Subscription } from 'rxjs'; 32 import { Subscription } from 'rxjs';
33 -import { AlarmSeverity, alarmSeverityTranslations } from '../../../../../shared/models/alarm.models'; 33 +import { AlarmSeverity, alarmSeverityTranslations } from '@shared/models/alarm.models';
  34 +import { EntityId } from '@shared/models/id/entity-id';
34 35
35 @Component({ 36 @Component({
36 selector: 'tb-create-alarm-rules', 37 selector: 'tb-create-alarm-rules',
@@ -59,6 +60,9 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, @@ -59,6 +60,9 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit,
59 @Input() 60 @Input()
60 disabled: boolean; 61 disabled: boolean;
61 62
  63 + @Input()
  64 + deviceProfileId: EntityId;
  65 +
62 createAlarmRulesFormGroup: FormGroup; 66 createAlarmRulesFormGroup: FormGroup;
63 67
64 private usedSeverities: AlarmSeverity[] = []; 68 private usedSeverities: AlarmSeverity[] = [];
@@ -80,14 +80,16 @@ @@ -80,14 +80,16 @@
80 </mat-expansion-panel> 80 </mat-expansion-panel>
81 <div fxFlex fxLayout="column"> 81 <div fxFlex fxLayout="column">
82 <div translate class="tb-small" style="padding-bottom: 8px;">device-profile.create-alarm-rules</div> 82 <div translate class="tb-small" style="padding-bottom: 8px;">device-profile.create-alarm-rules</div>
83 - <tb-create-alarm-rules formControlName="createRules" style="padding-bottom: 16px;"> 83 + <tb-create-alarm-rules formControlName="createRules"
  84 + style="padding-bottom: 16px;"
  85 + [deviceProfileId]="deviceProfileId">
84 </tb-create-alarm-rules> 86 </tb-create-alarm-rules>
85 <div translate class="tb-small" style="padding-bottom: 8px;">device-profile.clear-alarm-rule</div> 87 <div translate class="tb-small" style="padding-bottom: 8px;">device-profile.clear-alarm-rule</div>
86 <div fxLayout="row" fxLayoutGap="8px;" fxLayoutAlign="start center" 88 <div fxLayout="row" fxLayoutGap="8px;" fxLayoutAlign="start center"
87 [fxShow]="alarmFormGroup.get('clearRule').value" 89 [fxShow]="alarmFormGroup.get('clearRule').value"
88 style="padding-bottom: 8px;"> 90 style="padding-bottom: 8px;">
89 <div class="clear-alarm-rule" fxFlex fxLayout="row"> 91 <div class="clear-alarm-rule" fxFlex fxLayout="row">
90 - <tb-alarm-rule formControlName="clearRule" fxFlex> 92 + <tb-alarm-rule formControlName="clearRule" fxFlex [deviceProfileId]="deviceProfileId">
91 </tb-alarm-rule> 93 </tb-alarm-rule>
92 </div> 94 </div>
93 <button *ngIf="!disabled" 95 <button *ngIf="!disabled"
@@ -29,6 +29,7 @@ import { AlarmRule, DeviceProfileAlarm, deviceProfileAlarmValidator } from '@sha @@ -29,6 +29,7 @@ import { AlarmRule, DeviceProfileAlarm, deviceProfileAlarmValidator } from '@sha
29 import { MatDialog } from '@angular/material/dialog'; 29 import { MatDialog } from '@angular/material/dialog';
30 import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; 30 import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
31 import { MatChipInputEvent } from '@angular/material/chips'; 31 import { MatChipInputEvent } from '@angular/material/chips';
  32 +import { EntityId } from '@shared/models/id/entity-id';
32 33
33 @Component({ 34 @Component({
34 selector: 'tb-device-profile-alarm', 35 selector: 'tb-device-profile-alarm',
@@ -60,6 +61,9 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit @@ -60,6 +61,9 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit
60 @Input() 61 @Input()
61 expanded = false; 62 expanded = false;
62 63
  64 + @Input()
  65 + deviceProfileId: EntityId;
  66 +
63 private modelValue: DeviceProfileAlarm; 67 private modelValue: DeviceProfileAlarm;
64 68
65 alarmFormGroup: FormGroup; 69 alarmFormGroup: FormGroup;
@@ -22,6 +22,7 @@ @@ -22,6 +22,7 @@
22 fxLayout="column" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}"> 22 fxLayout="column" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}">
23 <tb-device-profile-alarm [formControl]="alarmControl" 23 <tb-device-profile-alarm [formControl]="alarmControl"
24 [expanded]="$index === 0" 24 [expanded]="$index === 0"
  25 + [deviceProfileId]="deviceProfileId"
25 (removeAlarm)="removeAlarm($index)"> 26 (removeAlarm)="removeAlarm($index)">
26 </tb-device-profile-alarm> 27 </tb-device-profile-alarm>
27 </div> 28 </div>
@@ -34,6 +34,7 @@ import { DeviceProfileAlarm, deviceProfileAlarmValidator } from '@shared/models/ @@ -34,6 +34,7 @@ import { DeviceProfileAlarm, deviceProfileAlarmValidator } from '@shared/models/
34 import { guid } from '@core/utils'; 34 import { guid } from '@core/utils';
35 import { Subscription } from 'rxjs'; 35 import { Subscription } from 'rxjs';
36 import { MatDialog } from '@angular/material/dialog'; 36 import { MatDialog } from '@angular/material/dialog';
  37 +import { EntityId } from '@shared/models/id/entity-id';
37 38
38 @Component({ 39 @Component({
39 selector: 'tb-device-profile-alarms', 40 selector: 'tb-device-profile-alarms',
@@ -68,6 +69,9 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni @@ -68,6 +69,9 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni
68 @Input() 69 @Input()
69 disabled: boolean; 70 disabled: boolean;
70 71
  72 + @Input()
  73 + deviceProfileId: EntityId;
  74 +
71 private valueChangeSubscription: Subscription = null; 75 private valueChangeSubscription: Subscription = null;
72 76
73 private propagateChange = (v: any) => { }; 77 private propagateChange = (v: any) => { };
@@ -116,7 +116,8 @@ @@ -116,7 +116,8 @@
116 </mat-panel-title> 116 </mat-panel-title>
117 </mat-expansion-panel-header> 117 </mat-expansion-panel-header>
118 <tb-device-profile-alarms 118 <tb-device-profile-alarms
119 - formControlName="alarms"> 119 + formControlName="alarms"
  120 + [deviceProfileId]="deviceProfileId">
120 </tb-device-profile-alarms> 121 </tb-device-profile-alarms>
121 </mat-expansion-panel> 122 </mat-expansion-panel>
122 <mat-expansion-panel [expanded]="true"> 123 <mat-expansion-panel [expanded]="true">
@@ -39,6 +39,7 @@ import { @@ -39,6 +39,7 @@ import {
39 import { EntityType } from '@shared/models/entity-type.models'; 39 import { EntityType } from '@shared/models/entity-type.models';
40 import { RuleChainId } from '@shared/models/id/rule-chain-id'; 40 import { RuleChainId } from '@shared/models/id/rule-chain-id';
41 import { ServiceType } from '@shared/models/queue.models'; 41 import { ServiceType } from '@shared/models/queue.models';
  42 +import { EntityId } from '@shared/models/id/entity-id';
42 43
43 @Component({ 44 @Component({
44 selector: 'tb-device-profile', 45 selector: 'tb-device-profile',
@@ -66,6 +67,8 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { @@ -66,6 +67,8 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
66 67
67 serviceType = ServiceType.TB_RULE_ENGINE; 68 serviceType = ServiceType.TB_RULE_ENGINE;
68 69
  70 + deviceProfileId: EntityId;
  71 +
69 constructor(protected store: Store<AppState>, 72 constructor(protected store: Store<AppState>,
70 protected translate: TranslateService, 73 protected translate: TranslateService,
71 @Optional() @Inject('entity') protected entityValue: DeviceProfile, 74 @Optional() @Inject('entity') protected entityValue: DeviceProfile,
@@ -83,6 +86,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { @@ -83,6 +86,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
83 } 86 }
84 87
85 buildForm(entity: DeviceProfile): FormGroup { 88 buildForm(entity: DeviceProfile): FormGroup {
  89 + this.deviceProfileId = entity?.id ? entity.id : null;
86 this.displayProfileConfiguration = entity && entity.type && 90 this.displayProfileConfiguration = entity && entity.type &&
87 deviceProfileTypeConfigurationInfoMap.get(entity.type).hasProfileConfiguration; 91 deviceProfileTypeConfigurationInfoMap.get(entity.type).hasProfileConfiguration;
88 this.displayTransportConfiguration = entity && entity.transportType && 92 this.displayTransportConfiguration = entity && entity.transportType &&
@@ -157,6 +161,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { @@ -157,6 +161,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
157 } 161 }
158 162
159 updateForm(entity: DeviceProfile) { 163 updateForm(entity: DeviceProfile) {
  164 + this.deviceProfileId = entity.id;
160 this.displayProfileConfiguration = entity.type && 165 this.displayProfileConfiguration = entity.type &&
161 deviceProfileTypeConfigurationInfoMap.get(entity.type).hasProfileConfiguration; 166 deviceProfileTypeConfigurationInfoMap.get(entity.type).hasProfileConfiguration;
162 this.displayTransportConfiguration = entity.transportType && 167 this.displayTransportConfiguration = entity.transportType &&
@@ -76,7 +76,7 @@ @@ -76,7 +76,7 @@
76 [required]="!createProfile" 76 [required]="!createProfile"
77 [transportType]="deviceWizardFormGroup.get('transportType').value" 77 [transportType]="deviceWizardFormGroup.get('transportType').value"
78 formControlName="deviceProfileId" 78 formControlName="deviceProfileId"
79 - [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 0}" 79 + [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 0}"
80 (deviceProfileChanged)="$event?.transportType ? deviceWizardFormGroup.get('transportType').patchValue($event?.transportType) : {}" 80 (deviceProfileChanged)="$event?.transportType ? deviceWizardFormGroup.get('transportType').patchValue($event?.transportType) : {}"
81 [addNewProfile]="false" 81 [addNewProfile]="false"
82 [selectDefaultProfile]="true" 82 [selectDefaultProfile]="true"
@@ -133,7 +133,8 @@ @@ -133,7 +133,8 @@
133 {count: alarmRulesFormGroup.get('alarms').value ? 133 {count: alarmRulesFormGroup.get('alarms').value ?
134 alarmRulesFormGroup.get('alarms').value.length : 0} }}</ng-template> 134 alarmRulesFormGroup.get('alarms').value.length : 0} }}</ng-template>
135 <tb-device-profile-alarms 135 <tb-device-profile-alarms
136 - formControlName="alarms"> 136 + formControlName="alarms"
  137 + [deviceProfileId]="null">
137 </tb-device-profile-alarms> 138 </tb-device-profile-alarms>
138 </form> 139 </form>
139 </mat-step> 140 </mat-step>
@@ -46,7 +46,7 @@ @@ -46,7 +46,7 @@
46 }}" #alarmRules="matTab"> 46 }}" #alarmRules="matTab">
47 <div class="mat-padding" [formGroup]="detailsForm"> 47 <div class="mat-padding" [formGroup]="detailsForm">
48 <div formGroupName="profileData"> 48 <div formGroupName="profileData">
49 - <tb-device-profile-alarms formControlName="alarms"></tb-device-profile-alarms> 49 + <tb-device-profile-alarms formControlName="alarms" [deviceProfileId]="entity.id"></tb-device-profile-alarms>
50 </div> 50 </div>
51 </div> 51 </div>
52 </mat-tab> 52 </mat-tab>