Commit 034af204f02aedbf5440ed4fe34b2ccddc88c746
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> |