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 | 34 | @Component({ |
35 | 35 | selector: 'tb-lwm2m-attributes-dialog', |
36 | 36 | templateUrl: './lwm2m-attributes-dialog.component.html', |
37 | - styleUrls: ['./lwm2m-attributes.component.scss'], | |
37 | + styleUrls: [], | |
38 | 38 | providers: [{provide: ErrorStateMatcher, useExisting: Lwm2mAttributesDialogComponent}], |
39 | 39 | }) |
40 | 40 | export class Lwm2mAttributesDialogComponent | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 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 | 19 | <button type="button" |
20 | 20 | [disabled]="isDisableBtn()" |
21 | 21 | mat-button mat-icon-button | ... | ... |
... | ... | @@ -14,29 +14,31 @@ |
14 | 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 | 18 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; |
19 | 19 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
20 | 20 | import { isEmpty, isUndefinedOrNull } from '@core/utils'; |
21 | 21 | import { Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData } from './lwm2m-attributes-dialog.component'; |
22 | 22 | import { MatDialog } from '@angular/material/dialog'; |
23 | 23 | import { AttributesNameValueMap } from './lwm2m-profile-config.models'; |
24 | - | |
24 | +import { Subject } from 'rxjs'; | |
25 | +import { takeUntil } from 'rxjs/operators'; | |
25 | 26 | |
26 | 27 | @Component({ |
27 | 28 | selector: 'tb-profile-lwm2m-attributes', |
28 | 29 | templateUrl: './lwm2m-attributes.component.html', |
29 | - styleUrls: ['./lwm2m-attributes.component.scss'], | |
30 | + styleUrls: [], | |
30 | 31 | providers: [{ |
31 | 32 | provide: NG_VALUE_ACCESSOR, |
32 | 33 | useExisting: forwardRef(() => Lwm2mAttributesComponent), |
33 | 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 | 40 | private requiredValue: boolean; |
41 | + private destroy$ = new Subject(); | |
40 | 42 | |
41 | 43 | @Input() |
42 | 44 | isAttributeTelemetry: boolean; |
... | ... | @@ -50,9 +52,6 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { |
50 | 52 | @Input() |
51 | 53 | isResource = false; |
52 | 54 | |
53 | - @Output() | |
54 | - updateAttributeLwm2m = new EventEmitter<any>(); | |
55 | - | |
56 | 55 | @Input() |
57 | 56 | set required(value: boolean) { |
58 | 57 | this.requiredValue = coerceBooleanProperty(value); |
... | ... | @@ -62,7 +61,21 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { |
62 | 61 | } |
63 | 62 | |
64 | 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 | 80 | registerOnChange(fn: any): void { |
68 | 81 | this.propagateChange = fn; |
... | ... | @@ -74,24 +87,18 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { |
74 | 87 | setDisabledState(isDisabled: boolean): void { |
75 | 88 | this.disabled = isDisabled; |
76 | 89 | if (isDisabled) { |
77 | - this.attributeLwm2mFormGroup.disable({emitEvent: false}); | |
90 | + this.attributesFormGroup.disable({emitEvent: false}); | |
78 | 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 | 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 | 100 | get attributesValueMap(): AttributesNameValueMap { |
94 | - return this.attributeLwm2mFormGroup.get('attributes').value; | |
101 | + return this.attributesFormGroup.get('attributes').value; | |
95 | 102 | } |
96 | 103 | |
97 | 104 | isDisableBtn(): boolean { |
... | ... | @@ -140,8 +147,7 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor { |
140 | 147 | } |
141 | 148 | }).afterClosed().subscribe((result) => { |
142 | 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 | 287 | |
288 | 288 | private updateDeviceProfileValue(config): void { |
289 | 289 | if (this.lwm2mDeviceProfileFormGroup.valid) { |
290 | - this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M); | |
290 | + this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry); | |
291 | 291 | } |
292 | 292 | this.configurationValue.bootstrap.bootstrapServer = config.bootstrap.bootstrapServer; |
293 | 293 | this.configurationValue.bootstrap.lwm2mServer = config.bootstrap.lwm2mServer; |
... | ... | @@ -297,7 +297,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro |
297 | 297 | this.updateModel(); |
298 | 298 | } |
299 | 299 | |
300 | - private getObserveAttrTelemetryObjects = (objectList: ObjectLwM2M[]): object => { | |
300 | + private getObserveAttrTelemetryObjects = (objectList: ObjectLwM2M[]): ObjectLwM2M[] => { | |
301 | 301 | const objectLwM2MS = deepClone(objectList); |
302 | 302 | if (this.configurationValue.observeAttr && objectLwM2MS.length > 0) { |
303 | 303 | const attributeArray = this.configurationValue.observeAttr.attribute; |
... | ... | @@ -317,7 +317,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro |
317 | 317 | this.updateObserveAttrTelemetryObjects(telemetryArray, objectLwM2MS, TELEMETRY); |
318 | 318 | } |
319 | 319 | if (isDefinedAndNotNull(this.configurationValue.observeAttr.attributeLwm2m)) { |
320 | - this.updateAttributeLwm2m(objectLwM2MS); | |
320 | + this.updateAttributes(objectLwM2MS); | |
321 | 321 | } |
322 | 322 | if (isDefinedAndNotNull(keyNameJson)) { |
323 | 323 | this.configurationValue.observeAttr.keyName = this.validateKeyNameObjects(keyNameJson, attributeArray, telemetryArray); |
... | ... | @@ -325,7 +325,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro |
325 | 325 | this.updateKeyNameObjects(objectLwM2MS); |
326 | 326 | } |
327 | 327 | } |
328 | - return {clientLwM2M: objectLwM2MS}; | |
328 | + return objectLwM2MS; | |
329 | 329 | } |
330 | 330 | |
331 | 331 | private includesNotZeroInstance = (attribute: string[], telemetry: string[]): boolean => { |
... | ... | @@ -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 | 373 | Object.keys(this.configurationValue.observeAttr.attributeLwm2m).forEach(key => { |
374 | 374 | const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String); |
375 | 375 | const objectLwM2M = objectLwM2MS.find(objectLwm2m => objectLwm2m.keyId === objectKeyId); |
... | ... | @@ -377,12 +377,12 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro |
377 | 377 | const instance = objectLwM2M.instances.find(obj => obj.id === +instanceId); |
378 | 378 | if (instance && resourceId) { |
379 | 379 | instance.resources.find(resource => resource.id === +resourceId) |
380 | - .attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key]; | |
380 | + .attributes = this.configurationValue.observeAttr.attributeLwm2m[key]; | |
381 | 381 | } else if (instance) { |
382 | - instance.attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key]; | |
382 | + instance.attributes = this.configurationValue.observeAttr.attributeLwm2m[key]; | |
383 | 383 | } |
384 | 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 | 415 | const observeArray: Array<string> = []; |
416 | 416 | const attributeArray: Array<string> = []; |
417 | 417 | const telemetryArray: Array<string> = []; |
418 | - const attributeLwm2m: any = {}; | |
418 | + const attributes: any = {}; | |
419 | 419 | const keyNameNew = {}; |
420 | 420 | const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val)); |
421 | 421 | observeJson.forEach(obj => { |
422 | - if (isDefinedAndNotNull(obj.attributeLwm2m) && !isEmpty(obj.attributeLwm2m)) { | |
422 | + if (isDefinedAndNotNull(obj.attributes) && !isEmpty(obj.attributes)) { | |
423 | 423 | const pathObject = `/${obj.keyId}`; |
424 | - attributeLwm2m[pathObject] = obj.attributeLwm2m; | |
424 | + attributes[pathObject] = obj.attributes; | |
425 | 425 | } |
426 | 426 | if (obj.hasOwnProperty(INSTANCES) && Array.isArray(obj.instances)) { |
427 | 427 | obj.instances.forEach(instance => { |
428 | - if (isDefinedAndNotNull(instance.attributeLwm2m) && !isEmpty(instance.attributeLwm2m)) { | |
428 | + if (isDefinedAndNotNull(instance.attributes) && !isEmpty(instance.attributes)) { | |
429 | 429 | const pathInstance = `/${obj.keyId}/${instance.id}`; |
430 | - attributeLwm2m[pathInstance] = instance.attributeLwm2m; | |
430 | + attributes[pathInstance] = instance.attributes; | |
431 | 431 | } |
432 | 432 | if (instance.hasOwnProperty(RESOURCES) && Array.isArray(instance.resources)) { |
433 | 433 | instance.resources.forEach(resource => { |
... | ... | @@ -443,8 +443,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro |
443 | 443 | telemetryArray.push(pathRes); |
444 | 444 | } |
445 | 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 | 458 | attribute: attributeArray, |
459 | 459 | telemetry: telemetryArray, |
460 | 460 | keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew), |
461 | - attributeLwm2m | |
461 | + attributeLwm2m: attributes | |
462 | 462 | }; |
463 | 463 | } else { |
464 | 464 | this.configurationValue.observeAttr.observe = observeArray; |
465 | 465 | this.configurationValue.observeAttr.attribute = attributeArray; |
466 | 466 | this.configurationValue.observeAttr.telemetry = telemetryArray; |
467 | 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 | 535 | this.removeObserveAttrTelemetryFromJson(TELEMETRY, value.keyId); |
536 | 536 | this.removeObserveAttrTelemetryFromJson(ATTRIBUTE, value.keyId); |
537 | 537 | this.removeKeyNameFromJson(value.keyId); |
538 | - this.removeAttributeLwm2mFromJson(value.keyId); | |
538 | + this.removeAttributesFromJson(value.keyId); | |
539 | 539 | this.updateObserveAttrTelemetryObjectFormGroup(objectsOld); |
540 | 540 | this.upDateJsonAllConfig(); |
541 | 541 | } |
... | ... | @@ -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 | 562 | const keyNameJson = this.configurationValue.observeAttr.attributeLwm2m; |
563 | 563 | Object.keys(keyNameJson).forEach(key => { |
564 | 564 | if (key.startsWith(`/${keyId}`)) { | ... | ... |
... | ... | @@ -17,7 +17,7 @@ |
17 | 17 | --> |
18 | 18 | <form [formGroup]="instancesFormGroup" (ngSubmit)="add()" style="min-width: 400px;"> |
19 | 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 | 21 | <span fxFlex></span> |
22 | 22 | <button mat-button mat-icon-button |
23 | 23 | (click)="cancel()" | ... | ... |
... | ... | @@ -23,9 +23,9 @@ import { Router } from '@angular/router'; |
23 | 23 | import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; |
24 | 24 | |
25 | 25 | export interface Lwm2mObjectAddInstancesData { |
26 | - instancesIds: Set<number>; | |
26 | + instancesId: Set<number>; | |
27 | 27 | objectName?: string; |
28 | - objectKeyId?: string; | |
28 | + objectId?: number; | |
29 | 29 | } |
30 | 30 | |
31 | 31 | @Component({ |
... | ... | @@ -48,16 +48,15 @@ export class Lwm2mObjectAddInstancesDialogComponent extends DialogComponent<Lwm2 |
48 | 48 | |
49 | 49 | ngOnInit(): void { |
50 | 50 | this.instancesFormGroup = this.fb.group({ |
51 | - instancesIds: this.data.instancesIds | |
51 | + instancesIds: [this.data.instancesId] | |
52 | 52 | }); |
53 | 53 | } |
54 | 54 | |
55 | 55 | cancel(): void { |
56 | - this.dialogRef.close(undefined); | |
56 | + this.dialogRef.close(null); | |
57 | 57 | } |
58 | 58 | |
59 | 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 | 13 | * See the License for the specific language governing permissions and |
14 | 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 | 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 | 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 | 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 | 73 | </div> |
90 | 74 | </div> |
91 | 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 | 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 | 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 | 23 | <mat-expansion-panel-header > |
24 | 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 | 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 | 35 | matTooltipPosition="above"> |
41 | - <mat-icon class="material-icons">{{'note_add'}}</mat-icon> | |
36 | + <mat-icon class="material-icons">note_add</mat-icon> | |
42 | 37 | </button> |
43 | - </mat-panel-description> | |
38 | + </mat-panel-title> | |
44 | 39 | </mat-expansion-panel-header> |
45 | 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 | 44 | </ng-template> |
120 | 45 | </mat-expansion-panel> |
121 | 46 | </mat-accordion> | ... | ... |
... | ... | @@ -13,23 +13,13 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 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 | 16 | :host{ |
27 | 17 | section { |
28 | 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 | 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 | 18 | import { |
19 | 19 | AbstractControl, |
20 | 20 | ControlValueAccessor, |
21 | 21 | FormArray, |
22 | 22 | FormBuilder, |
23 | 23 | FormGroup, |
24 | + NG_VALIDATORS, | |
24 | 25 | NG_VALUE_ACCESSOR, |
26 | + ValidationErrors, | |
27 | + Validator, | |
25 | 28 | Validators |
26 | 29 | } from '@angular/forms'; |
27 | -import { Store } from '@ngrx/store'; | |
28 | -import { AppState } from '@core/core.state'; | |
29 | 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 | 33 | import { MatDialog } from '@angular/material/dialog'; |
43 | -import { TranslateService } from '@ngx-translate/core'; | |
44 | 34 | import { |
45 | 35 | Lwm2mObjectAddInstancesData, |
46 | 36 | Lwm2mObjectAddInstancesDialogComponent |
47 | 37 | } from '@home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component'; |
48 | 38 | import _ from 'lodash'; |
39 | +import { Subscription } from 'rxjs'; | |
49 | 40 | |
50 | 41 | @Component({ |
51 | 42 | selector: 'tb-profile-lwm2m-observe-attr-telemetry', |
... | ... | @@ -56,18 +47,20 @@ import _ from 'lodash'; |
56 | 47 | provide: NG_VALUE_ACCESSOR, |
57 | 48 | useExisting: forwardRef(() => Lwm2mObserveAttrTelemetryComponent), |
58 | 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 | 64 | get required(): boolean { |
72 | 65 | return this.requiredValue; |
73 | 66 | } |
... | ... | @@ -84,132 +77,103 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor |
84 | 77 | @Input() |
85 | 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 | 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 | 97 | registerOnChange(fn: any): void { |
105 | 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 | 101 | registerOnTouched(fn: any): void { |
126 | 102 | } |
127 | 103 | |
128 | 104 | setDisabledState(isDisabled: boolean): void { |
129 | 105 | this.disabled = isDisabled; |
130 | - this.valuePrev = null; | |
131 | 106 | if (isDisabled) { |
132 | - this.observeAttrTelemetryFormGroup.disable(); | |
107 | + this.modelsFormGroup.disable({emitEvent: false}); | |
133 | 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 | 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 | 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 | 198 | if ($event) { |
235 | 199 | $event.stopPropagation(); |
236 | 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 | 205 | disableClose: true, |
240 | 206 | panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], |
241 | 207 | data: { |
242 | - instancesIds: this.instancesToSetId(object.instances), | |
208 | + instancesId: new Set(instancesId), | |
243 | 209 | objectName: object.name, |
244 | - objectKeyId: object.keyId | |
210 | + objectId: object.id | |
245 | 211 | } |
246 | 212 | }).afterClosed().subscribe( |
247 | - (res: Lwm2mObjectAddInstancesData | undefined) => { | |
213 | + (res: Set<number> | null) => { | |
248 | 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 | 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 | 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 | 258 | private diffBetweenSet<T>(firstSet: Set<T>, secondSet: Set<T>): Set<T> { |
310 | 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 | 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 | 18 | import { Lwm2mDeviceProfileTransportConfigurationComponent } from './lwm2m-device-profile-transport-configuration.component'; |
19 | 19 | import { Lwm2mObjectListComponent } from './lwm2m-object-list.component'; |
20 | 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 | 22 | import { Lwm2mAttributesDialogComponent } from './lwm2m-attributes-dialog.component'; |
23 | 23 | import { Lwm2mAttributesComponent } from './lwm2m-attributes.component'; |
24 | 24 | import { Lwm2mAttributesKeyListComponent } from './lwm2m-attributes-key-list.component'; |
... | ... | @@ -27,6 +27,7 @@ import { Lwm2mObjectAddInstancesDialogComponent } from './lwm2m-object-add-insta |
27 | 27 | import { Lwm2mObjectAddInstancesListComponent } from './lwm2m-object-add-instances-list.component'; |
28 | 28 | import { CommonModule } from '@angular/common'; |
29 | 29 | import { SharedModule } from '@app/shared/shared.module'; |
30 | +import { Lwm2mObserveAttrTelemetryInstancesComponent } from './lwm2m-observe-attr-telemetry-instances.component'; | |
30 | 31 | |
31 | 32 | @NgModule({ |
32 | 33 | declarations: |
... | ... | @@ -34,13 +35,14 @@ import { SharedModule } from '@app/shared/shared.module'; |
34 | 35 | Lwm2mDeviceProfileTransportConfigurationComponent, |
35 | 36 | Lwm2mObjectListComponent, |
36 | 37 | Lwm2mObserveAttrTelemetryComponent, |
37 | - Lwm2mObserveAttrTelemetryResourceComponent, | |
38 | + Lwm2mObserveAttrTelemetryResourcesComponent, | |
38 | 39 | Lwm2mAttributesDialogComponent, |
39 | 40 | Lwm2mAttributesComponent, |
40 | 41 | Lwm2mAttributesKeyListComponent, |
41 | 42 | Lwm2mDeviceConfigServerComponent, |
42 | 43 | Lwm2mObjectAddInstancesDialogComponent, |
43 | - Lwm2mObjectAddInstancesListComponent | |
44 | + Lwm2mObjectAddInstancesListComponent, | |
45 | + Lwm2mObserveAttrTelemetryInstancesComponent | |
44 | 46 | ], |
45 | 47 | imports: [ |
46 | 48 | CommonModule, |
... | ... | @@ -50,13 +52,14 @@ import { SharedModule } from '@app/shared/shared.module'; |
50 | 52 | Lwm2mDeviceProfileTransportConfigurationComponent, |
51 | 53 | Lwm2mObjectListComponent, |
52 | 54 | Lwm2mObserveAttrTelemetryComponent, |
53 | - Lwm2mObserveAttrTelemetryResourceComponent, | |
55 | + Lwm2mObserveAttrTelemetryResourcesComponent, | |
54 | 56 | Lwm2mAttributesDialogComponent, |
55 | 57 | Lwm2mAttributesComponent, |
56 | 58 | Lwm2mAttributesKeyListComponent, |
57 | 59 | Lwm2mDeviceConfigServerComponent, |
58 | 60 | Lwm2mObjectAddInstancesDialogComponent, |
59 | - Lwm2mObjectAddInstancesListComponent | |
61 | + Lwm2mObjectAddInstancesListComponent, | |
62 | + Lwm2mObserveAttrTelemetryInstancesComponent | |
60 | 63 | ], |
61 | 64 | providers: [ |
62 | 65 | ] | ... | ... |
... | ... | @@ -20,8 +20,6 @@ export const PAGE_SIZE_LIMIT = 50; |
20 | 20 | export const INSTANCES = 'instances'; |
21 | 21 | export const INSTANCE = 'instance'; |
22 | 22 | export const RESOURCES = 'resources'; |
23 | -export const ATTRIBUTE_LWM2M = 'attributeLwm2m'; | |
24 | -export const CLIENT_LWM2M = 'clientLwM2M'; | |
25 | 23 | export const OBSERVE_ATTR_TELEMETRY = 'observeAttrTelemetry'; |
26 | 24 | export const OBSERVE = 'observe'; |
27 | 25 | export const ATTRIBUTE = 'attribute'; |
... | ... | @@ -184,7 +182,7 @@ export interface ObservableAttributes { |
184 | 182 | attribute: string[]; |
185 | 183 | telemetry: string[]; |
186 | 184 | keyName: {}; |
187 | - attributeLwm2m?: AttributesNameValueMap; | |
185 | + attributeLwm2m?: AttributesNameValueMap[]; | |
188 | 186 | } |
189 | 187 | |
190 | 188 | export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig { |
... | ... | @@ -222,7 +220,7 @@ export function getDefaultProfileObserveAttrConfig(): ObservableAttributes { |
222 | 220 | attribute: [], |
223 | 221 | telemetry: [], |
224 | 222 | keyName: {}, |
225 | - attributeLwm2m: {} | |
223 | + attributeLwm2m: [] | |
226 | 224 | }; |
227 | 225 | } |
228 | 226 | |
... | ... | @@ -237,6 +235,8 @@ export function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSetting |
237 | 235 | }; |
238 | 236 | } |
239 | 237 | |
238 | +export type ResourceSettingTelemetry = 'observe' | 'attribute' | 'telemetry'; | |
239 | + | |
240 | 240 | export interface ResourceLwM2M { |
241 | 241 | id: number; |
242 | 242 | name: string; |
... | ... | @@ -244,12 +244,12 @@ export interface ResourceLwM2M { |
244 | 244 | attribute: boolean; |
245 | 245 | telemetry: boolean; |
246 | 246 | keyName: string; |
247 | - attributeLwm2m?: AttributesNameValueMap; | |
247 | + attributes?: AttributesNameValueMap; | |
248 | 248 | } |
249 | 249 | |
250 | 250 | export interface Instance { |
251 | 251 | id: number; |
252 | - attributeLwm2m?: AttributesNameValueMap; | |
252 | + attributes?: AttributesNameValueMap; | |
253 | 253 | resources: ResourceLwM2M[]; |
254 | 254 | } |
255 | 255 | |
... | ... | @@ -265,7 +265,7 @@ export interface ObjectLwM2M { |
265 | 265 | name: string; |
266 | 266 | multiple?: boolean; |
267 | 267 | mandatory?: boolean; |
268 | - attributeLwm2m?: AttributesNameValueMap; | |
268 | + attributes?: AttributesNameValueMap; | |
269 | 269 | instances?: Instance []; |
270 | 270 | } |
271 | 271 | ... | ... |
... | ... | @@ -1228,25 +1228,20 @@ |
1228 | 1228 | "valid-id-instance-no-max": "Instance number '{{instance}}' no validated. Max value='{{max}}'", |
1229 | 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 | 1230 | "model-tab": "LWM2M Model", |
1231 | - "add-instances-tip": "Add new instances", | |
1231 | + "add-new-instances": "Add new instances", | |
1232 | 1232 | "instances-list": "Instances list", |
1233 | 1233 | "instances-input": "Input Instance Id value", |
1234 | 1234 | "instances-input-holder": "Input Instance number...", |
1235 | - "instance-label": "Instance", | |
1235 | + "instance": "Instance", | |
1236 | 1236 | "resource-label": "<id> Resource name", |
1237 | 1237 | "observe-label": "Observe", |
1238 | 1238 | "attribute-label": "Attribute", |
1239 | 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 | 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 | 1245 | "attribute-name": "Name attribute", |
1251 | 1246 | "attribute-name-required": "Name attribute is required.", |
1252 | 1247 | "attribute-value": "Attribute value", | ... | ... |