Commit 09b6b06abb19380e87f83241b5adc63436f88fa4
Committed by
Andrew Shvayka
1 parent
023915c2
Refactoring lwm2m device profile transport, configuration observe attributes/telemetry
Showing
19 changed files
with
814 additions
and
619 deletions
@@ -34,7 +34,7 @@ export interface Lwm2mAttributesDialogData { | @@ -34,7 +34,7 @@ export interface Lwm2mAttributesDialogData { | ||
34 | @Component({ | 34 | @Component({ |
35 | selector: 'tb-lwm2m-attributes-dialog', | 35 | selector: 'tb-lwm2m-attributes-dialog', |
36 | templateUrl: './lwm2m-attributes-dialog.component.html', | 36 | templateUrl: './lwm2m-attributes-dialog.component.html', |
37 | - styleUrls: ['./lwm2m-attributes.component.scss'], | 37 | + styleUrls: [], |
38 | providers: [{provide: ErrorStateMatcher, useExisting: Lwm2mAttributesDialogComponent}], | 38 | providers: [{provide: ErrorStateMatcher, useExisting: Lwm2mAttributesDialogComponent}], |
39 | }) | 39 | }) |
40 | export class Lwm2mAttributesDialogComponent | 40 | export class Lwm2mAttributesDialogComponent |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<div fxLayout="row" [fxHide]="disabled && isEmpty()" fxLayoutAlign="end center" matTooltip="{{ tooltipSetAttributesTelemetry | translate }}" matTooltipPosition="above"> | 18 | +<div [fxHide]="disabled && isEmpty()" matTooltip="{{ tooltipSetAttributesTelemetry | translate }}" matTooltipPosition="above"> |
19 | <button type="button" | 19 | <button type="button" |
20 | [disabled]="isDisableBtn()" | 20 | [disabled]="isDisableBtn()" |
21 | mat-button mat-icon-button | 21 | mat-button mat-icon-button |
@@ -14,29 +14,31 @@ | @@ -14,29 +14,31 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core'; | 17 | +import { Component, forwardRef, Input, OnDestroy } from '@angular/core'; |
18 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; | 18 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; |
19 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; | 19 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
20 | import { isEmpty, isUndefinedOrNull } from '@core/utils'; | 20 | import { isEmpty, isUndefinedOrNull } from '@core/utils'; |
21 | import { Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData } from './lwm2m-attributes-dialog.component'; | 21 | import { Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData } from './lwm2m-attributes-dialog.component'; |
22 | import { MatDialog } from '@angular/material/dialog'; | 22 | import { MatDialog } from '@angular/material/dialog'; |
23 | import { AttributesNameValueMap } from './lwm2m-profile-config.models'; | 23 | import { AttributesNameValueMap } from './lwm2m-profile-config.models'; |
24 | - | 24 | +import { Subject } from 'rxjs'; |
25 | +import { takeUntil } from 'rxjs/operators'; | ||
25 | 26 | ||
26 | @Component({ | 27 | @Component({ |
27 | selector: 'tb-profile-lwm2m-attributes', | 28 | selector: 'tb-profile-lwm2m-attributes', |
28 | templateUrl: './lwm2m-attributes.component.html', | 29 | templateUrl: './lwm2m-attributes.component.html', |
29 | - styleUrls: ['./lwm2m-attributes.component.scss'], | 30 | + styleUrls: [], |
30 | providers: [{ | 31 | providers: [{ |
31 | provide: NG_VALUE_ACCESSOR, | 32 | provide: NG_VALUE_ACCESSOR, |
32 | useExisting: forwardRef(() => Lwm2mAttributesComponent), | 33 | useExisting: forwardRef(() => Lwm2mAttributesComponent), |
33 | multi: true | 34 | multi: true |
34 | }] | 35 | }] |
35 | }) | 36 | }) |
36 | -export class Lwm2mAttributesComponent implements ControlValueAccessor { | ||
37 | - attributeLwm2mFormGroup: FormGroup; | 37 | +export class Lwm2mAttributesComponent implements ControlValueAccessor, OnDestroy { |
38 | + attributesFormGroup: FormGroup; | ||
38 | 39 | ||
39 | private requiredValue: boolean; | 40 | private requiredValue: boolean; |
41 | + private destroy$ = new Subject(); | ||
40 | 42 | ||
41 | @Input() | 43 | @Input() |
42 | isAttributeTelemetry: boolean; | 44 | isAttributeTelemetry: boolean; |
@@ -50,9 +52,6 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { | @@ -50,9 +52,6 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { | ||
50 | @Input() | 52 | @Input() |
51 | isResource = false; | 53 | isResource = false; |
52 | 54 | ||
53 | - @Output() | ||
54 | - updateAttributeLwm2m = new EventEmitter<any>(); | ||
55 | - | ||
56 | @Input() | 55 | @Input() |
57 | set required(value: boolean) { | 56 | set required(value: boolean) { |
58 | this.requiredValue = coerceBooleanProperty(value); | 57 | this.requiredValue = coerceBooleanProperty(value); |
@@ -62,7 +61,21 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { | @@ -62,7 +61,21 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { | ||
62 | } | 61 | } |
63 | 62 | ||
64 | constructor(private dialog: MatDialog, | 63 | constructor(private dialog: MatDialog, |
65 | - private fb: FormBuilder) {} | 64 | + private fb: FormBuilder) { |
65 | + this.attributesFormGroup = this.fb.group({ | ||
66 | + attributes: [{}] | ||
67 | + }); | ||
68 | + this.attributesFormGroup.get('attributes').valueChanges.pipe( | ||
69 | + takeUntil(this.destroy$) | ||
70 | + ).subscribe(attributes => { | ||
71 | + this.propagateChange(attributes); | ||
72 | + }); | ||
73 | + } | ||
74 | + | ||
75 | + ngOnDestroy() { | ||
76 | + this.destroy$.next(); | ||
77 | + this.destroy$.complete(); | ||
78 | + } | ||
66 | 79 | ||
67 | registerOnChange(fn: any): void { | 80 | registerOnChange(fn: any): void { |
68 | this.propagateChange = fn; | 81 | this.propagateChange = fn; |
@@ -74,24 +87,18 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { | @@ -74,24 +87,18 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { | ||
74 | setDisabledState(isDisabled: boolean): void { | 87 | setDisabledState(isDisabled: boolean): void { |
75 | this.disabled = isDisabled; | 88 | this.disabled = isDisabled; |
76 | if (isDisabled) { | 89 | if (isDisabled) { |
77 | - this.attributeLwm2mFormGroup.disable({emitEvent: false}); | 90 | + this.attributesFormGroup.disable({emitEvent: false}); |
78 | } else { | 91 | } else { |
79 | - this.attributeLwm2mFormGroup.enable({emitEvent: false}); | 92 | + this.attributesFormGroup.enable({emitEvent: false}); |
80 | } | 93 | } |
81 | } | 94 | } |
82 | 95 | ||
83 | - ngOnInit() { | ||
84 | - this.attributeLwm2mFormGroup = this.fb.group({ | ||
85 | - attributes: [{}] | ||
86 | - }); | ||
87 | - } | ||
88 | - | ||
89 | writeValue(value: AttributesNameValueMap | null) { | 96 | writeValue(value: AttributesNameValueMap | null) { |
90 | - this.attributeLwm2mFormGroup.patchValue({attributes: value}, {emitEvent: false}); | 97 | + this.attributesFormGroup.patchValue({attributes: value}, {emitEvent: false}); |
91 | } | 98 | } |
92 | 99 | ||
93 | get attributesValueMap(): AttributesNameValueMap { | 100 | get attributesValueMap(): AttributesNameValueMap { |
94 | - return this.attributeLwm2mFormGroup.get('attributes').value; | 101 | + return this.attributesFormGroup.get('attributes').value; |
95 | } | 102 | } |
96 | 103 | ||
97 | isDisableBtn(): boolean { | 104 | isDisableBtn(): boolean { |
@@ -140,8 +147,7 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { | @@ -140,8 +147,7 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { | ||
140 | } | 147 | } |
141 | }).afterClosed().subscribe((result) => { | 148 | }).afterClosed().subscribe((result) => { |
142 | if (result) { | 149 | if (result) { |
143 | - this.attributeLwm2mFormGroup.patchValue({attributeLwm2m: result}); | ||
144 | - this.updateAttributeLwm2m.next(result); | 150 | + this.attributesFormGroup.patchValue({attributes: result}); |
145 | } | 151 | } |
146 | }); | 152 | }); |
147 | } | 153 | } |
@@ -287,7 +287,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -287,7 +287,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
287 | 287 | ||
288 | private updateDeviceProfileValue(config): void { | 288 | private updateDeviceProfileValue(config): void { |
289 | if (this.lwm2mDeviceProfileFormGroup.valid) { | 289 | if (this.lwm2mDeviceProfileFormGroup.valid) { |
290 | - this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M); | 290 | + this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry); |
291 | } | 291 | } |
292 | this.configurationValue.bootstrap.bootstrapServer = config.bootstrap.bootstrapServer; | 292 | this.configurationValue.bootstrap.bootstrapServer = config.bootstrap.bootstrapServer; |
293 | this.configurationValue.bootstrap.lwm2mServer = config.bootstrap.lwm2mServer; | 293 | this.configurationValue.bootstrap.lwm2mServer = config.bootstrap.lwm2mServer; |
@@ -297,7 +297,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -297,7 +297,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
297 | this.updateModel(); | 297 | this.updateModel(); |
298 | } | 298 | } |
299 | 299 | ||
300 | - private getObserveAttrTelemetryObjects = (objectList: ObjectLwM2M[]): object => { | 300 | + private getObserveAttrTelemetryObjects = (objectList: ObjectLwM2M[]): ObjectLwM2M[] => { |
301 | const objectLwM2MS = deepClone(objectList); | 301 | const objectLwM2MS = deepClone(objectList); |
302 | if (this.configurationValue.observeAttr && objectLwM2MS.length > 0) { | 302 | if (this.configurationValue.observeAttr && objectLwM2MS.length > 0) { |
303 | const attributeArray = this.configurationValue.observeAttr.attribute; | 303 | const attributeArray = this.configurationValue.observeAttr.attribute; |
@@ -317,7 +317,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -317,7 +317,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
317 | this.updateObserveAttrTelemetryObjects(telemetryArray, objectLwM2MS, TELEMETRY); | 317 | this.updateObserveAttrTelemetryObjects(telemetryArray, objectLwM2MS, TELEMETRY); |
318 | } | 318 | } |
319 | if (isDefinedAndNotNull(this.configurationValue.observeAttr.attributeLwm2m)) { | 319 | if (isDefinedAndNotNull(this.configurationValue.observeAttr.attributeLwm2m)) { |
320 | - this.updateAttributeLwm2m(objectLwM2MS); | 320 | + this.updateAttributes(objectLwM2MS); |
321 | } | 321 | } |
322 | if (isDefinedAndNotNull(keyNameJson)) { | 322 | if (isDefinedAndNotNull(keyNameJson)) { |
323 | this.configurationValue.observeAttr.keyName = this.validateKeyNameObjects(keyNameJson, attributeArray, telemetryArray); | 323 | this.configurationValue.observeAttr.keyName = this.validateKeyNameObjects(keyNameJson, attributeArray, telemetryArray); |
@@ -325,7 +325,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -325,7 +325,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
325 | this.updateKeyNameObjects(objectLwM2MS); | 325 | this.updateKeyNameObjects(objectLwM2MS); |
326 | } | 326 | } |
327 | } | 327 | } |
328 | - return {clientLwM2M: objectLwM2MS}; | 328 | + return objectLwM2MS; |
329 | } | 329 | } |
330 | 330 | ||
331 | private includesNotZeroInstance = (attribute: string[], telemetry: string[]): boolean => { | 331 | private includesNotZeroInstance = (attribute: string[], telemetry: string[]): boolean => { |
@@ -369,7 +369,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -369,7 +369,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
369 | }); | 369 | }); |
370 | } | 370 | } |
371 | 371 | ||
372 | - private updateAttributeLwm2m = (objectLwM2MS: ObjectLwM2M[]): void => { | 372 | + private updateAttributes = (objectLwM2MS: ObjectLwM2M[]): void => { |
373 | Object.keys(this.configurationValue.observeAttr.attributeLwm2m).forEach(key => { | 373 | Object.keys(this.configurationValue.observeAttr.attributeLwm2m).forEach(key => { |
374 | const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String); | 374 | const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String); |
375 | const objectLwM2M = objectLwM2MS.find(objectLwm2m => objectLwm2m.keyId === objectKeyId); | 375 | const objectLwM2M = objectLwM2MS.find(objectLwm2m => objectLwm2m.keyId === objectKeyId); |
@@ -377,12 +377,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -377,12 +377,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
377 | const instance = objectLwM2M.instances.find(obj => obj.id === +instanceId); | 377 | const instance = objectLwM2M.instances.find(obj => obj.id === +instanceId); |
378 | if (instance && resourceId) { | 378 | if (instance && resourceId) { |
379 | instance.resources.find(resource => resource.id === +resourceId) | 379 | instance.resources.find(resource => resource.id === +resourceId) |
380 | - .attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key]; | 380 | + .attributes = this.configurationValue.observeAttr.attributeLwm2m[key]; |
381 | } else if (instance) { | 381 | } else if (instance) { |
382 | - instance.attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key]; | 382 | + instance.attributes = this.configurationValue.observeAttr.attributeLwm2m[key]; |
383 | } | 383 | } |
384 | } else if (objectLwM2M) { | 384 | } else if (objectLwM2M) { |
385 | - objectLwM2M.attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key]; | 385 | + objectLwM2M.attributes = this.configurationValue.observeAttr.attributeLwm2m[key]; |
386 | } | 386 | } |
387 | }); | 387 | }); |
388 | } | 388 | } |
@@ -415,19 +415,19 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -415,19 +415,19 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
415 | const observeArray: Array<string> = []; | 415 | const observeArray: Array<string> = []; |
416 | const attributeArray: Array<string> = []; | 416 | const attributeArray: Array<string> = []; |
417 | const telemetryArray: Array<string> = []; | 417 | const telemetryArray: Array<string> = []; |
418 | - const attributeLwm2m: any = {}; | 418 | + const attributes: any = {}; |
419 | const keyNameNew = {}; | 419 | const keyNameNew = {}; |
420 | const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val)); | 420 | const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val)); |
421 | observeJson.forEach(obj => { | 421 | observeJson.forEach(obj => { |
422 | - if (isDefinedAndNotNull(obj.attributeLwm2m) && !isEmpty(obj.attributeLwm2m)) { | 422 | + if (isDefinedAndNotNull(obj.attributes) && !isEmpty(obj.attributes)) { |
423 | const pathObject = `/${obj.keyId}`; | 423 | const pathObject = `/${obj.keyId}`; |
424 | - attributeLwm2m[pathObject] = obj.attributeLwm2m; | 424 | + attributes[pathObject] = obj.attributes; |
425 | } | 425 | } |
426 | if (obj.hasOwnProperty(INSTANCES) && Array.isArray(obj.instances)) { | 426 | if (obj.hasOwnProperty(INSTANCES) && Array.isArray(obj.instances)) { |
427 | obj.instances.forEach(instance => { | 427 | obj.instances.forEach(instance => { |
428 | - if (isDefinedAndNotNull(instance.attributeLwm2m) && !isEmpty(instance.attributeLwm2m)) { | 428 | + if (isDefinedAndNotNull(instance.attributes) && !isEmpty(instance.attributes)) { |
429 | const pathInstance = `/${obj.keyId}/${instance.id}`; | 429 | const pathInstance = `/${obj.keyId}/${instance.id}`; |
430 | - attributeLwm2m[pathInstance] = instance.attributeLwm2m; | 430 | + attributes[pathInstance] = instance.attributes; |
431 | } | 431 | } |
432 | if (instance.hasOwnProperty(RESOURCES) && Array.isArray(instance.resources)) { | 432 | if (instance.hasOwnProperty(RESOURCES) && Array.isArray(instance.resources)) { |
433 | instance.resources.forEach(resource => { | 433 | instance.resources.forEach(resource => { |
@@ -443,8 +443,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -443,8 +443,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
443 | telemetryArray.push(pathRes); | 443 | telemetryArray.push(pathRes); |
444 | } | 444 | } |
445 | keyNameNew[pathRes] = resource.keyName; | 445 | keyNameNew[pathRes] = resource.keyName; |
446 | - if (isDefinedAndNotNull(resource.attributeLwm2m) && !isEmpty(resource.attributeLwm2m)) { | ||
447 | - attributeLwm2m[pathRes] = resource.attributeLwm2m; | 446 | + if (isDefinedAndNotNull(resource.attributes) && !isEmpty(resource.attributes)) { |
447 | + attributes[pathRes] = resource.attributes; | ||
448 | } | 448 | } |
449 | } | 449 | } |
450 | }); | 450 | }); |
@@ -458,14 +458,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -458,14 +458,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
458 | attribute: attributeArray, | 458 | attribute: attributeArray, |
459 | telemetry: telemetryArray, | 459 | telemetry: telemetryArray, |
460 | keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew), | 460 | keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew), |
461 | - attributeLwm2m | 461 | + attributeLwm2m: attributes |
462 | }; | 462 | }; |
463 | } else { | 463 | } else { |
464 | this.configurationValue.observeAttr.observe = observeArray; | 464 | this.configurationValue.observeAttr.observe = observeArray; |
465 | this.configurationValue.observeAttr.attribute = attributeArray; | 465 | this.configurationValue.observeAttr.attribute = attributeArray; |
466 | this.configurationValue.observeAttr.telemetry = telemetryArray; | 466 | this.configurationValue.observeAttr.telemetry = telemetryArray; |
467 | this.configurationValue.observeAttr.keyName = this.sortObjectKeyPathJson(KEY_NAME, keyNameNew); | 467 | this.configurationValue.observeAttr.keyName = this.sortObjectKeyPathJson(KEY_NAME, keyNameNew); |
468 | - this.configurationValue.observeAttr.attributeLwm2m = attributeLwm2m; | 468 | + this.configurationValue.observeAttr.attributeLwm2m = attributes; |
469 | } | 469 | } |
470 | } | 470 | } |
471 | 471 | ||
@@ -535,7 +535,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -535,7 +535,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
535 | this.removeObserveAttrTelemetryFromJson(TELEMETRY, value.keyId); | 535 | this.removeObserveAttrTelemetryFromJson(TELEMETRY, value.keyId); |
536 | this.removeObserveAttrTelemetryFromJson(ATTRIBUTE, value.keyId); | 536 | this.removeObserveAttrTelemetryFromJson(ATTRIBUTE, value.keyId); |
537 | this.removeKeyNameFromJson(value.keyId); | 537 | this.removeKeyNameFromJson(value.keyId); |
538 | - this.removeAttributeLwm2mFromJson(value.keyId); | 538 | + this.removeAttributesFromJson(value.keyId); |
539 | this.updateObserveAttrTelemetryObjectFormGroup(objectsOld); | 539 | this.updateObserveAttrTelemetryObjectFormGroup(objectsOld); |
540 | this.upDateJsonAllConfig(); | 540 | this.upDateJsonAllConfig(); |
541 | } | 541 | } |
@@ -558,7 +558,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -558,7 +558,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
558 | }); | 558 | }); |
559 | } | 559 | } |
560 | 560 | ||
561 | - private removeAttributeLwm2mFromJson = (keyId: string): void => { | 561 | + private removeAttributesFromJson = (keyId: string): void => { |
562 | const keyNameJson = this.configurationValue.observeAttr.attributeLwm2m; | 562 | const keyNameJson = this.configurationValue.observeAttr.attributeLwm2m; |
563 | Object.keys(keyNameJson).forEach(key => { | 563 | Object.keys(keyNameJson).forEach(key => { |
564 | if (key.startsWith(`/${keyId}`)) { | 564 | if (key.startsWith(`/${keyId}`)) { |
@@ -17,7 +17,7 @@ | @@ -17,7 +17,7 @@ | ||
17 | --> | 17 | --> |
18 | <form [formGroup]="instancesFormGroup" (ngSubmit)="add()" style="min-width: 400px;"> | 18 | <form [formGroup]="instancesFormGroup" (ngSubmit)="add()" style="min-width: 400px;"> |
19 | <mat-toolbar fxLayout="row" color="primary"> | 19 | <mat-toolbar fxLayout="row" color="primary"> |
20 | - <b><i>{{data.objectName}}</i></b> (object <<b>{{data.objectKeyId}}</b>>) | 20 | + <b><i>{{data.objectName}}</i></b> (object <<b>{{data.objectId}}</b>>) |
21 | <span fxFlex></span> | 21 | <span fxFlex></span> |
22 | <button mat-button mat-icon-button | 22 | <button mat-button mat-icon-button |
23 | (click)="cancel()" | 23 | (click)="cancel()" |
@@ -23,9 +23,9 @@ import { Router } from '@angular/router'; | @@ -23,9 +23,9 @@ import { Router } from '@angular/router'; | ||
23 | import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; | 23 | import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; |
24 | 24 | ||
25 | export interface Lwm2mObjectAddInstancesData { | 25 | export interface Lwm2mObjectAddInstancesData { |
26 | - instancesIds: Set<number>; | 26 | + instancesId: Set<number>; |
27 | objectName?: string; | 27 | objectName?: string; |
28 | - objectKeyId?: string; | 28 | + objectId?: number; |
29 | } | 29 | } |
30 | 30 | ||
31 | @Component({ | 31 | @Component({ |
@@ -48,16 +48,15 @@ export class Lwm2mObjectAddInstancesDialogComponent extends DialogComponent<Lwm2 | @@ -48,16 +48,15 @@ export class Lwm2mObjectAddInstancesDialogComponent extends DialogComponent<Lwm2 | ||
48 | 48 | ||
49 | ngOnInit(): void { | 49 | ngOnInit(): void { |
50 | this.instancesFormGroup = this.fb.group({ | 50 | this.instancesFormGroup = this.fb.group({ |
51 | - instancesIds: this.data.instancesIds | 51 | + instancesIds: [this.data.instancesId] |
52 | }); | 52 | }); |
53 | } | 53 | } |
54 | 54 | ||
55 | cancel(): void { | 55 | cancel(): void { |
56 | - this.dialogRef.close(undefined); | 56 | + this.dialogRef.close(null); |
57 | } | 57 | } |
58 | 58 | ||
59 | add(): void { | 59 | add(): void { |
60 | - this.data.instancesIds = this.instancesFormGroup.get('instancesIds').value; | ||
61 | - this.dialogRef.close(this.data); | 60 | + this.dialogRef.close(this.instancesFormGroup.get('instancesIds').value); |
62 | } | 61 | } |
63 | } | 62 | } |
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2021 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<section [formGroup]="instancesFormGroup"> | ||
19 | + <mat-accordion multi="true" formArrayName="instances"> | ||
20 | + <mat-expansion-panel | ||
21 | + class="instance-row" | ||
22 | + *ngFor="let instances of instancesFormArray.controls; let $index = index; trackBy: trackByParams" | ||
23 | + [formGroupName]="$index" | ||
24 | + [expanded]="isExpend" | ||
25 | + [disabled]="isExpend"> | ||
26 | + <mat-expansion-panel-header> | ||
27 | + <mat-panel-title class="tb-panel-title-height" fxLayout="row"> | ||
28 | + <div fxFlex="30" fxLayoutAlign="start center"> | ||
29 | + {{ 'device-profile.lwm2m.instance' | translate }} <<b>{{instances.get('id').value}}</b>> | ||
30 | + </div> | ||
31 | + <div fxLayoutAlign="center center" fxFlex="10"> | ||
32 | + <mat-checkbox color="warn" | ||
33 | + [disabled]="this.disabled" | ||
34 | + [checked]="getChecked(instances, 'attribute')" | ||
35 | + (change)="changeInstanceResourcesCheckBox($event.checked, instances, 'attribute')" | ||
36 | + [indeterminate]="getIndeterminate(instances, 'attribute')"> | ||
37 | + </mat-checkbox> | ||
38 | + </div> | ||
39 | + <div fxLayoutAlign="center center" fxFlex="10"> | ||
40 | + <mat-checkbox color="primary" | ||
41 | + [disabled]="this.disabled" | ||
42 | + [checked]="getChecked(instances, 'telemetry')" | ||
43 | + (change)="changeInstanceResourcesCheckBox($event.checked, instances, 'telemetry')" | ||
44 | + [indeterminate]="getIndeterminate(instances, 'telemetry')"> | ||
45 | + </mat-checkbox> | ||
46 | + </div> | ||
47 | + <div fxLayoutAlign="center center" fxFlex="10"> | ||
48 | + <mat-checkbox color="primary" | ||
49 | + [disabled]="this.disabled || !(getIndeterminate(instances, 'telemetry') || getIndeterminate(instances, 'attribute'))" | ||
50 | + [checked]="getChecked(instances, 'observe')" | ||
51 | + (change)="changeInstanceResourcesCheckBox($event.checked, instances, 'observe')" | ||
52 | + [indeterminate]="getIndeterminate(instances, 'observe')"> | ||
53 | + </mat-checkbox> | ||
54 | + </div> | ||
55 | + <span fxFlex></span> | ||
56 | + <tb-profile-lwm2m-attributes | ||
57 | + formControlName="attributes" | ||
58 | + [isAttributeTelemetry]="disableObserveInstance(instances)" | ||
59 | + [modelName]="getNameInstance(instances.value)"> | ||
60 | + </tb-profile-lwm2m-attributes> | ||
61 | + </mat-panel-title> | ||
62 | + </mat-expansion-panel-header> | ||
63 | + <ng-template matExpansionPanelContent> | ||
64 | + <tb-profile-lwm2m-observe-attr-telemetry-resource | ||
65 | + formControlName="resources"> | ||
66 | + </tb-profile-lwm2m-observe-attr-telemetry-resource> | ||
67 | + </ng-template> | ||
68 | + </mat-expansion-panel> | ||
69 | + </mat-accordion> | ||
70 | +</section> |
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-instances.component.scss
renamed from
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.scss
@@ -13,21 +13,18 @@ | @@ -13,21 +13,18 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -:host { | ||
17 | - .resource-name-lw-end{ | ||
18 | - white-space: nowrap; | ||
19 | - overflow: hidden; | ||
20 | - text-overflow: ellipsis; | ||
21 | - text-align:end; | ||
22 | - //width: 80px; | ||
23 | - cursor: pointer; | 16 | +:host{ |
17 | + .tb-panel-title-height { | ||
18 | + min-height: 42px; | ||
24 | } | 19 | } |
25 | 20 | ||
26 | - .resource-name-lw{ | ||
27 | - white-space: nowrap; | ||
28 | - overflow: hidden; | ||
29 | - text-overflow: ellipsis; | ||
30 | - cursor: pointer; | 21 | + .instance-row { |
22 | + mat-expansion-panel-header { | ||
23 | + color: inherit; | ||
24 | + } | ||
31 | } | 25 | } |
32 | -} | ||
33 | 26 | ||
27 | + .mat-expansion-panel-header-title { | ||
28 | + margin-right: 0; | ||
29 | + } | ||
30 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2021 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { Component, forwardRef, Input, OnDestroy } from '@angular/core'; | ||
18 | +import { | ||
19 | + AbstractControl, | ||
20 | + ControlValueAccessor, | ||
21 | + FormArray, | ||
22 | + FormBuilder, | ||
23 | + FormGroup, | ||
24 | + NG_VALIDATORS, | ||
25 | + NG_VALUE_ACCESSOR, | ||
26 | + ValidationErrors, | ||
27 | + Validator, | ||
28 | + Validators | ||
29 | +} from '@angular/forms'; | ||
30 | +import { coerceBooleanProperty } from '@angular/cdk/coercion'; | ||
31 | +import { Instance, ResourceLwM2M, ResourceSettingTelemetry, } from './lwm2m-profile-config.models'; | ||
32 | +import { deepClone, isDefinedAndNotNull } from '@core/utils'; | ||
33 | +import { TranslateService } from '@ngx-translate/core'; | ||
34 | +import { Subscription } from 'rxjs'; | ||
35 | + | ||
36 | +@Component({ | ||
37 | + selector: 'tb-profile-lwm2m-observe-attr-telemetry-instances', | ||
38 | + templateUrl: './lwm2m-observe-attr-telemetry-instances.component.html', | ||
39 | + styleUrls: [ './lwm2m-observe-attr-telemetry-instances.component.scss'], | ||
40 | + providers: [ | ||
41 | + { | ||
42 | + provide: NG_VALUE_ACCESSOR, | ||
43 | + useExisting: forwardRef(() => Lwm2mObserveAttrTelemetryInstancesComponent), | ||
44 | + multi: true | ||
45 | + }, | ||
46 | + { | ||
47 | + provide: NG_VALIDATORS, | ||
48 | + useExisting: forwardRef(() => Lwm2mObserveAttrTelemetryInstancesComponent), | ||
49 | + multi: true | ||
50 | + } | ||
51 | + ] | ||
52 | +}) | ||
53 | + | ||
54 | +export class Lwm2mObserveAttrTelemetryInstancesComponent implements ControlValueAccessor, Validator, OnDestroy { | ||
55 | + | ||
56 | + instancesFormGroup: FormGroup; | ||
57 | + | ||
58 | + private requiredValue: boolean; | ||
59 | + get required(): boolean { | ||
60 | + return this.requiredValue; | ||
61 | + } | ||
62 | + | ||
63 | + @Input() | ||
64 | + set required(value: boolean) { | ||
65 | + const newVal = coerceBooleanProperty(value); | ||
66 | + if (this.requiredValue !== newVal) { | ||
67 | + this.requiredValue = newVal; | ||
68 | + this.updateValidators(); | ||
69 | + } | ||
70 | + } | ||
71 | + | ||
72 | + @Input() | ||
73 | + disabled: boolean; | ||
74 | + | ||
75 | + private valueChange$: Subscription = null; | ||
76 | + private propagateChange = (v: any) => { }; | ||
77 | + | ||
78 | + constructor(private fb: FormBuilder, | ||
79 | + public translate: TranslateService) { | ||
80 | + this.instancesFormGroup = this.fb.group({ | ||
81 | + instances: this.fb.array([]) | ||
82 | + }); | ||
83 | + } | ||
84 | + | ||
85 | + ngOnDestroy() { | ||
86 | + if (this.valueChange$) { | ||
87 | + this.valueChange$.unsubscribe(); | ||
88 | + } | ||
89 | + } | ||
90 | + | ||
91 | + registerOnChange(fn: any): void { | ||
92 | + this.propagateChange = fn; | ||
93 | + } | ||
94 | + | ||
95 | + registerOnTouched(fn: any): void { | ||
96 | + } | ||
97 | + | ||
98 | + setDisabledState(isDisabled: boolean): void { | ||
99 | + this.disabled = isDisabled; | ||
100 | + if (isDisabled) { | ||
101 | + this.instancesFormGroup.disable({emitEvent: false}); | ||
102 | + } else { | ||
103 | + this.instancesFormGroup.enable({emitEvent: false}); | ||
104 | + } | ||
105 | + } | ||
106 | + | ||
107 | + writeValue(value: Instance[]): void { | ||
108 | + this.updateInstances(value); | ||
109 | + } | ||
110 | + | ||
111 | + validate(control: AbstractControl): ValidationErrors | null { | ||
112 | + return this.instancesFormGroup.valid ? null : { | ||
113 | + instancesForm: false | ||
114 | + }; | ||
115 | + } | ||
116 | + | ||
117 | + get instancesFormArray(): FormArray { | ||
118 | + return this.instancesFormGroup.get('instances') as FormArray; | ||
119 | + } | ||
120 | + | ||
121 | + private updateInstances(instances: Instance[]): void { | ||
122 | + if (instances.length === this.instancesFormArray.length) { | ||
123 | + this.instancesFormArray.patchValue(instances, {emitEvent: false}); | ||
124 | + } else { | ||
125 | + if (this.valueChange$) { | ||
126 | + this.valueChange$.unsubscribe(); | ||
127 | + } | ||
128 | + const instancesControl: Array<AbstractControl> = []; | ||
129 | + if (instances) { | ||
130 | + instances.forEach((instance) => { | ||
131 | + instancesControl.push(this.createInstanceFormGroup(instance)); | ||
132 | + }); | ||
133 | + } | ||
134 | + this.instancesFormGroup.setControl('instances', this.fb.array(instancesControl)); | ||
135 | + if (this.disabled) { | ||
136 | + this.instancesFormGroup.disable({emitEvent: false}); | ||
137 | + } | ||
138 | + this.valueChange$ = this.instancesFormGroup.valueChanges.subscribe(value => { | ||
139 | + this.updateModel(value.instances); | ||
140 | + }); | ||
141 | + } | ||
142 | + } | ||
143 | + | ||
144 | + private createInstanceFormGroup(instance: Instance): FormGroup { | ||
145 | + return this.fb.group({ | ||
146 | + id: [instance.id], | ||
147 | + attributes: [instance.attributes], | ||
148 | + resources: [instance.resources] | ||
149 | + }); | ||
150 | + } | ||
151 | + | ||
152 | + private updateModel(instances: Instance[]) { | ||
153 | + if (instances && this.instancesFormGroup.valid) { | ||
154 | + this.propagateChange(instances); | ||
155 | + } else { | ||
156 | + this.propagateChange(null); | ||
157 | + } | ||
158 | + } | ||
159 | + | ||
160 | + changeInstanceResourcesCheckBox = (value: boolean, instance: AbstractControl, type: ResourceSettingTelemetry): void => { | ||
161 | + const resources = deepClone(instance.get('resources').value as ResourceLwM2M[]); | ||
162 | + if (value && type === 'observe') { | ||
163 | + resources.forEach(resource => resource[type] = resource.telemetry || resource.attribute); | ||
164 | + } else { | ||
165 | + resources.forEach(resource => resource[type] = value); | ||
166 | + } | ||
167 | + instance.get('resources').patchValue(resources); | ||
168 | + } | ||
169 | + | ||
170 | + private updateValidators(): void { | ||
171 | + this.instancesFormArray.setValidators(this.required ? Validators.required : []); | ||
172 | + this.instancesFormArray.updateValueAndValidity(); | ||
173 | + } | ||
174 | + | ||
175 | + trackByParams = (index: number, instance: Instance): number => { | ||
176 | + return instance.id; | ||
177 | + } | ||
178 | + | ||
179 | + getIndeterminate = (instance: AbstractControl, type: ResourceSettingTelemetry): boolean => { | ||
180 | + const resources = instance.get('resources').value as ResourceLwM2M[]; | ||
181 | + if (isDefinedAndNotNull(resources)) { | ||
182 | + const checkedResource = resources.filter(resource => resource[type]); | ||
183 | + return checkedResource.length !== 0 && checkedResource.length !== resources.length; | ||
184 | + } | ||
185 | + return false; | ||
186 | + } | ||
187 | + | ||
188 | + getChecked = (instance: AbstractControl, type: ResourceSettingTelemetry): boolean => { | ||
189 | + const resources = instance.get('resources').value as ResourceLwM2M[]; | ||
190 | + return isDefinedAndNotNull(resources) && resources.every(resource => resource[type]); | ||
191 | + } | ||
192 | + | ||
193 | + get isExpend(): boolean { | ||
194 | + return this.instancesFormArray.length === 1; | ||
195 | + } | ||
196 | + | ||
197 | + getNameInstance(instance: Instance): string { | ||
198 | + return `${this.translate.instant('device-profile.lwm2m.instance')} <${instance.id}>`; | ||
199 | + } | ||
200 | + | ||
201 | + disableObserveInstance = (instance: AbstractControl): boolean => { | ||
202 | + const checkedAttrTelemetry = this.observeInstance(instance); | ||
203 | + if (checkedAttrTelemetry) { | ||
204 | + instance.get('attributes').patchValue(null, {emitEvent: false}); | ||
205 | + } | ||
206 | + return checkedAttrTelemetry; | ||
207 | + } | ||
208 | + | ||
209 | + | ||
210 | + observeInstance = (instance: AbstractControl): boolean => { | ||
211 | + const resources = instance.get('resources').value as ResourceLwM2M[]; | ||
212 | + if (isDefinedAndNotNull(resources)) { | ||
213 | + const checkedAttribute = resources.filter(resource => resource.attribute); | ||
214 | + const checkedTelemetry = resources.filter(resource => resource.telemetry); | ||
215 | + return checkedAttribute.length === 0 && checkedTelemetry.length === 0; | ||
216 | + } | ||
217 | + return false; | ||
218 | + } | ||
219 | +} |
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.ts
deleted
100644 → 0
1 | -/// | ||
2 | -/// Copyright © 2016-2021 The Thingsboard Authors | ||
3 | -/// | ||
4 | -/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | -/// you may not use this file except in compliance with the License. | ||
6 | -/// You may obtain a copy of the License at | ||
7 | -/// | ||
8 | -/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | -/// | ||
10 | -/// Unless required by applicable law or agreed to in writing, software | ||
11 | -/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | -/// See the License for the specific language governing permissions and | ||
14 | -/// limitations under the License. | ||
15 | -/// | ||
16 | - | ||
17 | -import { Component, forwardRef, Input } from '@angular/core'; | ||
18 | -import { ControlValueAccessor, FormArray, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; | ||
19 | -import { ResourceLwM2M, RESOURCES } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; | ||
20 | -import _ from 'lodash'; | ||
21 | -import { coerceBooleanProperty } from '@angular/cdk/coercion'; | ||
22 | - | ||
23 | -@Component({ | ||
24 | - selector: 'tb-profile-lwm2m-observe-attr-telemetry-resource', | ||
25 | - templateUrl: './lwm2m-observe-attr-telemetry-resource.component.html', | ||
26 | - styleUrls: ['./lwm2m-attributes.component.scss'], | ||
27 | - providers: [ | ||
28 | - { | ||
29 | - provide: NG_VALUE_ACCESSOR, | ||
30 | - useExisting: forwardRef(() => Lwm2mObserveAttrTelemetryResourceComponent), | ||
31 | - multi: true | ||
32 | - } | ||
33 | - ] | ||
34 | -}) | ||
35 | - | ||
36 | -export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueAccessor { | ||
37 | - | ||
38 | - private requiredValue: boolean; | ||
39 | - | ||
40 | - resourceFormGroup: FormGroup; | ||
41 | - disabled = false; | ||
42 | - | ||
43 | - get required(): boolean { | ||
44 | - return this.requiredValue; | ||
45 | - } | ||
46 | - | ||
47 | - @Input() | ||
48 | - set required(value: boolean) { | ||
49 | - const newVal = coerceBooleanProperty(value); | ||
50 | - if (this.requiredValue !== newVal) { | ||
51 | - this.requiredValue = newVal; | ||
52 | - } | ||
53 | - } | ||
54 | - | ||
55 | - constructor(private fb: FormBuilder) { | ||
56 | - this.resourceFormGroup = this.fb.group({ | ||
57 | - resources: this.fb.array([]) | ||
58 | - }); | ||
59 | - this.resourceFormGroup.valueChanges.subscribe(value => { | ||
60 | - if (!this.disabled) { | ||
61 | - this.propagateChangeState(value.resources); | ||
62 | - } | ||
63 | - }); | ||
64 | - } | ||
65 | - | ||
66 | - registerOnTouched(fn: any): void { | ||
67 | - } | ||
68 | - | ||
69 | - writeValue(value: ResourceLwM2M[]): void { | ||
70 | - this.createResourceLwM2M(value); | ||
71 | - } | ||
72 | - | ||
73 | - get resourceFormArray(): FormArray{ | ||
74 | - return this.resourceFormGroup.get(RESOURCES) as FormArray; | ||
75 | - } | ||
76 | - | ||
77 | - setDisabledState(isDisabled: boolean): void { | ||
78 | - this.disabled = isDisabled; | ||
79 | - if (isDisabled) { | ||
80 | - this.resourceFormGroup.disable(); | ||
81 | - } else { | ||
82 | - this.resourceFormGroup.enable(); | ||
83 | - } | ||
84 | - } | ||
85 | - | ||
86 | - updateValueKeyName = (event: Event, index: number): void => { | ||
87 | - this.resourceFormArray.at(index).patchValue({keyName: _.camelCase((event.target as HTMLInputElement).value)}); | ||
88 | - } | ||
89 | - | ||
90 | - updateAttributeLwm2m = (event: Event, index: number): void => { | ||
91 | - this.resourceFormArray.at(index).patchValue({attributeLwm2m: event}); | ||
92 | - } | ||
93 | - | ||
94 | - getNameResourceLwm2m = (resourceLwM2M: ResourceLwM2M): string => { | ||
95 | - return `<${resourceLwM2M.id}> ${resourceLwM2M.name}`; | ||
96 | - } | ||
97 | - | ||
98 | - createResourceLwM2M(resourcesLwM2M: ResourceLwM2M[]): void { | ||
99 | - if (resourcesLwM2M.length === this.resourceFormArray.length) { | ||
100 | - this.resourceFormArray.patchValue(resourcesLwM2M, {emitEvent: false}); | ||
101 | - } else { | ||
102 | - this.resourceFormArray.clear(); | ||
103 | - resourcesLwM2M.forEach(resourceLwM2M => { | ||
104 | - this.resourceFormArray.push(this.fb.group( { | ||
105 | - id: resourceLwM2M.id, | ||
106 | - name: resourceLwM2M.name, | ||
107 | - observe: resourceLwM2M.observe, | ||
108 | - attribute: resourceLwM2M.attribute, | ||
109 | - telemetry: resourceLwM2M.telemetry, | ||
110 | - keyName: [resourceLwM2M.keyName, Validators.required], | ||
111 | - attributeLwm2m: [resourceLwM2M.attributeLwm2m] | ||
112 | - })); | ||
113 | - }); | ||
114 | - } | ||
115 | - } | ||
116 | - | ||
117 | - private propagateChange = (v: any) => { }; | ||
118 | - | ||
119 | - registerOnChange(fn: any): void { | ||
120 | - this.propagateChange = fn; | ||
121 | - } | ||
122 | - | ||
123 | - private propagateChangeState = (value: any): void => { | ||
124 | - if (value && this.resourceFormGroup.valid) { | ||
125 | - this.propagateChange(value); | ||
126 | - } else { | ||
127 | - this.propagateChange(null); | ||
128 | - } | ||
129 | - } | ||
130 | - | ||
131 | - trackByParams = (index: number): number => { | ||
132 | - return index; | ||
133 | - } | ||
134 | - | ||
135 | - updateObserve = (index: number): void => { | ||
136 | - if (this.resourceFormArray.at(index).value.attribute === false && this.resourceFormArray.at(index).value.telemetry === false) { | ||
137 | - this.resourceFormArray.at(index).patchValue({observe: false}); | ||
138 | - this.resourceFormArray.at(index).patchValue({attributeLwm2m: {}}); | ||
139 | - } | ||
140 | - } | ||
141 | - | ||
142 | - disableObserve = (index: number): boolean => { | ||
143 | - return !this.resourceFormArray.at(index).value.telemetry && !this.resourceFormArray.at(index).value.attribute; | ||
144 | - } | ||
145 | -} |
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resources.component.html
renamed from
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html
@@ -15,77 +15,61 @@ | @@ -15,77 +15,61 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<section [formGroup]="resourceFormGroup"> | ||
19 | - <div fxLayout="row" fxFill formArrayName="resources" | ||
20 | - *ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams"> | ||
21 | - <div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i"> | ||
22 | - <div fxLayout="row" fxFill fxLayoutAlign="start center" [fxShow]="!i"> | ||
23 | - <div fxFlex="30"> | ||
24 | - <mat-label translate>device-profile.lwm2m.resource-label</mat-label> | ||
25 | - </div> | ||
26 | - <div fxFlex="10" style="text-align: center"> | ||
27 | - <mat-label translate>device-profile.lwm2m.attribute-label</mat-label> | ||
28 | - </div> | ||
29 | - <div fxFlex="10" style="text-align: center"> | ||
30 | - <mat-label translate>device-profile.lwm2m.telemetry-label</mat-label> | ||
31 | - </div> | ||
32 | - <div fxFlex="10" style="text-align: center"> | ||
33 | - <mat-label translate>device-profile.lwm2m.observe-label</mat-label> | ||
34 | - </div> | ||
35 | - <div fxFlex> | ||
36 | - <mat-label translate>device-profile.lwm2m.key-name-label</mat-label> | ||
37 | - </div> | 18 | +<section [formGroup]="resourcesFormGroup"> |
19 | + <div fxLayout="row" fxLayoutAlign="start center"> | ||
20 | + <div fxFlex="30"> | ||
21 | + <mat-label translate>device-profile.lwm2m.resource-label</mat-label> | ||
22 | + </div> | ||
23 | + <div fxFlex="10" class="checkbox-column-title"> | ||
24 | + <mat-label translate>device-profile.lwm2m.attribute-label</mat-label> | ||
25 | + </div> | ||
26 | + <div fxFlex="10" class="checkbox-column-title"> | ||
27 | + <mat-label translate>device-profile.lwm2m.telemetry-label</mat-label> | ||
28 | + </div> | ||
29 | + <div fxFlex="10" class="checkbox-column-title"> | ||
30 | + <mat-label translate>device-profile.lwm2m.observe-label</mat-label> | ||
31 | + </div> | ||
32 | + <div fxFlex> | ||
33 | + <mat-label translate>device-profile.lwm2m.key-name</mat-label> | ||
34 | + </div> | ||
35 | + </div> | ||
36 | + <mat-divider></mat-divider> | ||
37 | + <div formArrayName="resources" | ||
38 | + *ngFor="let resourceLwM2M of resourcesFormArray.controls; let $index = index; trackBy: trackByParams"> | ||
39 | + <div [formGroupName]="$index" fxLayout="row" fxLayoutAlign="start center" class="resource-list"> | ||
40 | + <div class="resource-name" fxFlex="30"> | ||
41 | + <<b>{{resourceLwM2M.get('id').value}}</b>> <b>{{resourceLwM2M.get('name').value}}</b> | ||
42 | + </div> | ||
43 | + <div fxFlex="10" fxLayoutAlign="center center"> | ||
44 | + <mat-checkbox formControlName="attribute" color="warn"> | ||
45 | + </mat-checkbox> | ||
46 | + </div> | ||
47 | + <div fxFlex="10" fxLayoutAlign="center center"> | ||
48 | + <mat-checkbox formControlName="telemetry" color="primary"> | ||
49 | + </mat-checkbox> | ||
38 | </div> | 50 | </div> |
39 | - <div fxLayout="row" fxFill fxLayoutAlign="start center"> | ||
40 | - <div class="resource-name-lw" fxFlex="30" | ||
41 | - matTooltip="{{'device-profile.lwm2m.resource-tip' | translate}}" matTooltipPosition="above"> | ||
42 | - <<b>{{resourceLwM2M.get('id').value}}</b>> <b><i>{{resourceLwM2M.get('name').value}}</i></b> | ||
43 | - </div> | ||
44 | - <div fxFlex="10" fxLayoutAlign="center center"> | ||
45 | - <mat-checkbox formControlName="attribute" color="warn" | ||
46 | - (change)="updateObserve(i)" | ||
47 | - matTooltip="{{'device-profile.lwm2m.is-attr-tip' | translate}}" | ||
48 | - matTooltipPosition="above"> | ||
49 | - </mat-checkbox> | ||
50 | - </div> | ||
51 | - <div fxFlex="10" fxLayoutAlign="center center"> | ||
52 | - <mat-checkbox formControlName="telemetry" color="primary" | ||
53 | - (change)="updateObserve(i)" | ||
54 | - matTooltip="{{'device-profile.lwm2m.is-telemetry-tip' | translate}}" | ||
55 | - matTooltipPosition="above"> | ||
56 | - </mat-checkbox> | ||
57 | - </div> | ||
58 | - <div fxFlex="10" fxLayoutAlign="center center"> | ||
59 | - <mat-checkbox formControlName="observe" color="primary" | ||
60 | - [disabled]="disableObserve(i)" | ||
61 | - matTooltip="{{(disableObserve(i) ? 'device-profile.lwm2m.not-observe-tip' : 'device-profile.lwm2m.is-observe-tip') | translate}}" | ||
62 | - matTooltipPosition="above"> | ||
63 | - </mat-checkbox> | ||
64 | - </div> | ||
65 | - <mat-form-field fxFlex="33"> | ||
66 | - <mat-label *ngIf="resourceLwM2M.get('keyName').hasError('required')"> | ||
67 | - {{ 'device-profile.lwm2m.key-name-label' | translate }}</mat-label> | ||
68 | - <input class="resource-name-lw" matInput type="text" formControlName="keyName" required | ||
69 | - matTooltip="{{'device-profile.lwm2m.key-name-tip' | translate}}" | ||
70 | - (input)="updateValueKeyName($event, i)" | ||
71 | - matTooltipPosition="above"> | ||
72 | - <mat-error *ngIf="resourceLwM2M.get('keyName').hasError('required')"> | ||
73 | - {{ 'device-profile.lwm2m.key-name' | translate }} | ||
74 | - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> | ||
75 | - </mat-error> | ||
76 | - </mat-form-field> | ||
77 | - <span fxFlex></span> | ||
78 | - <div class="resource-name-lw-end"> | ||
79 | - <tb-profile-lwm2m-attributes | ||
80 | - formControlName="attributeLwm2m" | ||
81 | - [isAttributeTelemetry]="disableObserve(i)" | ||
82 | - isResource="true" | ||
83 | - [modelName]="getNameResourceLwm2m(resourceLwM2M.value)" | ||
84 | - [disabled]="this.disabled" | ||
85 | - (updateAttributeLwm2m)="updateAttributeLwm2m($event, i)"> | ||
86 | - </tb-profile-lwm2m-attributes> | ||
87 | - </div> | 51 | + <div fxFlex="10" fxLayoutAlign="center center"> |
52 | + <mat-checkbox fxFlex="10" formControlName="observe" color="primary" | ||
53 | + matTooltip="{{ 'device-profile.lwm2m.edit-observe-select' | translate }}" | ||
54 | + [matTooltipDisabled]="disabled || !isDisabledObserve($index)" | ||
55 | + matTooltipPosition="above"> | ||
56 | + </mat-checkbox> | ||
88 | </div> | 57 | </div> |
58 | + <mat-form-field fxFlex="33" hideRequiredMarker> | ||
59 | + <mat-label></mat-label> | ||
60 | + <input matInput type="text" formControlName="keyName" required> | ||
61 | + <mat-error *ngIf="resourceLwM2M.get('keyName').hasError('required') || | ||
62 | + resourceLwM2M.get('keyName').hasError('pattern')"> | ||
63 | + {{ 'device-profile.lwm2m.key-name-required' | translate }} | ||
64 | + </mat-error> | ||
65 | + </mat-form-field> | ||
66 | + <span fxFlex></span> | ||
67 | + <tb-profile-lwm2m-attributes | ||
68 | + formControlName="attributes" | ||
69 | + [isAttributeTelemetry]="isDisabledObserve($index)" | ||
70 | + isResource="true" | ||
71 | + [modelName]="getNameResourceLwm2m(resourceLwM2M.value)"> | ||
72 | + </tb-profile-lwm2m-attributes> | ||
89 | </div> | 73 | </div> |
90 | </div> | 74 | </div> |
91 | </section> | 75 | </section> |
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +:host { | ||
17 | + .resource-name{ | ||
18 | + white-space: nowrap; | ||
19 | + overflow: hidden; | ||
20 | + text-overflow: ellipsis; | ||
21 | + } | ||
22 | + | ||
23 | + .checkbox-column-title { | ||
24 | + text-align: center; | ||
25 | + } | ||
26 | + | ||
27 | + .mat-divider { | ||
28 | + margin-bottom: 4px; | ||
29 | + } | ||
30 | + | ||
31 | + .resource-list { | ||
32 | + padding-bottom: 8px; | ||
33 | + height: 42px; | ||
34 | + } | ||
35 | +} | ||
36 | + | ||
37 | +:host ::ng-deep { | ||
38 | + .resource-list { | ||
39 | + mat-form-field { | ||
40 | + .mat-form-field-wrapper { | ||
41 | + padding-bottom: 0; | ||
42 | + .mat-form-field-infix { | ||
43 | + border-top-width: 0.2em; | ||
44 | + width: auto; | ||
45 | + min-width: auto; | ||
46 | + } | ||
47 | + .mat-form-field-underline { | ||
48 | + bottom: 0; | ||
49 | + } | ||
50 | + .mat-form-field-subscript-wrapper{ | ||
51 | + margin-top: 1.8em; | ||
52 | + } | ||
53 | + } | ||
54 | + } | ||
55 | + } | ||
56 | +} |
1 | +/// | ||
2 | +/// Copyright © 2016-2021 The Thingsboard Authors | ||
3 | +/// | ||
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | +/// you may not use this file except in compliance with the License. | ||
6 | +/// You may obtain a copy of the License at | ||
7 | +/// | ||
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | +/// | ||
10 | +/// Unless required by applicable law or agreed to in writing, software | ||
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | +/// See the License for the specific language governing permissions and | ||
14 | +/// limitations under the License. | ||
15 | +/// | ||
16 | + | ||
17 | +import { Component, forwardRef, Input, OnDestroy } from '@angular/core'; | ||
18 | +import { | ||
19 | + AbstractControl, | ||
20 | + ControlValueAccessor, | ||
21 | + FormArray, | ||
22 | + FormBuilder, | ||
23 | + FormGroup, | ||
24 | + NG_VALIDATORS, | ||
25 | + NG_VALUE_ACCESSOR, | ||
26 | + ValidationErrors, | ||
27 | + Validator, | ||
28 | + Validators | ||
29 | +} from '@angular/forms'; | ||
30 | +import { ResourceLwM2M } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; | ||
31 | +import { coerceBooleanProperty } from '@angular/cdk/coercion'; | ||
32 | +import { combineLatest, Subject, Subscription } from 'rxjs'; | ||
33 | +import { startWith, takeUntil } from 'rxjs/operators'; | ||
34 | + | ||
35 | +@Component({ | ||
36 | + selector: 'tb-profile-lwm2m-observe-attr-telemetry-resource', | ||
37 | + templateUrl: './lwm2m-observe-attr-telemetry-resources.component.html', | ||
38 | + styleUrls: ['./lwm2m-observe-attr-telemetry-resources.component.scss'], | ||
39 | + providers: [ | ||
40 | + { | ||
41 | + provide: NG_VALUE_ACCESSOR, | ||
42 | + useExisting: forwardRef(() => Lwm2mObserveAttrTelemetryResourcesComponent), | ||
43 | + multi: true | ||
44 | + }, | ||
45 | + { | ||
46 | + provide: NG_VALIDATORS, | ||
47 | + useExisting: forwardRef(() => Lwm2mObserveAttrTelemetryResourcesComponent), | ||
48 | + multi: true | ||
49 | + } | ||
50 | + ] | ||
51 | +}) | ||
52 | + | ||
53 | +export class Lwm2mObserveAttrTelemetryResourcesComponent implements ControlValueAccessor, OnDestroy, Validator { | ||
54 | + | ||
55 | + resourcesFormGroup: FormGroup; | ||
56 | + | ||
57 | + @Input() | ||
58 | + disabled = false; | ||
59 | + | ||
60 | + private requiredValue: boolean; | ||
61 | + get required(): boolean { | ||
62 | + return this.requiredValue; | ||
63 | + } | ||
64 | + | ||
65 | + @Input() | ||
66 | + set required(value: boolean) { | ||
67 | + const newVal = coerceBooleanProperty(value); | ||
68 | + if (this.requiredValue !== newVal) { | ||
69 | + this.requiredValue = newVal; | ||
70 | + } | ||
71 | + } | ||
72 | + | ||
73 | + private destroy$ = new Subject(); | ||
74 | + private valueChange$: Subscription = null; | ||
75 | + private propagateChange = (v: any) => { }; | ||
76 | + | ||
77 | + constructor(private fb: FormBuilder) { | ||
78 | + this.resourcesFormGroup = this.fb.group({ | ||
79 | + resources: this.fb.array([]) | ||
80 | + }); | ||
81 | + } | ||
82 | + | ||
83 | + ngOnDestroy() { | ||
84 | + if (this.valueChange$) { | ||
85 | + this.valueChange$.unsubscribe(); | ||
86 | + } | ||
87 | + this.destroy$.next(); | ||
88 | + this.destroy$.complete(); | ||
89 | + } | ||
90 | + | ||
91 | + registerOnTouched(fn: any): void { | ||
92 | + } | ||
93 | + | ||
94 | + registerOnChange(fn: any): void { | ||
95 | + this.propagateChange = fn; | ||
96 | + } | ||
97 | + | ||
98 | + writeValue(value: ResourceLwM2M[]): void { | ||
99 | + this.updatedResources(value); | ||
100 | + } | ||
101 | + | ||
102 | + setDisabledState(isDisabled: boolean): void { | ||
103 | + this.disabled = isDisabled; | ||
104 | + if (isDisabled) { | ||
105 | + this.resourcesFormGroup.disable({emitEvent: false}); | ||
106 | + } else { | ||
107 | + this.resourcesFormArray.controls.forEach(resource => { | ||
108 | + resource.get('keyName').enable({emitEvent: false}); | ||
109 | + resource.get('attribute').enable({emitEvent: false}); | ||
110 | + resource.get('telemetry').enable({onlySelf: true}); | ||
111 | + resource.get('attributes').enable({emitEvent: false}); | ||
112 | + }); | ||
113 | + } | ||
114 | + } | ||
115 | + | ||
116 | + validate(): ValidationErrors | null { | ||
117 | + return this.resourcesFormGroup.valid ? null : { | ||
118 | + resources: false | ||
119 | + }; | ||
120 | + } | ||
121 | + | ||
122 | + get resourcesFormArray(): FormArray { | ||
123 | + return this.resourcesFormGroup.get('resources') as FormArray; | ||
124 | + } | ||
125 | + | ||
126 | + getNameResourceLwm2m(resourceLwM2M: ResourceLwM2M): string { | ||
127 | + return `<${resourceLwM2M.id}> ${resourceLwM2M.name}`; | ||
128 | + } | ||
129 | + | ||
130 | + private updatedResources(resources: ResourceLwM2M[]): void { | ||
131 | + if (resources.length === this.resourcesFormArray.length) { | ||
132 | + this.resourcesFormArray.patchValue(resources, {emitEvent: false}); | ||
133 | + } else { | ||
134 | + if (this.valueChange$) { | ||
135 | + this.valueChange$.unsubscribe(); | ||
136 | + } | ||
137 | + const resourcesControl: Array<AbstractControl> = []; | ||
138 | + if (resources) { | ||
139 | + resources.forEach((resource) => { | ||
140 | + resourcesControl.push(this.createdResourceFormGroup(resource)); | ||
141 | + }); | ||
142 | + } | ||
143 | + this.resourcesFormGroup.setControl('resources', this.fb.array(resourcesControl)); | ||
144 | + if (this.disabled) { | ||
145 | + this.resourcesFormGroup.disable({emitEvent: false}); | ||
146 | + } | ||
147 | + this.valueChange$ = this.resourcesFormGroup.valueChanges.subscribe(value => { | ||
148 | + this.updateModel(this.resourcesFormGroup.getRawValue().resources); | ||
149 | + }); | ||
150 | + } | ||
151 | + } | ||
152 | + | ||
153 | + private createdResourceFormGroup(resource: ResourceLwM2M): FormGroup { | ||
154 | + const form = this.fb.group( { | ||
155 | + id: [resource.id], | ||
156 | + name: [resource.name], | ||
157 | + attribute: [resource.attribute], | ||
158 | + telemetry: [resource.telemetry], | ||
159 | + observe: [resource.observe], | ||
160 | + keyName: [resource.keyName, [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*')]], | ||
161 | + attributes: [resource.attributes] | ||
162 | + }); | ||
163 | + combineLatest([ | ||
164 | + form.get('attribute').valueChanges.pipe(startWith(resource.attribute), takeUntil(this.destroy$)), | ||
165 | + form.get('telemetry').valueChanges.pipe(startWith(resource.telemetry), takeUntil(this.destroy$)) | ||
166 | + ]).subscribe(([attribute, telemetry]) => { | ||
167 | + if (attribute || telemetry) { | ||
168 | + form.get('observe').enable({emitEvent: false}); | ||
169 | + } else { | ||
170 | + form.get('observe').disable({emitEvent: false}); | ||
171 | + form.get('observe').patchValue(false, {emitEvent: false}); | ||
172 | + form.get('attributes').patchValue({}, {emitEvent: false}); | ||
173 | + } | ||
174 | + }); | ||
175 | + return form; | ||
176 | + } | ||
177 | + | ||
178 | + private updateModel(value: ResourceLwM2M[]) { | ||
179 | + if (value && this.resourcesFormGroup.valid) { | ||
180 | + this.propagateChange(value); | ||
181 | + } else { | ||
182 | + this.propagateChange(null); | ||
183 | + } | ||
184 | + } | ||
185 | + | ||
186 | + trackByParams(index: number, resource: ResourceLwM2M): number { | ||
187 | + return resource.id; | ||
188 | + } | ||
189 | + | ||
190 | + isDisabledObserve(index: number): boolean{ | ||
191 | + return this.resourcesFormArray.at(index).get('observe').disabled; | ||
192 | + } | ||
193 | +} |
@@ -15,107 +15,32 @@ | @@ -15,107 +15,32 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<section [formGroup]="observeAttrTelemetryFormGroup"> | ||
19 | - <mat-accordion multi="true" formArrayName="clientLwM2M"> | 18 | +<section [formGroup]="modelsFormGroup"> |
19 | + <mat-accordion multi="true" formArrayName="models"> | ||
20 | <mat-expansion-panel | 20 | <mat-expansion-panel |
21 | - *ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index;" | ||
22 | - [formGroupName]="i"> | 21 | + *ngFor="let objectLwM2M of modelsFormArray.controls; let $index = index; trackBy: trackByParams" |
22 | + [formGroupName]="$index"> | ||
23 | <mat-expansion-panel-header > | 23 | <mat-expansion-panel-header > |
24 | <mat-panel-title fxLayoutAlign="start center" > | 24 | <mat-panel-title fxLayoutAlign="start center" > |
25 | - <b><i>{{ objectLwM2M.get('name').value}}</i></b> <{{ objectLwM2M.get('keyId').value}}> | ||
26 | - <div fxFlex class="resource-name-lw-end"> | ||
27 | - <tb-profile-lwm2m-attributes | ||
28 | - formControlName="attributeLwm2m" | ||
29 | - [isAttributeTelemetry]="disableObserveObject(i)" | ||
30 | - [modelName]="getNameObjectLwm2m( objectLwM2M.get('name').value, objectLwM2M.get('keyId').value)" | ||
31 | - [disabled]="this.disabled" | ||
32 | - (updateAttributeLwm2m)="updateAttributeLwm2mObject($event, objectLwM2M.get('keyId').value)"> | ||
33 | - </tb-profile-lwm2m-attributes> | ||
34 | - </div> | ||
35 | - </mat-panel-title> | ||
36 | - <mat-panel-description fxFlex="5" fxLayoutAlign="end center" *ngIf="!disabled && objectLwM2M.get('multiple').value"> | 25 | + <b><i>{{ objectLwM2M.get('name').value}}</i></b> <{{ objectLwM2M.get('id').value}}> |
26 | + <span fxFlex></span> | ||
27 | + <tb-profile-lwm2m-attributes | ||
28 | + formControlName="attributes" | ||
29 | + [modelName]="getNameObject(objectLwM2M.value)"> | ||
30 | + </tb-profile-lwm2m-attributes> | ||
37 | <button type="button" | 31 | <button type="button" |
38 | - mat-button mat-icon-button (click)="addInstances($event, objectLwM2M.value)" | ||
39 | - matTooltip="{{'device-profile.lwm2m.add-instances-tip' | translate}}" | 32 | + *ngIf="!disabled && objectLwM2M.get('multiple').value" |
33 | + mat-button mat-icon-button (click)="addInstances($event, objectLwM2M)" | ||
34 | + matTooltip="{{'device-profile.lwm2m.add-new-instances' | translate}}" | ||
40 | matTooltipPosition="above"> | 35 | matTooltipPosition="above"> |
41 | - <mat-icon class="material-icons">{{'note_add'}}</mat-icon> | 36 | + <mat-icon class="material-icons">note_add</mat-icon> |
42 | </button> | 37 | </button> |
43 | - </mat-panel-description> | 38 | + </mat-panel-title> |
44 | </mat-expansion-panel-header> | 39 | </mat-expansion-panel-header> |
45 | <ng-template matExpansionPanelContent> | 40 | <ng-template matExpansionPanelContent> |
46 | - <div fxLayout="column" fxLayoutGap="8px" formArrayName="instances"> | ||
47 | - <mat-expansion-panel | ||
48 | - class="instance-list" | ||
49 | - *ngFor="let instances of instancesLwm2mFormArray(objectLwM2M).controls; let y = index;" | ||
50 | - [formGroupName]="y" | ||
51 | - [expanded]="getExpended(objectLwM2M)" | ||
52 | - [disabled]="getExpended(objectLwM2M)"> | ||
53 | - <mat-expansion-panel-header> | ||
54 | - <mat-panel-title> | ||
55 | - <div class="tb-panel-title-height" fxFlex="100"> | ||
56 | - <div fxLayout="row" fxFill> | ||
57 | - <div fxFlex="30"> | ||
58 | - {{'device-profile.lwm2m.instance-label' | translate}} <<b>{{instances.get('id').value}}</b>> | ||
59 | - </div> | ||
60 | - <div class="checkbox-padding" fxFlex="10"> | ||
61 | - <mat-checkbox color="warn" | ||
62 | - [disabled]="this.disabled" | ||
63 | - [checked]="getChecked(instances, 'attribute')" | ||
64 | - (click)="$event.stopPropagation()" | ||
65 | - (change)="changeInstanceResourcesCheckBox($event.checked, instances, 'attribute')" | ||
66 | - (keydown)="$event.stopPropagation()" | ||
67 | - [indeterminate]="getIndeterminate(instances, 'attribute')" | ||
68 | - matTooltip="{{'device-profile.lwm2m.is-attr-tip' | translate}}" | ||
69 | - matTooltipPosition="above"> | ||
70 | - </mat-checkbox> | ||
71 | - </div> | ||
72 | - <div class="checkbox-padding" fxFlex="10"> | ||
73 | - <mat-checkbox color="primary" | ||
74 | - [disabled]="this.disabled" | ||
75 | - [checked]="getChecked(instances, 'telemetry')" | ||
76 | - (click)="$event.stopPropagation()" | ||
77 | - (change)="changeInstanceResourcesCheckBox($event.checked, instances, 'telemetry')" | ||
78 | - (keydown)="$event.stopPropagation()" | ||
79 | - [indeterminate]="getIndeterminate(instances, 'telemetry')" | ||
80 | - matTooltip="{{'device-profile.lwm2m.is-telemetry-tip' | translate}}" | ||
81 | - matTooltipPosition="above"> | ||
82 | - </mat-checkbox> | ||
83 | - </div> | ||
84 | - <div class="checkbox-padding" fxFlex="10"> | ||
85 | - <mat-checkbox color="primary" | ||
86 | - [disabled]="this.disabled" | ||
87 | - [checked]="getChecked(instances, 'observe')" | ||
88 | - (click)="$event.stopPropagation()" | ||
89 | - (change)="changeInstanceResourcesCheckBox($event.checked, instances, 'observe')" | ||
90 | - (keydown)="$event.stopPropagation()" | ||
91 | - [indeterminate]="getIndeterminate(instances, 'observe')" | ||
92 | - matTooltip="{{'device-profile.lwm2m.is-observe-tip' | translate}}" | ||
93 | - matTooltipPosition="above"> | ||
94 | - </mat-checkbox> | ||
95 | - </div> | ||
96 | - <div fxFlex="7"> | ||
97 | - </div> | ||
98 | - <div fxFlex="37" class="resource-name-lw-end" fxFlexOffset="5"> | ||
99 | - <tb-profile-lwm2m-attributes | ||
100 | - formControlName="attributeLwm2m" | ||
101 | - [isAttributeTelemetry]="disableObserveInstance(instances)" | ||
102 | - [modelName]="getNameInstanceLwm2m(instances.value, objectLwM2M.get('keyId').value)" | ||
103 | - [disabled]="this.disabled" | ||
104 | - (updateAttributeLwm2m)="updateAttributeLwm2mInstance($event, y, objectLwM2M.get('keyId').value)"> | ||
105 | - </tb-profile-lwm2m-attributes> | ||
106 | - </div> | ||
107 | - </div> | ||
108 | - </div> | ||
109 | - </mat-panel-title> | ||
110 | - </mat-expansion-panel-header> | ||
111 | - <ng-template matExpansionPanelContent> | ||
112 | - <tb-profile-lwm2m-observe-attr-telemetry-resource | ||
113 | - formControlName="resources" | ||
114 | - [required]="required"> | ||
115 | - </tb-profile-lwm2m-observe-attr-telemetry-resource> | ||
116 | - </ng-template> | ||
117 | - </mat-expansion-panel> | ||
118 | - </div> | 41 | + <tb-profile-lwm2m-observe-attr-telemetry-instances |
42 | + formControlName="instances"> | ||
43 | + </tb-profile-lwm2m-observe-attr-telemetry-instances> | ||
119 | </ng-template> | 44 | </ng-template> |
120 | </mat-expansion-panel> | 45 | </mat-expansion-panel> |
121 | </mat-accordion> | 46 | </mat-accordion> |
@@ -13,23 +13,13 @@ | @@ -13,23 +13,13 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -.tb-panel-title-height { | ||
17 | - user-select: none; | ||
18 | - min-height: 32px; | ||
19 | -} | ||
20 | - | ||
21 | -.checkbox-padding { | ||
22 | - padding-left: 22px; | ||
23 | - text-align:center; | ||
24 | -} | ||
25 | - | ||
26 | :host{ | 16 | :host{ |
27 | section { | 17 | section { |
28 | padding: 2px; | 18 | padding: 2px; |
29 | } | 19 | } |
30 | - .instance-list { | ||
31 | - mat-expansion-panel-header { | ||
32 | - color: inherit; | ||
33 | - } | 20 | + |
21 | + .tb-panel-title-height { | ||
22 | + user-select: none; | ||
23 | + min-height: 32px; | ||
34 | } | 24 | } |
35 | } | 25 | } |
@@ -14,38 +14,29 @@ | @@ -14,38 +14,29 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { Component, forwardRef, Input } from '@angular/core'; | 17 | +import { ChangeDetectorRef, Component, forwardRef, Input, OnDestroy } from '@angular/core'; |
18 | import { | 18 | import { |
19 | AbstractControl, | 19 | AbstractControl, |
20 | ControlValueAccessor, | 20 | ControlValueAccessor, |
21 | FormArray, | 21 | FormArray, |
22 | FormBuilder, | 22 | FormBuilder, |
23 | FormGroup, | 23 | FormGroup, |
24 | + NG_VALIDATORS, | ||
24 | NG_VALUE_ACCESSOR, | 25 | NG_VALUE_ACCESSOR, |
26 | + ValidationErrors, | ||
27 | + Validator, | ||
25 | Validators | 28 | Validators |
26 | } from '@angular/forms'; | 29 | } from '@angular/forms'; |
27 | -import { Store } from '@ngrx/store'; | ||
28 | -import { AppState } from '@core/core.state'; | ||
29 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; | 30 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
30 | -import { | ||
31 | - ATTRIBUTE, | ||
32 | - ATTRIBUTE_LWM2M, | ||
33 | - CLIENT_LWM2M, | ||
34 | - Instance, | ||
35 | - INSTANCES, | ||
36 | - ObjectLwM2M, | ||
37 | - ResourceLwM2M, | ||
38 | - RESOURCES, | ||
39 | - TELEMETRY | ||
40 | -} from './lwm2m-profile-config.models'; | ||
41 | -import { deepClone, isDefinedAndNotNull, isEqual, isUndefined } from '@core/utils'; | 31 | +import { Instance, ObjectLwM2M } from './lwm2m-profile-config.models'; |
32 | +import { deepClone, isDefinedAndNotNull, isEqual } from '@core/utils'; | ||
42 | import { MatDialog } from '@angular/material/dialog'; | 33 | import { MatDialog } from '@angular/material/dialog'; |
43 | -import { TranslateService } from '@ngx-translate/core'; | ||
44 | import { | 34 | import { |
45 | Lwm2mObjectAddInstancesData, | 35 | Lwm2mObjectAddInstancesData, |
46 | Lwm2mObjectAddInstancesDialogComponent | 36 | Lwm2mObjectAddInstancesDialogComponent |
47 | } from '@home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component'; | 37 | } from '@home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component'; |
48 | import _ from 'lodash'; | 38 | import _ from 'lodash'; |
39 | +import { Subscription } from 'rxjs'; | ||
49 | 40 | ||
50 | @Component({ | 41 | @Component({ |
51 | selector: 'tb-profile-lwm2m-observe-attr-telemetry', | 42 | selector: 'tb-profile-lwm2m-observe-attr-telemetry', |
@@ -56,18 +47,20 @@ import _ from 'lodash'; | @@ -56,18 +47,20 @@ import _ from 'lodash'; | ||
56 | provide: NG_VALUE_ACCESSOR, | 47 | provide: NG_VALUE_ACCESSOR, |
57 | useExisting: forwardRef(() => Lwm2mObserveAttrTelemetryComponent), | 48 | useExisting: forwardRef(() => Lwm2mObserveAttrTelemetryComponent), |
58 | multi: true | 49 | multi: true |
50 | + }, | ||
51 | + { | ||
52 | + provide: NG_VALIDATORS, | ||
53 | + useExisting: forwardRef(() => Lwm2mObserveAttrTelemetryComponent), | ||
54 | + multi: true | ||
59 | } | 55 | } |
60 | ] | 56 | ] |
61 | }) | 57 | }) |
62 | 58 | ||
63 | -export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor { | 59 | +export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor, OnDestroy, Validator { |
64 | 60 | ||
65 | - private requiredValue: boolean; | ||
66 | - | ||
67 | - valuePrev = null as any; | ||
68 | - observeAttrTelemetryFormGroup: FormGroup; | ||
69 | - resources = RESOURCES; | 61 | + modelsFormGroup: FormGroup; |
70 | 62 | ||
63 | + private requiredValue: boolean; | ||
71 | get required(): boolean { | 64 | get required(): boolean { |
72 | return this.requiredValue; | 65 | return this.requiredValue; |
73 | } | 66 | } |
@@ -84,132 +77,103 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor | @@ -84,132 +77,103 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor | ||
84 | @Input() | 77 | @Input() |
85 | disabled: boolean; | 78 | disabled: boolean; |
86 | 79 | ||
87 | - constructor(private store: Store<AppState>, | ||
88 | - private fb: FormBuilder, | 80 | + private valueChange$: Subscription = null; |
81 | + private propagateChange = (v: any) => { }; | ||
82 | + | ||
83 | + constructor(private fb: FormBuilder, | ||
89 | private dialog: MatDialog, | 84 | private dialog: MatDialog, |
90 | - public translate: TranslateService) { | ||
91 | - this.observeAttrTelemetryFormGroup = this.fb.group({ | ||
92 | - [CLIENT_LWM2M]: this.fb.array([]) | ||
93 | - }); | ||
94 | - this.observeAttrTelemetryFormGroup.valueChanges.subscribe(value => { | ||
95 | - if (isUndefined(this.disabled) || !this.disabled) { | ||
96 | - this.propagateChangeState(value); | ||
97 | - } | 85 | + private cd: ChangeDetectorRef) { |
86 | + this.modelsFormGroup = this.fb.group({ | ||
87 | + models: this.fb.array([]) | ||
98 | }); | 88 | }); |
99 | } | 89 | } |
100 | 90 | ||
101 | - private propagateChange = (v: any) => { | ||
102 | - }; | 91 | + ngOnDestroy() { |
92 | + if (this.valueChange$) { | ||
93 | + this.valueChange$.unsubscribe(); | ||
94 | + } | ||
95 | + } | ||
103 | 96 | ||
104 | registerOnChange(fn: any): void { | 97 | registerOnChange(fn: any): void { |
105 | this.propagateChange = fn; | 98 | this.propagateChange = fn; |
106 | } | 99 | } |
107 | 100 | ||
108 | - private propagateChangeState = (value: any): void => { | ||
109 | - if (value) { | ||
110 | - if (this.valuePrev === null) { | ||
111 | - this.valuePrev = 'init'; | ||
112 | - } else if (this.valuePrev === 'init') { | ||
113 | - this.valuePrev = value; | ||
114 | - } else if (JSON.stringify(value) !== JSON.stringify(this.valuePrev)) { | ||
115 | - this.valuePrev = value; | ||
116 | - if (this.observeAttrTelemetryFormGroup.valid) { | ||
117 | - this.propagateChange(value); | ||
118 | - } else { | ||
119 | - this.propagateChange(null); | ||
120 | - } | ||
121 | - } | ||
122 | - } | ||
123 | - } | ||
124 | - | ||
125 | registerOnTouched(fn: any): void { | 101 | registerOnTouched(fn: any): void { |
126 | } | 102 | } |
127 | 103 | ||
128 | setDisabledState(isDisabled: boolean): void { | 104 | setDisabledState(isDisabled: boolean): void { |
129 | this.disabled = isDisabled; | 105 | this.disabled = isDisabled; |
130 | - this.valuePrev = null; | ||
131 | if (isDisabled) { | 106 | if (isDisabled) { |
132 | - this.observeAttrTelemetryFormGroup.disable(); | 107 | + this.modelsFormGroup.disable({emitEvent: false}); |
133 | } else { | 108 | } else { |
134 | - this.observeAttrTelemetryFormGroup.enable(); | 109 | + this.modelsFormGroup.enable({emitEvent: false}); |
135 | } | 110 | } |
136 | } | 111 | } |
137 | 112 | ||
138 | - writeValue(value: {}): void { | 113 | + writeValue(value: ObjectLwM2M[]): void { |
139 | if (isDefinedAndNotNull(value)) { | 114 | if (isDefinedAndNotNull(value)) { |
140 | - this.buildClientObjectsLwM2M(value[CLIENT_LWM2M]); | 115 | + this.updateModels(value); |
141 | } | 116 | } |
142 | } | 117 | } |
143 | 118 | ||
144 | - private buildClientObjectsLwM2M = (objectsLwM2M: ObjectLwM2M []): void => { | ||
145 | - this.observeAttrTelemetryFormGroup.setControl(CLIENT_LWM2M, | ||
146 | - this.createObjectsLwM2M(objectsLwM2M) | ||
147 | - ); | 119 | + validate(): ValidationErrors | null { |
120 | + return this.modelsFormGroup.valid ? null : { | ||
121 | + modelsFormGroup: false | ||
122 | + }; | ||
148 | } | 123 | } |
149 | 124 | ||
150 | - private createObjectsLwM2M = (objectsLwM2M: ObjectLwM2M[]): FormArray => { | ||
151 | - return this.fb.array(objectsLwM2M.map((objectLwM2M) => { | ||
152 | - return this.fb.group({ | ||
153 | - keyId: objectLwM2M.keyId, | ||
154 | - name: objectLwM2M.name, | ||
155 | - multiple: objectLwM2M.multiple, | ||
156 | - mandatory: objectLwM2M.mandatory, | ||
157 | - attributeLwm2m: objectLwM2M.attributeLwm2m, | ||
158 | - instances: this.createInstanceLwM2M(objectLwM2M.instances) | ||
159 | - }); | ||
160 | - })); | 125 | + get modelsFormArray(): FormArray { |
126 | + return this.modelsFormGroup.get('models') as FormArray; | ||
161 | } | 127 | } |
162 | 128 | ||
163 | - private createInstanceLwM2M = (instancesLwM2M: Instance[]): FormArray => { | ||
164 | - return this.fb.array(instancesLwM2M.map((instanceLwM2M) => { | ||
165 | - return this.fb.group({ | ||
166 | - id: instanceLwM2M.id, | ||
167 | - attributeLwm2m: {value: instanceLwM2M.attributeLwm2m, disabled: this.disabled}, | ||
168 | - resources: {value: instanceLwM2M.resources, disabled: this.disabled} | 129 | + private updateModels(models: ObjectLwM2M[]) { |
130 | + if (models.length === this.modelsFormArray.length) { | ||
131 | + this.modelsFormArray.patchValue(models, {emitEvent: false}); | ||
132 | + } else { | ||
133 | + if (this.valueChange$) { | ||
134 | + this.valueChange$.unsubscribe(); | ||
135 | + } | ||
136 | + const modelControls: Array<AbstractControl> = []; | ||
137 | + models.forEach(model => { | ||
138 | + modelControls.push(this.createModelFormGroup(model)); | ||
169 | }); | 139 | }); |
170 | - })); | ||
171 | - } | ||
172 | - | ||
173 | - get clientLwM2MFormArray(): FormArray { | ||
174 | - return this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray; | ||
175 | - } | ||
176 | - | ||
177 | - instancesLwm2mFormArray = (objectLwM2M: AbstractControl): FormArray => { | ||
178 | - return objectLwM2M.get(INSTANCES) as FormArray; | ||
179 | - } | ||
180 | - | ||
181 | - changeInstanceResourcesCheckBox = (value: boolean, instance: AbstractControl, type: string): void => { | ||
182 | - const resources = deepClone(instance.get(RESOURCES).value as ResourceLwM2M[]); | ||
183 | - resources.forEach(resource => resource[type] = value); | ||
184 | - instance.get(RESOURCES).patchValue(resources); | ||
185 | - this.propagateChange(this.observeAttrTelemetryFormGroup.value); | ||
186 | - } | ||
187 | - | ||
188 | - private updateValidators = (): void => { | ||
189 | - this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M).setValidators(this.required ? Validators.required : []); | ||
190 | - this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M).updateValueAndValidity(); | 140 | + this.modelsFormGroup.setControl('models', this.fb.array(modelControls)); |
141 | + if (this.disabled) { | ||
142 | + this.modelsFormGroup.disable({emitEvent: false}); | ||
143 | + } | ||
144 | + this.valueChange$ = this.modelsFormGroup.valueChanges.subscribe(value => { | ||
145 | + this.updateModel(value.models); | ||
146 | + }); | ||
147 | + } | ||
191 | } | 148 | } |
192 | 149 | ||
193 | - trackByParams = (index: number, element: any): number => { | ||
194 | - return index; | 150 | + private createModelFormGroup(objectLwM2M: ObjectLwM2M): FormGroup { |
151 | + return this.fb.group({ | ||
152 | + id: [objectLwM2M.id], | ||
153 | + keyId: [objectLwM2M.keyId], | ||
154 | + name: [objectLwM2M.name], | ||
155 | + multiple: [objectLwM2M.multiple], | ||
156 | + mandatory: [objectLwM2M.mandatory], | ||
157 | + attributes: [objectLwM2M.attributes], | ||
158 | + instances: [objectLwM2M.instances] | ||
159 | + }); | ||
195 | } | 160 | } |
196 | 161 | ||
197 | - getIndeterminate = (instance: AbstractControl, type: string): boolean => { | ||
198 | - const resources = instance.get(RESOURCES).value as ResourceLwM2M[]; | ||
199 | - if (isDefinedAndNotNull(resources)) { | ||
200 | - const checkedResource = resources.filter(resource => resource[type]); | ||
201 | - return checkedResource.length !== 0 && checkedResource.length !== resources.length; | 162 | + private updateModel = (value: ObjectLwM2M[]): void => { |
163 | + if (value && this.modelsFormGroup.valid) { | ||
164 | + this.propagateChange(value); | ||
165 | + } else { | ||
166 | + this.propagateChange(null); | ||
202 | } | 167 | } |
203 | - return false; | ||
204 | } | 168 | } |
205 | 169 | ||
206 | - getChecked = (instance: AbstractControl, type: string): boolean => { | ||
207 | - const resources = instance.get(RESOURCES).value as ResourceLwM2M[]; | ||
208 | - return isDefinedAndNotNull(resources) && resources.every(resource => resource[type]); | 170 | + private updateValidators = (): void => { |
171 | + this.modelsFormArray.setValidators(this.required ? Validators.required : []); | ||
172 | + this.modelsFormArray.updateValueAndValidity(); | ||
209 | } | 173 | } |
210 | 174 | ||
211 | - getExpended = (objectLwM2M: AbstractControl): boolean => { | ||
212 | - return this.instancesLwm2mFormArray(objectLwM2M).length === 1; | 175 | + trackByParams = (index: number, objectLwM2M: ObjectLwM2M): number => { |
176 | + return objectLwM2M.id; | ||
213 | } | 177 | } |
214 | 178 | ||
215 | /** | 179 | /** |
@@ -230,137 +194,76 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor | @@ -230,137 +194,76 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor | ||
230 | * Object Instance ID cnt_max = cnt_min = 1 (всегда есть один) | 194 | * Object Instance ID cnt_max = cnt_min = 1 (всегда есть один) |
231 | */ | 195 | */ |
232 | 196 | ||
233 | - addInstances = ($event: Event, object: ObjectLwM2M): void => { | 197 | + addInstances = ($event: Event, control: AbstractControl): void => { |
234 | if ($event) { | 198 | if ($event) { |
235 | $event.stopPropagation(); | 199 | $event.stopPropagation(); |
236 | $event.preventDefault(); | 200 | $event.preventDefault(); |
237 | } | 201 | } |
238 | - this.dialog.open<Lwm2mObjectAddInstancesDialogComponent, Lwm2mObjectAddInstancesData, object>(Lwm2mObjectAddInstancesDialogComponent, { | 202 | + const object: ObjectLwM2M = control.value; |
203 | + const instancesId: Set<number> = this.instancesToSetId(object.instances); | ||
204 | + this.dialog.open<Lwm2mObjectAddInstancesDialogComponent, Lwm2mObjectAddInstancesData>(Lwm2mObjectAddInstancesDialogComponent, { | ||
239 | disableClose: true, | 205 | disableClose: true, |
240 | panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], | 206 | panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], |
241 | data: { | 207 | data: { |
242 | - instancesIds: this.instancesToSetId(object.instances), | 208 | + instancesId: new Set(instancesId), |
243 | objectName: object.name, | 209 | objectName: object.name, |
244 | - objectKeyId: object.keyId | 210 | + objectId: object.id |
245 | } | 211 | } |
246 | }).afterClosed().subscribe( | 212 | }).afterClosed().subscribe( |
247 | - (res: Lwm2mObjectAddInstancesData | undefined) => { | 213 | + (res: Set<number> | null) => { |
248 | if (isDefinedAndNotNull(res)) { | 214 | if (isDefinedAndNotNull(res)) { |
249 | - this.updateInstancesIds(res); | 215 | + this.updateInstancesIds(res, control, instancesId); |
250 | } | 216 | } |
251 | } | 217 | } |
252 | ); | 218 | ); |
253 | } | 219 | } |
254 | 220 | ||
255 | - private updateInstancesIds = (data: Lwm2mObjectAddInstancesData): void => { | ||
256 | - const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls | ||
257 | - .find(e => e.value.keyId === data.objectKeyId) as FormGroup; | ||
258 | - const instancesArray = objectLwM2MFormGroup.value.instances as Instance []; | ||
259 | - const instancesFormArray = objectLwM2MFormGroup.get(INSTANCES) as FormArray; | ||
260 | - const instance0 = deepClone(instancesFormArray.at(0).value as Instance); | ||
261 | - instance0.resources.forEach(r => { | ||
262 | - r.attribute = false; | ||
263 | - r.telemetry = false; | ||
264 | - r.observe = false; | ||
265 | - r.attributeLwm2m = {}; | 221 | + private updateInstancesIds(instancesId: Set<number>, control: AbstractControl, prevInstancesId: Set<number>) { |
222 | + let instancesValue: Instance[] = control.get('instances').value; | ||
223 | + const instanceTemplate = deepClone(instancesValue[0]); | ||
224 | + instanceTemplate.attributes = {}; | ||
225 | + instanceTemplate.resources.forEach(resource => { | ||
226 | + resource.attribute = false; | ||
227 | + resource.telemetry = false; | ||
228 | + resource.observe = false; | ||
229 | + resource.keyName = _.camelCase(resource.name); | ||
230 | + resource.attributes = {}; | ||
266 | }); | 231 | }); |
267 | - const valueOld = this.instancesToSetId(instancesArray); | ||
268 | - if (!isEqual(valueOld, data.instancesIds)) { | ||
269 | - const idsDel = this.diffBetweenSet(valueOld, data.instancesIds); | ||
270 | - const idsAdd = this.diffBetweenSet(data.instancesIds, valueOld); | 232 | + if (!isEqual(prevInstancesId, instancesId)) { |
233 | + const idsDel = this.diffBetweenSet(prevInstancesId, instancesId); | ||
234 | + const idsAdd = this.diffBetweenSet(instancesId, prevInstancesId); | ||
271 | if (idsAdd.size) { | 235 | if (idsAdd.size) { |
272 | - this.addInstancesNew(idsAdd, objectLwM2MFormGroup, instancesFormArray, instance0); | 236 | + idsAdd.forEach(id => { |
237 | + const template = deepClone(instanceTemplate); | ||
238 | + template.resources.forEach(resource => resource.keyName += id); | ||
239 | + template.id = id; | ||
240 | + instancesValue.push(template); | ||
241 | + }); | ||
273 | } | 242 | } |
274 | if (idsDel.size) { | 243 | if (idsDel.size) { |
275 | - this.deleteInstances(idsDel, objectLwM2MFormGroup, instancesFormArray, instance0); | 244 | + instancesValue = instancesValue.filter(instance => !idsDel.has(instance.id)); |
245 | + if (instancesValue.length === 0) { | ||
246 | + instanceTemplate.id = 0; | ||
247 | + instancesValue.push(instanceTemplate); | ||
248 | + } | ||
249 | + } | ||
250 | + if (idsAdd.size || idsDel.size) { | ||
251 | + instancesValue.sort((a, b) => a.id - b.id); | ||
252 | + control.get('instances').patchValue(instancesValue); | ||
253 | + this.cd.markForCheck(); | ||
276 | } | 254 | } |
277 | } | 255 | } |
278 | } | 256 | } |
279 | 257 | ||
280 | - private addInstancesNew = (idsAdd: Set<number>, objectLwM2MFormGroup: FormGroup, instancesFormArray: FormArray, | ||
281 | - instanceNew: Instance): void => { | ||
282 | - idsAdd.forEach(x => { | ||
283 | - instanceNew.resources.forEach(resource => {resource.keyName = _.camelCase(resource.name + x);}); | ||
284 | - this.pushInstance(instancesFormArray, x, deepClone(instanceNew as Instance)); | ||
285 | - }); | ||
286 | - (instancesFormArray.controls as FormGroup[]).sort((a, b) => a.value.id - b.value.id); | ||
287 | - } | ||
288 | - | ||
289 | - private deleteInstances = (idsDel: Set<number>, objectLwM2MFormGroup: FormGroup, instancesFormArray: FormArray, | ||
290 | - instance0: Instance): void => { | ||
291 | - idsDel.forEach(x => { | ||
292 | - const instanceIndex = instancesFormArray.value.findIndex(element => element.id === x); | ||
293 | - instancesFormArray.removeAt(instanceIndex); | ||
294 | - }); | ||
295 | - if (instancesFormArray.length === 0) { | ||
296 | - this.pushInstance(instancesFormArray, 0, instance0); | ||
297 | - } | ||
298 | - (instancesFormArray.controls as FormGroup[]).sort((a, b) => a.value.id - b.value.id); | ||
299 | - } | ||
300 | - | ||
301 | - private pushInstance = (instancesFormArray: FormArray, x: number, instanceNew: Instance): void => { | ||
302 | - instancesFormArray.push(this.fb.group({ | ||
303 | - id: x, | ||
304 | - attributeLwm2m: instanceNew.attributeLwm2m, | ||
305 | - resources: {value: instanceNew.resources, disabled: this.disabled} | ||
306 | - })); | ||
307 | - } | ||
308 | - | ||
309 | private diffBetweenSet<T>(firstSet: Set<T>, secondSet: Set<T>): Set<T> { | 258 | private diffBetweenSet<T>(firstSet: Set<T>, secondSet: Set<T>): Set<T> { |
310 | return new Set([...Array.from(firstSet)].filter(x => !secondSet.has(x))); | 259 | return new Set([...Array.from(firstSet)].filter(x => !secondSet.has(x))); |
311 | } | 260 | } |
312 | 261 | ||
313 | - private instancesToSetId = (instances: Instance[]): Set<number> => { | 262 | + private instancesToSetId(instances: Instance[]): Set<number> { |
314 | return new Set(instances.map(x => x.id)); | 263 | return new Set(instances.map(x => x.id)); |
315 | } | 264 | } |
316 | 265 | ||
317 | - getNameObjectLwm2m = (objectName: string, idVerObj: string): string => { | ||
318 | - return objectName + ' <' + idVerObj + '>'; | ||
319 | - } | ||
320 | - getNameInstanceLwm2m = (instance: Instance, idVerObj: string): string => { | ||
321 | - return ' instance <' + idVerObj + '/' + instance.id +'>'; | ||
322 | - } | ||
323 | - | ||
324 | - updateAttributeLwm2mObject = (event: Event, objectKeyId: number): void => { | ||
325 | - const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls | ||
326 | - .find(e => e.value.keyId === objectKeyId) as FormGroup; | ||
327 | - objectLwM2MFormGroup.patchValue({attributeLwm2m: event}); | ||
328 | - } | ||
329 | - | ||
330 | - updateAttributeLwm2mInstance = (event: Event, indexInstance: number, objectKeyId: number): void => { | ||
331 | - | ||
332 | - const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls | ||
333 | - .find(e => e.value.keyId === objectKeyId) as FormGroup; | ||
334 | - const instancesFormArray = objectLwM2MFormGroup.get(INSTANCES) as FormArray; | ||
335 | - instancesFormArray.at(indexInstance).patchValue({attributeLwm2m: event}); | ||
336 | - } | ||
337 | - | ||
338 | - disableObserveInstance = (instance: AbstractControl): boolean => { | ||
339 | - const checkedAttrTelemetry = this.observeInstance(instance); | ||
340 | - if (checkedAttrTelemetry) { | ||
341 | - instance.get(ATTRIBUTE_LWM2M).patchValue(null); | ||
342 | - } | ||
343 | - return checkedAttrTelemetry; | ||
344 | - } | ||
345 | - | ||
346 | - disableObserveObject = (index: number): boolean => { | ||
347 | - const object = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).at(index) as FormGroup; | ||
348 | - const instances = object.controls.instances as FormArray; | ||
349 | - const checkedAttrTelemetry = instances.controls.filter(instance => !this.disableObserveInstance(instance)); | ||
350 | - if (checkedAttrTelemetry.length === 0) { | ||
351 | - object.controls.attributeLwm2m.patchValue(null); | ||
352 | - } | ||
353 | - return checkedAttrTelemetry.length === 0; | ||
354 | - } | ||
355 | - | ||
356 | - | ||
357 | - observeInstance = (instance: AbstractControl): boolean => { | ||
358 | - const resources = instance.get(RESOURCES).value as ResourceLwM2M[]; | ||
359 | - if (isDefinedAndNotNull(resources)) { | ||
360 | - const checkedAttribute = resources.filter(resource => resource[ATTRIBUTE]); | ||
361 | - const checkedTelemetry = resources.filter(resource => resource[TELEMETRY]); | ||
362 | - return checkedAttribute.length === 0 && checkedTelemetry.length === 0; | ||
363 | - } | ||
364 | - return false; | 266 | + getNameObject = (objectLwM2M: ObjectLwM2M): string => { |
267 | + return `${objectLwM2M.name} <${objectLwM2M.id}>`; | ||
365 | } | 268 | } |
366 | } | 269 | } |
@@ -18,7 +18,7 @@ import { NgModule } from '@angular/core'; | @@ -18,7 +18,7 @@ import { NgModule } from '@angular/core'; | ||
18 | import { Lwm2mDeviceProfileTransportConfigurationComponent } from './lwm2m-device-profile-transport-configuration.component'; | 18 | import { Lwm2mDeviceProfileTransportConfigurationComponent } from './lwm2m-device-profile-transport-configuration.component'; |
19 | import { Lwm2mObjectListComponent } from './lwm2m-object-list.component'; | 19 | import { Lwm2mObjectListComponent } from './lwm2m-object-list.component'; |
20 | import { Lwm2mObserveAttrTelemetryComponent } from './lwm2m-observe-attr-telemetry.component'; | 20 | import { Lwm2mObserveAttrTelemetryComponent } from './lwm2m-observe-attr-telemetry.component'; |
21 | -import { Lwm2mObserveAttrTelemetryResourceComponent } from './lwm2m-observe-attr-telemetry-resource.component'; | 21 | +import { Lwm2mObserveAttrTelemetryResourcesComponent } from './lwm2m-observe-attr-telemetry-resources.component'; |
22 | import { Lwm2mAttributesDialogComponent } from './lwm2m-attributes-dialog.component'; | 22 | import { Lwm2mAttributesDialogComponent } from './lwm2m-attributes-dialog.component'; |
23 | import { Lwm2mAttributesComponent } from './lwm2m-attributes.component'; | 23 | import { Lwm2mAttributesComponent } from './lwm2m-attributes.component'; |
24 | import { Lwm2mAttributesKeyListComponent } from './lwm2m-attributes-key-list.component'; | 24 | import { Lwm2mAttributesKeyListComponent } from './lwm2m-attributes-key-list.component'; |
@@ -27,6 +27,7 @@ import { Lwm2mObjectAddInstancesDialogComponent } from './lwm2m-object-add-insta | @@ -27,6 +27,7 @@ import { Lwm2mObjectAddInstancesDialogComponent } from './lwm2m-object-add-insta | ||
27 | import { Lwm2mObjectAddInstancesListComponent } from './lwm2m-object-add-instances-list.component'; | 27 | import { Lwm2mObjectAddInstancesListComponent } from './lwm2m-object-add-instances-list.component'; |
28 | import { CommonModule } from '@angular/common'; | 28 | import { CommonModule } from '@angular/common'; |
29 | import { SharedModule } from '@app/shared/shared.module'; | 29 | import { SharedModule } from '@app/shared/shared.module'; |
30 | +import { Lwm2mObserveAttrTelemetryInstancesComponent } from './lwm2m-observe-attr-telemetry-instances.component'; | ||
30 | 31 | ||
31 | @NgModule({ | 32 | @NgModule({ |
32 | declarations: | 33 | declarations: |
@@ -34,13 +35,14 @@ import { SharedModule } from '@app/shared/shared.module'; | @@ -34,13 +35,14 @@ import { SharedModule } from '@app/shared/shared.module'; | ||
34 | Lwm2mDeviceProfileTransportConfigurationComponent, | 35 | Lwm2mDeviceProfileTransportConfigurationComponent, |
35 | Lwm2mObjectListComponent, | 36 | Lwm2mObjectListComponent, |
36 | Lwm2mObserveAttrTelemetryComponent, | 37 | Lwm2mObserveAttrTelemetryComponent, |
37 | - Lwm2mObserveAttrTelemetryResourceComponent, | 38 | + Lwm2mObserveAttrTelemetryResourcesComponent, |
38 | Lwm2mAttributesDialogComponent, | 39 | Lwm2mAttributesDialogComponent, |
39 | Lwm2mAttributesComponent, | 40 | Lwm2mAttributesComponent, |
40 | Lwm2mAttributesKeyListComponent, | 41 | Lwm2mAttributesKeyListComponent, |
41 | Lwm2mDeviceConfigServerComponent, | 42 | Lwm2mDeviceConfigServerComponent, |
42 | Lwm2mObjectAddInstancesDialogComponent, | 43 | Lwm2mObjectAddInstancesDialogComponent, |
43 | - Lwm2mObjectAddInstancesListComponent | 44 | + Lwm2mObjectAddInstancesListComponent, |
45 | + Lwm2mObserveAttrTelemetryInstancesComponent | ||
44 | ], | 46 | ], |
45 | imports: [ | 47 | imports: [ |
46 | CommonModule, | 48 | CommonModule, |
@@ -50,13 +52,14 @@ import { SharedModule } from '@app/shared/shared.module'; | @@ -50,13 +52,14 @@ import { SharedModule } from '@app/shared/shared.module'; | ||
50 | Lwm2mDeviceProfileTransportConfigurationComponent, | 52 | Lwm2mDeviceProfileTransportConfigurationComponent, |
51 | Lwm2mObjectListComponent, | 53 | Lwm2mObjectListComponent, |
52 | Lwm2mObserveAttrTelemetryComponent, | 54 | Lwm2mObserveAttrTelemetryComponent, |
53 | - Lwm2mObserveAttrTelemetryResourceComponent, | 55 | + Lwm2mObserveAttrTelemetryResourcesComponent, |
54 | Lwm2mAttributesDialogComponent, | 56 | Lwm2mAttributesDialogComponent, |
55 | Lwm2mAttributesComponent, | 57 | Lwm2mAttributesComponent, |
56 | Lwm2mAttributesKeyListComponent, | 58 | Lwm2mAttributesKeyListComponent, |
57 | Lwm2mDeviceConfigServerComponent, | 59 | Lwm2mDeviceConfigServerComponent, |
58 | Lwm2mObjectAddInstancesDialogComponent, | 60 | Lwm2mObjectAddInstancesDialogComponent, |
59 | - Lwm2mObjectAddInstancesListComponent | 61 | + Lwm2mObjectAddInstancesListComponent, |
62 | + Lwm2mObserveAttrTelemetryInstancesComponent | ||
60 | ], | 63 | ], |
61 | providers: [ | 64 | providers: [ |
62 | ] | 65 | ] |
@@ -20,8 +20,6 @@ export const PAGE_SIZE_LIMIT = 50; | @@ -20,8 +20,6 @@ export const PAGE_SIZE_LIMIT = 50; | ||
20 | export const INSTANCES = 'instances'; | 20 | export const INSTANCES = 'instances'; |
21 | export const INSTANCE = 'instance'; | 21 | export const INSTANCE = 'instance'; |
22 | export const RESOURCES = 'resources'; | 22 | export const RESOURCES = 'resources'; |
23 | -export const ATTRIBUTE_LWM2M = 'attributeLwm2m'; | ||
24 | -export const CLIENT_LWM2M = 'clientLwM2M'; | ||
25 | export const OBSERVE_ATTR_TELEMETRY = 'observeAttrTelemetry'; | 23 | export const OBSERVE_ATTR_TELEMETRY = 'observeAttrTelemetry'; |
26 | export const OBSERVE = 'observe'; | 24 | export const OBSERVE = 'observe'; |
27 | export const ATTRIBUTE = 'attribute'; | 25 | export const ATTRIBUTE = 'attribute'; |
@@ -184,7 +182,7 @@ export interface ObservableAttributes { | @@ -184,7 +182,7 @@ export interface ObservableAttributes { | ||
184 | attribute: string[]; | 182 | attribute: string[]; |
185 | telemetry: string[]; | 183 | telemetry: string[]; |
186 | keyName: {}; | 184 | keyName: {}; |
187 | - attributeLwm2m?: AttributesNameValueMap; | 185 | + attributeLwm2m?: AttributesNameValueMap[]; |
188 | } | 186 | } |
189 | 187 | ||
190 | export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig { | 188 | export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig { |
@@ -222,7 +220,7 @@ export function getDefaultProfileObserveAttrConfig(): ObservableAttributes { | @@ -222,7 +220,7 @@ export function getDefaultProfileObserveAttrConfig(): ObservableAttributes { | ||
222 | attribute: [], | 220 | attribute: [], |
223 | telemetry: [], | 221 | telemetry: [], |
224 | keyName: {}, | 222 | keyName: {}, |
225 | - attributeLwm2m: {} | 223 | + attributeLwm2m: [] |
226 | }; | 224 | }; |
227 | } | 225 | } |
228 | 226 | ||
@@ -237,6 +235,8 @@ export function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSetting | @@ -237,6 +235,8 @@ export function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSetting | ||
237 | }; | 235 | }; |
238 | } | 236 | } |
239 | 237 | ||
238 | +export type ResourceSettingTelemetry = 'observe' | 'attribute' | 'telemetry'; | ||
239 | + | ||
240 | export interface ResourceLwM2M { | 240 | export interface ResourceLwM2M { |
241 | id: number; | 241 | id: number; |
242 | name: string; | 242 | name: string; |
@@ -244,12 +244,12 @@ export interface ResourceLwM2M { | @@ -244,12 +244,12 @@ export interface ResourceLwM2M { | ||
244 | attribute: boolean; | 244 | attribute: boolean; |
245 | telemetry: boolean; | 245 | telemetry: boolean; |
246 | keyName: string; | 246 | keyName: string; |
247 | - attributeLwm2m?: AttributesNameValueMap; | 247 | + attributes?: AttributesNameValueMap; |
248 | } | 248 | } |
249 | 249 | ||
250 | export interface Instance { | 250 | export interface Instance { |
251 | id: number; | 251 | id: number; |
252 | - attributeLwm2m?: AttributesNameValueMap; | 252 | + attributes?: AttributesNameValueMap; |
253 | resources: ResourceLwM2M[]; | 253 | resources: ResourceLwM2M[]; |
254 | } | 254 | } |
255 | 255 | ||
@@ -265,7 +265,7 @@ export interface ObjectLwM2M { | @@ -265,7 +265,7 @@ export interface ObjectLwM2M { | ||
265 | name: string; | 265 | name: string; |
266 | multiple?: boolean; | 266 | multiple?: boolean; |
267 | mandatory?: boolean; | 267 | mandatory?: boolean; |
268 | - attributeLwm2m?: AttributesNameValueMap; | 268 | + attributes?: AttributesNameValueMap; |
269 | instances?: Instance []; | 269 | instances?: Instance []; |
270 | } | 270 | } |
271 | 271 |
@@ -1228,25 +1228,20 @@ | @@ -1228,25 +1228,20 @@ | ||
1228 | "valid-id-instance-no-max": "Instance number '{{instance}}' no validated. Max value='{{max}}'", | 1228 | "valid-id-instance-no-max": "Instance number '{{instance}}' no validated. Max value='{{max}}'", |
1229 | "valid-id-instance": "Instance number '{{instance}}' no validated. { count, plural, 1 {Max value='{{max}}'} 2 {Min value='{{min}}'} other {Must be only number} }", | 1229 | "valid-id-instance": "Instance number '{{instance}}' no validated. { count, plural, 1 {Max value='{{max}}'} 2 {Min value='{{min}}'} other {Must be only number} }", |
1230 | "model-tab": "LWM2M Model", | 1230 | "model-tab": "LWM2M Model", |
1231 | - "add-instances-tip": "Add new instances", | 1231 | + "add-new-instances": "Add new instances", |
1232 | "instances-list": "Instances list", | 1232 | "instances-list": "Instances list", |
1233 | "instances-input": "Input Instance Id value", | 1233 | "instances-input": "Input Instance Id value", |
1234 | "instances-input-holder": "Input Instance number...", | 1234 | "instances-input-holder": "Input Instance number...", |
1235 | - "instance-label": "Instance", | 1235 | + "instance": "Instance", |
1236 | "resource-label": "<id> Resource name", | 1236 | "resource-label": "<id> Resource name", |
1237 | "observe-label": "Observe", | 1237 | "observe-label": "Observe", |
1238 | "attribute-label": "Attribute", | 1238 | "attribute-label": "Attribute", |
1239 | "telemetry-label": "Telemetry", | 1239 | "telemetry-label": "Telemetry", |
1240 | - "key-name-label": "Key Name", | ||
1241 | - "resource-tip": "ID & Original Name of the Resource (only Operations isReadable)", | ||
1242 | - "is-observe-tip": "Is Observe", | ||
1243 | - "not-observe-tip": "To observe select telemetry or attributes first", | ||
1244 | - "is-attr-tip": "Is Attribute", | ||
1245 | - "is-telemetry-tip": "Is Telemetry", | ||
1246 | - "key-name-tip": "Key Name in Camel format", | ||
1247 | - "edit-attributes-select": "To edit attributes select telemetry or attributes", | 1240 | + "edit-observe-select": "To edit observe select telemetry or attribute", |
1241 | + "edit-attributes-select": "To edit attributes select telemetry or attribute", | ||
1248 | "no-attributes-set": "No attributes set", | 1242 | "no-attributes-set": "No attributes set", |
1249 | - "key-name": "Key Name", | 1243 | + "key-name": "Key name", |
1244 | + "key-name-required": "Key name is required", | ||
1250 | "attribute-name": "Name attribute", | 1245 | "attribute-name": "Name attribute", |
1251 | "attribute-name-required": "Name attribute is required.", | 1246 | "attribute-name-required": "Name attribute is required.", |
1252 | "attribute-value": "Attribute value", | 1247 | "attribute-value": "Attribute value", |