Commit c634859e906d866b09b571cab27e69abc89a1e57

Authored by nickAS21
Committed by GitHub
1 parent 1b531e28

lwm2m: front start add attributes Lwm2m (#4388)

* lwm2m: front start add attributes Lwm2m

* lwm2m: front finish add attributes Lwm2m for resources

* lwm2m: front add attributes Lwm2m for resources if isAttribte or isTelemetry

* lwm2m: front add attributes Lwm2m for objects if isAttribte or isTelemetry

* lwm2m: back add resource one

* lwm2m: back fix bug resource controller test
Showing 27 changed files with 1065 additions and 179 deletions
@@ -30,7 +30,6 @@ import org.springframework.web.bind.annotation.ResponseBody; @@ -30,7 +30,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
30 import org.springframework.web.bind.annotation.RestController; 30 import org.springframework.web.bind.annotation.RestController;
31 import org.thingsboard.server.common.data.TbResource; 31 import org.thingsboard.server.common.data.TbResource;
32 import org.thingsboard.server.common.data.TbResourceInfo; 32 import org.thingsboard.server.common.data.TbResourceInfo;
33 -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;  
34 import org.thingsboard.server.common.data.exception.ThingsboardException; 33 import org.thingsboard.server.common.data.exception.ThingsboardException;
35 import org.thingsboard.server.common.data.id.TbResourceId; 34 import org.thingsboard.server.common.data.id.TbResourceId;
36 import org.thingsboard.server.common.data.lwm2m.LwM2mObject; 35 import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
@@ -42,10 +41,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent; @@ -42,10 +41,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
42 import org.thingsboard.server.service.security.permission.Operation; 41 import org.thingsboard.server.service.security.permission.Operation;
43 import org.thingsboard.server.service.security.permission.Resource; 42 import org.thingsboard.server.service.security.permission.Resource;
44 43
45 -import java.util.ArrayList;  
46 import java.util.Base64; 44 import java.util.Base64;
47 import java.util.List; 45 import java.util.List;
48 -import java.util.StringJoiner;  
49 46
50 @Slf4j 47 @Slf4j
51 @RestController 48 @RestController
@@ -111,25 +108,13 @@ public class TbResourceController extends BaseController { @@ -111,25 +108,13 @@ public class TbResourceController extends BaseController {
111 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") 108 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
112 @RequestMapping(value = "/resource", method = RequestMethod.POST) 109 @RequestMapping(value = "/resource", method = RequestMethod.POST)
113 @ResponseBody 110 @ResponseBody
114 - public List<TbResource> saveResources(@RequestBody List<TbResource> resources) throws ThingsboardException { 111 + public TbResource saveResource(@RequestBody TbResource resource) throws ThingsboardException {
115 try { 112 try {
116 - List<TbResource> addResources = new ArrayList<>();  
117 - StringJoiner noSaveResources = new StringJoiner("; ");  
118 - resources.forEach(resource -> {  
119 - try {  
120 resource.setTenantId(getTenantId()); 113 resource.setTenantId(getTenantId());
121 checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); 114 checkEntity(resource.getId(), resource, Resource.TB_RESOURCE);
122 - addResources.add(addResource(resource));  
123 - } catch (Exception e) {  
124 - noSaveResources.add(resource.getFileName());  
125 - log.warn("Fail save resource: [{}]", resource.getFileName(), e); 115 + return addResource(resource);
126 } 116 }
127 - });  
128 - if (noSaveResources.length() > 0) {  
129 - throw new ThingsboardException(String.format("Fail save resource: %s", noSaveResources.toString()), ThingsboardErrorCode.INVALID_ARGUMENTS);  
130 - }  
131 - return addResources;  
132 - } catch (Exception e) { 117 + catch (Exception e) {
133 throw handleException(e); 118 throw handleException(e);
134 } 119 }
135 } 120 }
@@ -32,7 +32,6 @@ import org.thingsboard.server.common.data.security.Authority; @@ -32,7 +32,6 @@ import org.thingsboard.server.common.data.security.Authority;
32 import java.util.ArrayList; 32 import java.util.ArrayList;
33 import java.util.Collections; 33 import java.util.Collections;
34 import java.util.List; 34 import java.util.List;
35 -import java.util.stream.Collectors;  
36 35
37 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 36 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
38 37
@@ -110,7 +109,7 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes @@ -110,7 +109,7 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
110 TbResource savedResource = save(resource); 109 TbResource savedResource = save(resource);
111 110
112 loginDifferentTenant(); 111 loginDifferentTenant();
113 - doPostWithTypedResponse("/api/resource", Collections.singletonList(savedResource), new TypeReference<>(){}, status().isBadRequest()); 112 + doPostWithTypedResponse("/api/resource", savedResource, new TypeReference<>(){}, status().isForbidden());
114 deleteDifferentTenant(); 113 deleteDifferentTenant();
115 } 114 }
116 115
@@ -148,18 +147,15 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes @@ -148,18 +147,15 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
148 147
149 @Test 148 @Test
150 public void testFindTenantTbResources() throws Exception { 149 public void testFindTenantTbResources() throws Exception {
151 - List<TbResource> resourcesToSave = new ArrayList<>(); 150 + List<TbResourceInfo> resources = new ArrayList<>();
152 for (int i = 0; i < 173; i++) { 151 for (int i = 0; i < 173; i++) {
153 TbResource resource = new TbResource(); 152 TbResource resource = new TbResource();
154 resource.setTitle("Resource" + i); 153 resource.setTitle("Resource" + i);
155 resource.setResourceType(ResourceType.JKS); 154 resource.setResourceType(ResourceType.JKS);
156 resource.setFileName(i + DEFAULT_FILE_NAME); 155 resource.setFileName(i + DEFAULT_FILE_NAME);
157 resource.setData("Test Data"); 156 resource.setData("Test Data");
158 - resourcesToSave.add(resource); 157 + resources.add(new TbResourceInfo(save(resource)));
159 } 158 }
160 -  
161 - List<TbResourceInfo> resources =save(resourcesToSave).stream().map(TbResourceInfo::new).collect(Collectors.toList());  
162 -  
163 List<TbResourceInfo> loadedResources = new ArrayList<>(); 159 List<TbResourceInfo> loadedResources = new ArrayList<>();
164 PageLink pageLink = new PageLink(24); 160 PageLink pageLink = new PageLink(24);
165 PageData<TbResourceInfo> pageData; 161 PageData<TbResourceInfo> pageData;
@@ -287,10 +283,6 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes @@ -287,10 +283,6 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
287 } 283 }
288 284
289 private TbResource save(TbResource tbResource) throws Exception { 285 private TbResource save(TbResource tbResource) throws Exception {
290 - return save(Collections.singletonList(tbResource)).get(0);  
291 - }  
292 -  
293 - private List<TbResource> save(List<TbResource> tbResources) throws Exception {  
294 - return doPostWithTypedResponse("/api/resource", tbResources, new TypeReference<>(){}); 286 + return doPostWithTypedResponse("/api/resource", tbResource, new TypeReference<>(){});
295 } 287 }
296 } 288 }
@@ -28,6 +28,7 @@ import java.util.Arrays; @@ -28,6 +28,7 @@ import java.util.Arrays;
28 @ClasspathSuite.ClassnameFilters({ 28 @ClasspathSuite.ClassnameFilters({
29 // "org.thingsboard.server.controller.sql.WebsocketApiSqlTest", 29 // "org.thingsboard.server.controller.sql.WebsocketApiSqlTest",
30 // "org.thingsboard.server.controller.sql.EntityQueryControllerSqlTest", 30 // "org.thingsboard.server.controller.sql.EntityQueryControllerSqlTest",
  31 +// "org.thingsboard.server.controller.sql.TbResourceControllerSqlTest",
31 "org.thingsboard.server.controller.sql.*Test", 32 "org.thingsboard.server.controller.sql.*Test",
32 }) 33 })
33 public class ControllerSqlTestSuite { 34 public class ControllerSqlTestSuite {
@@ -122,7 +122,7 @@ public class LwM2mTransportRequest { @@ -122,7 +122,7 @@ public class LwM2mTransportRequest {
122 DownlinkRequest request = null; 122 DownlinkRequest request = null;
123 ContentFormat contentFormat = contentFormatParam != null ? ContentFormat.fromName(contentFormatParam.toUpperCase()) : null; 123 ContentFormat contentFormat = contentFormatParam != null ? ContentFormat.fromName(contentFormatParam.toUpperCase()) : null;
124 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); 124 LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
125 - ResourceModel resource = lwM2MClient.getResourceModel(target); 125 + ResourceModel resource = lwM2MClient.getResourceModel(targetIdVer);
126 timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; 126 timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
127 switch (typeOper) { 127 switch (typeOper) {
128 case GET_TYPE_OPER_READ: 128 case GET_TYPE_OPER_READ:
@@ -22,7 +22,7 @@ import {Observable} from 'rxjs'; @@ -22,7 +22,7 @@ import {Observable} from 'rxjs';
22 import {PageData} from '@shared/models/page/page-data'; 22 import {PageData} from '@shared/models/page/page-data';
23 import {DeviceProfile, DeviceProfileInfo, DeviceTransportType} from '@shared/models/device.models'; 23 import {DeviceProfile, DeviceProfileInfo, DeviceTransportType} from '@shared/models/device.models';
24 import {isDefinedAndNotNull, isEmptyStr} from '@core/utils'; 24 import {isDefinedAndNotNull, isEmptyStr} from '@core/utils';
25 -import {ObjectLwM2M, ServerSecurityConfig} from '@home/components/profile/device/lwm2m/profile-config.models'; 25 +import {ObjectLwM2M, ServerSecurityConfig} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
26 import {SortOrder} from '@shared/models/page/sort-order'; 26 import {SortOrder} from '@shared/models/page/sort-order';
27 27
28 @Injectable({ 28 @Injectable({
  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 +<form [formGroup]="attributeLwm2mDialogFormGroup" (ngSubmit)="save()" style="width: 500px;">
  19 + <mat-toolbar color="primary">
  20 + <div fxFlex fxLayout="column" fxLayoutAlign="start">
  21 + <h2>{{ (readonly ? 'device-profile.lwm2m.attribute-lwm2m-toolbar-view' :
  22 + 'device-profile.lwm2m.attribute-lwm2m-toolbar-edit') | translate }}</h2>
  23 + </div>
  24 + <span fxFlex></span>
  25 + <button mat-icon-button
  26 + (click)="cancel()"
  27 + type="button">
  28 + <mat-icon class="material-icons">close</mat-icon>
  29 + </button>
  30 + </mat-toolbar>
  31 + <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
  32 + </mat-progress-bar>
  33 + <div mat-dialog-content>
  34 + <tb-lwm2m-attributes-key-list
  35 + formControlName="keyFilters"
  36 + titleText="{{data.destName}}">
  37 + </tb-lwm2m-attributes-key-list>
  38 + </div>
  39 + <div mat-dialog-actions fxLayoutAlign="end center">
  40 + <button mat-button color="primary"
  41 + type="button"
  42 + [disabled]="(isLoading$ | async)"
  43 + (click)="cancel()" cdkFocusInitial>
  44 + {{ (readonly ? 'action.close' : 'action.cancel') | translate }}
  45 + </button>
  46 + <button mat-raised-button color="primary"
  47 + *ngIf="!readonly"
  48 + type="submit"
  49 + [disabled]="(isLoading$ | async) || attributeLwm2mDialogFormGroup.invalid || !attributeLwm2mDialogFormGroup.dirty">
  50 + {{ 'action.save' | translate }}
  51 + </button>
  52 + </div>
  53 +</form>
  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, Inject, OnInit, SkipSelf} from "@angular/core";
  18 +import {ErrorStateMatcher} from "@angular/material/core";
  19 +import {DialogComponent} from "@shared/components/dialog.component";
  20 +import {Store} from "@ngrx/store";
  21 +import {AppState} from "@core/core.state";
  22 +import {Router} from "@angular/router";
  23 +import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
  24 +import {FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm} from "@angular/forms";
  25 +import {TranslateService} from "@ngx-translate/core";
  26 +import {JsonObject} from "@angular/compiler-cli/ngcc/src/packages/entry_point";
  27 +
  28 +export interface Lwm2mAttributesDialogData {
  29 + readonly: boolean;
  30 + attributeLwm2m: JsonObject;
  31 + destName: string
  32 +}
  33 +
  34 +@Component({
  35 + selector: 'tb-lwm2m-attributes-dialog',
  36 + templateUrl: './lwm2m-attributes-dialog.component.html',
  37 + styleUrls: ['./lwm2m-attributes.component.scss'],
  38 + providers: [{provide: ErrorStateMatcher, useExisting: Lwm2mAttributesDialogComponent}],
  39 +})
  40 +export class Lwm2mAttributesDialogComponent extends DialogComponent<Lwm2mAttributesDialogComponent, Object> implements OnInit, ErrorStateMatcher {
  41 +
  42 + readonly = this.data.readonly;
  43 +
  44 + attributeLwm2m = this.data.attributeLwm2m;
  45 +
  46 + submitted = false;
  47 +
  48 + dirtyValue = false;
  49 +
  50 + attributeLwm2mDialogFormGroup: FormGroup;
  51 +
  52 + constructor(protected store: Store<AppState>,
  53 + protected router: Router,
  54 + @Inject(MAT_DIALOG_DATA) public data: Lwm2mAttributesDialogData,
  55 + @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
  56 + public dialogRef: MatDialogRef<Lwm2mAttributesDialogComponent, object>,
  57 + private fb: FormBuilder,
  58 + public translate: TranslateService) {
  59 + super(store, router, dialogRef);
  60 +
  61 + this.attributeLwm2mDialogFormGroup = this.fb.group({
  62 + keyFilters: [{}, []]
  63 + });
  64 + this.attributeLwm2mDialogFormGroup.patchValue({keyFilters: this.attributeLwm2m});
  65 + this.attributeLwm2mDialogFormGroup.get('keyFilters').valueChanges.subscribe((attributes) => {
  66 + this.attributeLwm2m = attributes;
  67 + });
  68 + if (this.readonly) {
  69 + this.attributeLwm2mDialogFormGroup.disable({emitEvent: false});
  70 + }
  71 + }
  72 +
  73 + ngOnInit(): void {
  74 + }
  75 +
  76 + isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
  77 + const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
  78 + const customErrorState = !!(control && control.invalid && this.submitted);
  79 + return originalErrorState || customErrorState;
  80 + }
  81 +
  82 + save(): void {
  83 + this.submitted = true;
  84 + this.dialogRef.close(this.attributeLwm2m);
  85 + }
  86 +
  87 + cancel(): void {
  88 + this.dialogRef.close(null);
  89 + }
  90 +}
  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 fxLayout="column" class="tb-kv-map" [formGroup]="kvListFormGroup">
  19 + <div>
  20 + <mat-label translate class="tb-title no-padding">device-profile.lwm2m.attribute-lwm2m-destination</mat-label>
  21 + <mat-label class="tb-editor-area-title-panel">{{ titleText }}</mat-label>
  22 + </div>
  23 + <div fxLayout="row" fxLayoutGap="8px" style="max-height: 40px; margin-top: 8px;">
  24 + <mat-label fxFlex class="tb-title no-padding" translate>device-profile.lwm2m.attribute-lwm2m-name</mat-label>
  25 + <mat-label fxFlex class="tb-title no-padding" translate>device-profile.lwm2m.attribute-lwm2m-value</mat-label>
  26 + <div [fxShow]="!disabled" style="width: 40px;"></div>
  27 + </div>
  28 + <div fxLayout="column" formArrayName="keyVals"
  29 + *ngFor="let keyValControl of keyValsFormArray().controls; let $index = index">
  30 + <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
  31 + <mat-form-field class="mat-block" style="max-heights: 400px">
  32 + <mat-label></mat-label>
  33 + <mat-select [formControl]="keyValControl.get('key')">
  34 + <mat-option *ngFor="let attributeLwm2m of attrKeys"
  35 + [value]="attributeLwm2m">
  36 + {{ attributeLwm2mMap.get(attrKey[attributeLwm2m]) }}
  37 + </mat-option>
  38 + </mat-select>
  39 + </mat-form-field>
  40 + <mat-form-field fxFlex floatLabel="always" hideRequiredMarker class="mat-block"
  41 + style="max-height: 40px;">
  42 + <mat-label></mat-label>
  43 + <input [formControl]="keyValControl.get('value')" matInput
  44 + placeholder="{{ ('key-val.value') | translate }}"/>
  45 + </mat-form-field>
  46 + <button mat-button mat-icon-button color="primary"
  47 + [fxShow]="!disabled"
  48 + type="button"
  49 + (click)="removeKeyVal($index)"
  50 + [disabled]="isLoading$ | async"
  51 + matTooltip="{{ 'device-profile.lwm2m.attribute-lwm2m-remove-tip' | translate }}"
  52 + matTooltipPosition="above">
  53 + <mat-icon>close</mat-icon>
  54 + </button>
  55 + </div>
  56 + <mat-error *ngIf="keyValControl.get('key').hasError('required')" style="font-size: smaller">
  57 + {{ 'device-profile.lwm2m.key-name' | translate }}
  58 + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
  59 + </mat-error>
  60 + <mat-error fxLayout="row" *ngIf="keyValControl.get('key').hasError('validAttributeKey')"
  61 + style="font-size: smaller">
  62 + {{ 'device-profile.lwm2m.valid-attribute-lwm2m-key' | translate: {attrEnums: attrKeys} }}
  63 + </mat-error>
  64 + <mat-error fxLayout="row" *ngIf="keyValControl.get('value').hasError('validAttributeValue')"
  65 + style="font-size: smaller">
  66 + {{ 'device-profile.lwm2m.valid-attribute-lwm2m-value' | translate: {attrEnums: attrKeys} }}
  67 + </mat-error>
  68 + </div>
  69 + <span [fxShow]="!keyValsFormArray().length"
  70 + fxLayoutAlign="center center" [ngClass]="{'disabled': disabled}"
  71 + class="no-data-found" translate>{{noDataText ? noDataText : 'device-profile.lwm2m.no-data'}}</span>
  72 + <div style="margin-top: 8px;">
  73 + <button mat-button mat-raised-button color="primary"
  74 + [fxShow]="!disabled"
  75 + [disabled]="isLoading$ | async"
  76 + (click)="addKeyVal()"
  77 + type="button"
  78 + matTooltip="{{ 'device-profile.lwm2m.attribute-lwm2m-add-tip' | translate }}"
  79 + matTooltipPosition="above">
  80 + {{ 'action.add' | translate }}
  81 + </button>
  82 + </div>
  83 +</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 +
  17 +import {Component, forwardRef, Input, OnInit} from "@angular/core";
  18 +import {
  19 + AbstractControl,
  20 + ControlValueAccessor,
  21 + FormArray,
  22 + FormBuilder,
  23 + FormControl,
  24 + FormGroup,
  25 + NG_VALIDATORS,
  26 + NG_VALUE_ACCESSOR,
  27 + Validator,
  28 + Validators
  29 +} from "@angular/forms";
  30 +import {Subscription} from "rxjs";
  31 +import {PageComponent} from "@shared/components/page.component";
  32 +import {Store} from "@ngrx/store";
  33 +import {AppState} from "@core/core.state";
  34 +import {
  35 + ATTRIBUTE_KEYS,
  36 + ATTRIBUTE_LWM2M_ENUM,
  37 + ATTRIBUTE_LWM2M_MAP
  38 +} from "@home/components/profile/device/lwm2m/lwm2m-profile-config.models";
  39 +import {isDefinedAndNotNull, isEmpty, isEmptyStr, isUndefinedOrNull} from "@core/utils";
  40 +
  41 +
  42 +@Component({
  43 + selector: 'tb-lwm2m-attributes-key-list',
  44 + templateUrl: './lwm2m-attributes-key-list.component.html',
  45 + styleUrls: ['./lwm2m-attributes.component.scss'],
  46 + providers: [
  47 + {
  48 + provide: NG_VALUE_ACCESSOR,
  49 + useExisting: forwardRef(() => Lwm2mAttributesKeyListComponent),
  50 + multi: true
  51 + },
  52 + {
  53 + provide: NG_VALIDATORS,
  54 + useExisting: forwardRef(() => Lwm2mAttributesKeyListComponent),
  55 + multi: true,
  56 + }
  57 + ]
  58 +})
  59 +export class Lwm2mAttributesKeyListComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator {
  60 +
  61 + attrKeys = ATTRIBUTE_KEYS;
  62 +
  63 + attrKey = ATTRIBUTE_LWM2M_ENUM;
  64 +
  65 + attributeLwm2mMap = ATTRIBUTE_LWM2M_MAP;
  66 +
  67 + @Input() disabled: boolean;
  68 +
  69 + @Input() titleText: string;
  70 +
  71 + @Input() noDataText: string;
  72 +
  73 + kvListFormGroup: FormGroup;
  74 +
  75 + private propagateChange = null;
  76 +
  77 + private valueChangeSubscription: Subscription = null;
  78 +
  79 + constructor(protected store: Store<AppState>,
  80 + private fb: FormBuilder) {
  81 + super(store);
  82 + }
  83 +
  84 + ngOnInit(): void {
  85 + this.kvListFormGroup = this.fb.group({});
  86 + this.kvListFormGroup.addControl('keyVals',
  87 + this.fb.array([]));
  88 + }
  89 +
  90 + keyValsFormArray(): FormArray {
  91 + return this.kvListFormGroup.get('keyVals') as FormArray;
  92 + }
  93 +
  94 + registerOnChange(fn: any): void {
  95 + this.propagateChange = fn;
  96 + }
  97 +
  98 + registerOnTouched(fn: any): void {
  99 + }
  100 +
  101 + setDisabledState?(isDisabled: boolean): void {
  102 + this.disabled = isDisabled;
  103 + if (this.disabled) {
  104 + this.kvListFormGroup.disable({emitEvent: false});
  105 + } else {
  106 + this.kvListFormGroup.enable({emitEvent: false});
  107 + }
  108 + }
  109 +
  110 + writeValue(keyValMap: { [key: string]: string }): void {
  111 + if (this.valueChangeSubscription) {
  112 + this.valueChangeSubscription.unsubscribe();
  113 + }
  114 + const keyValsControls: Array<AbstractControl> = [];
  115 + if (keyValMap) {
  116 + for (const property of Object.keys(keyValMap)) {
  117 + if (Object.prototype.hasOwnProperty.call(keyValMap, property)) {
  118 + keyValsControls.push(this.fb.group({
  119 + key: [property, [Validators.required, this.attributeLwm2mKeyValidator]],
  120 + value: [keyValMap[property], this.attributeLwm2mValueValidator(property)]
  121 + }));
  122 + }
  123 + }
  124 + }
  125 + this.kvListFormGroup.setControl('keyVals', this.fb.array(keyValsControls));
  126 + this.valueChangeSubscription = this.kvListFormGroup.valueChanges.subscribe(() => {
  127 + // this.updateValidate();
  128 + this.updateModel();
  129 + });
  130 + if (this.disabled) {
  131 + this.kvListFormGroup.disable({emitEvent: false});
  132 + } else {
  133 + this.kvListFormGroup.enable({emitEvent: false});
  134 + }
  135 + }
  136 +
  137 + public removeKeyVal(index: number) {
  138 + (this.kvListFormGroup.get('keyVals') as FormArray).removeAt(index);
  139 + }
  140 +
  141 + public addKeyVal() {
  142 + const keyValsFormArray = this.kvListFormGroup.get('keyVals') as FormArray;
  143 + keyValsFormArray.push(this.fb.group({
  144 + key: ['', [Validators.required, this.attributeLwm2mKeyValidator]],
  145 + value: ['', []]
  146 + }));
  147 + }
  148 +
  149 + public validate(c?: FormControl) {
  150 + const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value;
  151 + let valid = true;
  152 + for (const entry of kvList) {
  153 + if (isUndefinedOrNull(entry.key) || isEmptyStr(entry.key) || !ATTRIBUTE_KEYS.includes(entry.key)) {
  154 + valid = false;
  155 + break;
  156 + }
  157 + if (entry.key !== 'ver' && isNaN(Number(entry.value))) {
  158 + valid = false;
  159 + break;
  160 + }
  161 + }
  162 + return (valid) ? null : {
  163 + keyVals: {
  164 + valid: false,
  165 + },
  166 + };
  167 + }
  168 +
  169 + private updateValidate (c?: FormControl) {
  170 + const kvList = this.kvListFormGroup.get('keyVals') as FormArray;
  171 + kvList.controls.forEach(fg => {
  172 + if (fg.get('key').value==='ver') {
  173 + fg.get('value').setValidators(null);
  174 + fg.get('value').setErrors(null);
  175 + }
  176 + else {
  177 + fg.get('value').setValidators(this.attributeLwm2mValueNumberValidator);
  178 + fg.get('value').setErrors(this.attributeLwm2mValueNumberValidator(fg.get('value')));
  179 + }
  180 + });
  181 + }
  182 +
  183 + private updateModel() {
  184 + this.updateValidate();
  185 + if (this.validate() === null) {
  186 + const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value;
  187 + const keyValMap: { [key: string]: string | number } = {};
  188 + kvList.forEach((entry) => {
  189 + if (isUndefinedOrNull(entry.value) || entry.key === 'ver' || isEmptyStr(entry.value.toString())) {
  190 + keyValMap[entry.key] = entry.value.toString();
  191 + } else {
  192 + keyValMap[entry.key] = Number(entry.value)
  193 + }
  194 + });
  195 + this.propagateChange(keyValMap);
  196 + }
  197 + else {
  198 + this.propagateChange(null);
  199 + }
  200 + }
  201 +
  202 +
  203 + private attributeLwm2mKeyValidator = (control: AbstractControl) => {
  204 + const key = control.value as string;
  205 + if (isDefinedAndNotNull(key) && !isEmpty(key)) {
  206 + if (!ATTRIBUTE_KEYS.includes(key)) {
  207 + return {
  208 + validAttributeKey: true
  209 + };
  210 + }
  211 + }
  212 + return null;
  213 + }
  214 +
  215 + private attributeLwm2mValueNumberValidator = (control: AbstractControl) => {
  216 + if (isNaN(Number(control.value)) || Number(control.value) < 0) {
  217 + return {
  218 + 'validAttributeValue': true
  219 + };
  220 + }
  221 + return null;
  222 + }
  223 +
  224 + private attributeLwm2mValueValidator = (property: string): Object [] => {
  225 + return property === 'ver'? [] : [this.attributeLwm2mValueNumberValidator];
  226 + }
  227 +}
  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 +<div fxLayout="row" [formGroup]="attributeLwm2mFormGroup">
  19 + <div fxFlex fxLayout="column" class="resource-name-lw-end" fxLayoutAlign="center"
  20 + [matTooltip]="isToolTipLabel()" matTooltipPosition="above">
  21 + {{attributeLwm2mToString()}}
  22 + </div>
  23 + <button type="button"
  24 + [disabled]="isDisableBtn()"
  25 + mat-button mat-icon-button
  26 + (click)="editAttributesLwm2m($event)"
  27 + [matTooltip]="(isIconView() ? 'action.view' : isIconEditAdd() ? 'action.edit' : 'action.add' ) | translate"
  28 + matTooltipPosition="above">
  29 + <mat-icon
  30 + class="material-icons">{{isIconView() ? 'visibility' : isIconEditAdd() ? 'edit' : 'add' }}</mat-icon>
  31 + </button>
  32 +</div>
  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 + .tb-kv-map {
  18 + span.no-data-found {
  19 + position: relative;
  20 + display: flex;
  21 + height: 40px;
  22 +
  23 + &.disabled {
  24 + color: rgba(0, 0, 0, .38);
  25 + }
  26 + }
  27 + }
  28 +}
  29 +
  30 +:host ::ng-deep {
  31 + .mat-form-field-wrapper {
  32 + padding-bottom: 0;
  33 + }
  34 + .mat-form-field-infix {
  35 + border-top: 0;
  36 + }
  37 + .mat-form-field-underline {
  38 + bottom: 0;
  39 + }
  40 +}
  41 +
  42 +.vertical-padding {
  43 + padding: 0 0 10px 20px;
  44 +}
  45 +
  46 +.resource-name-lw-end{
  47 + white-space: nowrap;
  48 + overflow: hidden;
  49 + text-overflow: ellipsis;
  50 + text-align:end;
  51 + //width: 80px;
  52 + cursor: pointer;
  53 +}
  54 +
  55 +.resource-name-lw{
  56 + white-space: nowrap;
  57 + overflow: hidden;
  58 + text-overflow: ellipsis;
  59 + cursor: pointer;
  60 +}
  61 +
  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, EventEmitter, forwardRef, Inject, Input, Output} from "@angular/core";
  18 +import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR} from "@angular/forms";
  19 +import {coerceBooleanProperty} from "@angular/cdk/coercion";
  20 +import {Store} from "@ngrx/store";
  21 +import {AppState} from "@core/core.state";
  22 +import {DeviceProfileService} from "@core/http/device-profile.service";
  23 +import {WINDOW} from "@core/services/window.service";
  24 +import {deepClone, isDefinedAndNotNull, isEmpty} from "@core/utils";
  25 +import {
  26 + Lwm2mAttributesDialogComponent,
  27 + Lwm2mAttributesDialogData
  28 +} from "@home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component";
  29 +import {MatDialog} from "@angular/material/dialog";
  30 +import {TranslateService} from "@ngx-translate/core";
  31 +import {ATTRIBUTE_LWM2M_LABEL} from "@home/components/profile/device/lwm2m/lwm2m-profile-config.models";
  32 +
  33 +
  34 +@Component({
  35 + selector: 'tb-profile-lwm2m-attributes',
  36 + templateUrl: './lwm2m-attributes.component.html',
  37 + styleUrls: ['./lwm2m-attributes.component.scss'],
  38 + providers: [{
  39 + provide: NG_VALUE_ACCESSOR,
  40 + useExisting: forwardRef(() => Lwm2mAttributesComponent),
  41 + multi: true
  42 + }]
  43 +})
  44 +export class Lwm2mAttributesComponent implements ControlValueAccessor {
  45 + attributeLwm2mFormGroup: FormGroup;
  46 + attributeLwm2mLabel = ATTRIBUTE_LWM2M_LABEL;
  47 +
  48 + private requiredValue: boolean;
  49 + private dirty = false;
  50 +
  51 + @Input()
  52 + attributeLwm2m: {};
  53 +
  54 + @Input()
  55 + isAttributeTelemetry: boolean;
  56 +
  57 + @Input()
  58 + destName: string;
  59 +
  60 + @Input()
  61 + disabled: boolean;
  62 +
  63 + @Output()
  64 + updateAttributeLwm2m = new EventEmitter<any>();
  65 +
  66 + @Input()
  67 + set required(value: boolean) {
  68 + this.requiredValue = coerceBooleanProperty(value);
  69 + }
  70 +
  71 + private propagateChange = (v: any) => {
  72 + }
  73 +
  74 + constructor(private store: Store<AppState>,
  75 + private dialog: MatDialog,
  76 + private fb: FormBuilder,
  77 + private deviceProfileService: DeviceProfileService,
  78 + private translate: TranslateService,
  79 + @Inject(WINDOW) private window: Window) {}
  80 +
  81 + registerOnChange(fn: any): void {
  82 + this.propagateChange = fn;
  83 + }
  84 +
  85 + registerOnTouched(fn: any): void {
  86 + }
  87 +
  88 + setDisabledState(isDisabled: boolean): void {
  89 + this.disabled = isDisabled;
  90 + if (isDisabled) {
  91 + this.attributeLwm2mFormGroup.disable({emitEvent: false});
  92 + } else {
  93 + this.attributeLwm2mFormGroup.enable({emitEvent: false});
  94 + }
  95 + }
  96 +
  97 + ngOnInit() {
  98 + this.attributeLwm2mFormGroup = this.fb.group({
  99 + attributeLwm2m: [this.attributeLwm2m]
  100 + });
  101 + }
  102 +
  103 + writeValue(value: {} | null): void {}
  104 +
  105 + attributeLwm2mToString = (): string => {
  106 + return this.isIconEditAdd () ? this.attributeLwm2mLabelToString() : this.translate.instant('device-profile.lwm2m.no-data');
  107 + }
  108 +
  109 + private attributeLwm2mLabelToString = (): string => {
  110 + let label = JSON.stringify(this.attributeLwm2m);
  111 + label = deepClone(label.replace('{', ''));
  112 + label = deepClone(label.replace('}', ''));
  113 + this.attributeLwm2mLabel.forEach((value: string, key: string) => {
  114 + const dest = '\"' + key + '\"\:';
  115 + label = deepClone(label.replace(dest, value));
  116 + });
  117 + return label;
  118 + }
  119 +
  120 + isDisableBtn (): boolean {
  121 + return this.disabled || this.isAttributeTelemetry ? !(isDefinedAndNotNull(this.attributeLwm2m) &&
  122 + !isEmpty(this.attributeLwm2m) && this.disabled) : this.disabled;
  123 + }
  124 +
  125 + isIconView (): boolean {
  126 + return this.isAttributeTelemetry || this.disabled;
  127 + }
  128 +
  129 + isIconEditAdd (): boolean {
  130 + return isDefinedAndNotNull(this.attributeLwm2m) && !isEmpty(this.attributeLwm2m);
  131 + }
  132 +
  133 + isToolTipLabel (): string {
  134 + return this.disabled ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip') :
  135 + this.isAttributeTelemetry ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-disable-tip') :
  136 + this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip');
  137 + }
  138 +
  139 + public editAttributesLwm2m = ($event: Event): void => {
  140 + if ($event) {
  141 + $event.stopPropagation();
  142 + }
  143 + this.dialog.open<Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData, Object>(Lwm2mAttributesDialogComponent, {
  144 + disableClose: true,
  145 + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
  146 + data: {
  147 + readonly: this.disabled,
  148 + attributeLwm2m: this.disabled ? this.attributeLwm2m : deepClone(this.attributeLwm2m),
  149 + destName: this.destName
  150 + }
  151 + }).afterClosed().subscribe((result) => {
  152 + if (result) {
  153 + this.attributeLwm2m = result;
  154 + this.attributeLwm2mFormGroup.patchValue({attributeLwm2m: this.attributeLwm2m});
  155 + this.updateAttributeLwm2m.next(this.attributeLwm2m);
  156 + }
  157 + });
  158 + }
  159 +}
@@ -14,8 +14,8 @@ @@ -14,8 +14,8 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component, forwardRef, Inject, Input } from '@angular/core';  
18 -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; 17 +import {Component, forwardRef, Inject, Input} from '@angular/core';
  18 +import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
19 import { 19 import {
20 DEFAULT_CLIENT_HOLD_OFF_TIME, 20 DEFAULT_CLIENT_HOLD_OFF_TIME,
21 DEFAULT_ID_SERVER, 21 DEFAULT_ID_SERVER,
@@ -27,13 +27,13 @@ import { @@ -27,13 +27,13 @@ import {
27 SECURITY_CONFIG_MODE, 27 SECURITY_CONFIG_MODE,
28 SECURITY_CONFIG_MODE_NAMES, 28 SECURITY_CONFIG_MODE_NAMES,
29 ServerSecurityConfig 29 ServerSecurityConfig
30 -} from './profile-config.models';  
31 -import { Store } from '@ngrx/store';  
32 -import { AppState } from '@core/core.state';  
33 -import { coerceBooleanProperty } from '@angular/cdk/coercion';  
34 -import { WINDOW } from '@core/services/window.service';  
35 -import { pairwise, startWith } from 'rxjs/operators';  
36 -import { DeviceProfileService } from '@core/http/device-profile.service'; 30 +} from './lwm2m-profile-config.models';
  31 +import {Store} from '@ngrx/store';
  32 +import {AppState} from '@core/core.state';
  33 +import {coerceBooleanProperty} from '@angular/cdk/coercion';
  34 +import {WINDOW} from '@core/services/window.service';
  35 +import {pairwise, startWith} from 'rxjs/operators';
  36 +import {DeviceProfileService} from '@core/http/device-profile.service';
37 37
38 @Component({ 38 @Component({
39 selector: 'tb-profile-lwm2m-device-config-server', 39 selector: 'tb-profile-lwm2m-device-config-server',
@@ -22,19 +22,20 @@ import {AppState} from '@app/core/core.state'; @@ -22,19 +22,20 @@ import {AppState} from '@app/core/core.state';
22 import {coerceBooleanProperty} from '@angular/cdk/coercion'; 22 import {coerceBooleanProperty} from '@angular/cdk/coercion';
23 import { 23 import {
24 ATTRIBUTE, 24 ATTRIBUTE,
  25 + DEFAULT_BINDING,
25 getDefaultProfileConfig, 26 getDefaultProfileConfig,
26 INSTANCES, 27 INSTANCES,
27 KEY_NAME, 28 KEY_NAME,
  29 + Lwm2mProfileConfigModels,
28 ModelValue, 30 ModelValue,
29 ObjectLwM2M, 31 ObjectLwM2M,
30 OBSERVE, 32 OBSERVE,
31 OBSERVE_ATTR_TELEMETRY, 33 OBSERVE_ATTR_TELEMETRY,
32 - ProfileConfigModels,  
33 RESOURCES, 34 RESOURCES,
34 TELEMETRY 35 TELEMETRY
35 -} from './profile-config.models'; 36 +} from './lwm2m-profile-config.models';
36 import {DeviceProfileService} from '@core/http/device-profile.service'; 37 import {DeviceProfileService} from '@core/http/device-profile.service';
37 -import {deepClone, isDefinedAndNotNull, isUndefined} from '@core/utils'; 38 +import {deepClone, isDefinedAndNotNull, isEmpty, isUndefined} from '@core/utils';
38 import {WINDOW} from '@core/services/window.service'; 39 import {WINDOW} from '@core/services/window.service';
39 import {JsonArray, JsonObject} from '@angular/compiler-cli/ngcc/src/packages/entry_point'; 40 import {JsonArray, JsonObject} from '@angular/compiler-cli/ngcc/src/packages/entry_point';
40 import {Direction} from '@shared/models/page/sort-order'; 41 import {Direction} from '@shared/models/page/sort-order';
@@ -50,7 +51,7 @@ import {Direction} from '@shared/models/page/sort-order'; @@ -50,7 +51,7 @@ import {Direction} from '@shared/models/page/sort-order';
50 }) 51 })
51 export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validators { 52 export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validators {
52 53
53 - private configurationValue: ProfileConfigModels; 54 + private configurationValue: Lwm2mProfileConfigModels;
54 private requiredValue: boolean; 55 private requiredValue: boolean;
55 private disabled = false; 56 private disabled = false;
56 57
@@ -70,7 +71,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -70,7 +71,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
70 this.requiredValue = coerceBooleanProperty(value); 71 this.requiredValue = coerceBooleanProperty(value);
71 } 72 }
72 73
73 - private propagateChange = (v: any) => { } 74 + private propagateChange = (v: any) => {
  75 + }
74 76
75 constructor(private store: Store<AppState>, 77 constructor(private store: Store<AppState>,
76 private fb: FormBuilder, 78 private fb: FormBuilder,
@@ -84,7 +86,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -84,7 +86,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
84 lifetime: [null, Validators.required], 86 lifetime: [null, Validators.required],
85 defaultMinPeriod: [null, Validators.required], 87 defaultMinPeriod: [null, Validators.required],
86 notifIfDisabled: [true, []], 88 notifIfDisabled: [true, []],
87 - binding: ['U', Validators.required], 89 + binding: [DEFAULT_BINDING, Validators.required],
88 bootstrapServer: [null, Validators.required], 90 bootstrapServer: [null, Validators.required],
89 lwm2mServer: [null, Validators.required], 91 lwm2mServer: [null, Validators.required],
90 }); 92 });
@@ -118,7 +120,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -118,7 +120,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
118 } 120 }
119 } 121 }
120 122
121 - writeValue(value: ProfileConfigModels | null): void { 123 + writeValue(value: Lwm2mProfileConfigModels | null): void {
122 this.configurationValue = (Object.keys(value).length === 0) ? getDefaultProfileConfig() : value; 124 this.configurationValue = (Object.keys(value).length === 0) ? getDefaultProfileConfig() : value;
123 this.lwm2mDeviceConfigFormGroup.patchValue({ 125 this.lwm2mDeviceConfigFormGroup.patchValue({
124 configurationJson: this.configurationValue 126 configurationJson: this.configurationValue
@@ -171,7 +173,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -171,7 +173,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
171 } 173 }
172 174
173 private updateObserveAttrTelemetryObjectFormGroup = (objectsList: ObjectLwM2M[]): void => { 175 private updateObserveAttrTelemetryObjectFormGroup = (objectsList: ObjectLwM2M[]): void => {
174 - this.lwm2mDeviceProfileFormGroup.patchValue({ 176 + this.lwm2mDeviceProfileFormGroup.patchValue({
175 observeAttrTelemetry: deepClone(this.getObserveAttrTelemetryObjects(objectsList)) 177 observeAttrTelemetry: deepClone(this.getObserveAttrTelemetryObjects(objectsList))
176 }, 178 },
177 {emitEvent: false}); 179 {emitEvent: false});
@@ -198,15 +200,15 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -198,15 +200,15 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
198 private getObserveAttrTelemetryObjects = (objectList: ObjectLwM2M[]): object => { 200 private getObserveAttrTelemetryObjects = (objectList: ObjectLwM2M[]): object => {
199 const objectLwM2MS = deepClone(objectList); 201 const objectLwM2MS = deepClone(objectList);
200 if (this.configurationValue.observeAttr && objectLwM2MS.length > 0) { 202 if (this.configurationValue.observeAttr && objectLwM2MS.length > 0) {
201 - const observeArray = this.configurationValue.observeAttr.observe;  
202 const attributeArray = this.configurationValue.observeAttr.attribute; 203 const attributeArray = this.configurationValue.observeAttr.attribute;
203 const telemetryArray = this.configurationValue.observeAttr.telemetry; 204 const telemetryArray = this.configurationValue.observeAttr.telemetry;
204 let keyNameJson = this.configurationValue.observeAttr.keyName; 205 let keyNameJson = this.configurationValue.observeAttr.keyName;
205 if (this.includesNotZeroInstance(attributeArray, telemetryArray)) { 206 if (this.includesNotZeroInstance(attributeArray, telemetryArray)) {
206 this.addInstances(attributeArray, telemetryArray, objectLwM2MS); 207 this.addInstances(attributeArray, telemetryArray, objectLwM2MS);
207 } 208 }
208 - if (isDefinedAndNotNull(observeArray) && observeArray.length > 0) {  
209 - this.updateObserveAttrTelemetryObjects(observeArray, objectLwM2MS, OBSERVE); 209 + if (isDefinedAndNotNull(this.configurationValue.observeAttr.observe) &&
  210 + this.configurationValue.observeAttr.observe.length > 0) {
  211 + this.updateObserveAttrTelemetryObjects(this.configurationValue.observeAttr.observe, objectLwM2MS, OBSERVE);
210 } 212 }
211 if (isDefinedAndNotNull(attributeArray) && attributeArray.length > 0) { 213 if (isDefinedAndNotNull(attributeArray) && attributeArray.length > 0) {
212 this.updateObserveAttrTelemetryObjects(attributeArray, objectLwM2MS, ATTRIBUTE); 214 this.updateObserveAttrTelemetryObjects(attributeArray, objectLwM2MS, ATTRIBUTE);
@@ -214,6 +216,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -214,6 +216,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
214 if (isDefinedAndNotNull(telemetryArray) && telemetryArray.length > 0) { 216 if (isDefinedAndNotNull(telemetryArray) && telemetryArray.length > 0) {
215 this.updateObserveAttrTelemetryObjects(telemetryArray, objectLwM2MS, TELEMETRY); 217 this.updateObserveAttrTelemetryObjects(telemetryArray, objectLwM2MS, TELEMETRY);
216 } 218 }
  219 + if (isDefinedAndNotNull(this.configurationValue.observeAttr.attributeLwm2m)) {
  220 + this.updateAttributeLwm2m(objectLwM2MS);
  221 + }
217 if (isDefinedAndNotNull(keyNameJson)) { 222 if (isDefinedAndNotNull(keyNameJson)) {
218 this.configurationValue.observeAttr.keyName = this.validateKeyNameObjects(keyNameJson, attributeArray, telemetryArray); 223 this.configurationValue.observeAttr.keyName = this.validateKeyNameObjects(keyNameJson, attributeArray, telemetryArray);
219 this.upDateJsonAllConfig(); 224 this.upDateJsonAllConfig();
@@ -256,45 +261,72 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -256,45 +261,72 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
256 [nameParameter] = true; 261 [nameParameter] = true;
257 } 262 }
258 }); 263 });
  264 + }
259 265
  266 + private updateAttributeLwm2m = (objectLwM2MS: ObjectLwM2M[]): void => {
  267 + Object.keys(this.configurationValue.observeAttr.attributeLwm2m).forEach(key => {
  268 + const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String);
  269 + let objectLwM2M = objectLwM2MS.find(objectLwm2m => objectLwm2m.keyId === objectKeyId);
  270 + if (objectLwM2M && instanceId) {
  271 + let instance = objectLwM2M.instances.find(instance => instance.id === +instanceId)
  272 + if (instance && resourceId) {
  273 + instance.resources.find(resource => resource.id === +resourceId)
  274 + .attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key];
  275 + } else if (instance) {
  276 + instance.attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key];
  277 + }
  278 + } else if (objectLwM2M) {
  279 + objectLwM2M.attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key];
  280 + }
  281 + });
260 } 282 }
261 283
262 - private updateKeyNameObjects = (clientObserveAttrTelemetry: ObjectLwM2M[]): void => { 284 + private updateKeyNameObjects = (objectLwM2MS: ObjectLwM2M[]): void => {
263 Object.keys(this.configurationValue.observeAttr.keyName).forEach(key => { 285 Object.keys(this.configurationValue.observeAttr.keyName).forEach(key => {
264 const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String); 286 const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String);
265 - const objectLwM2M = clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.keyId === objectKeyId) 287 + const objectLwM2M = objectLwM2MS.find(objectLwm2m => objectLwm2m.keyId === objectKeyId)
266 if (objectLwM2M) { 288 if (objectLwM2M) {
267 objectLwM2M.instances.find(instance => instance.id === +instanceId) 289 objectLwM2M.instances.find(instance => instance.id === +instanceId)
268 - .resources.find(resource => resource.id === +resourceId)  
269 - .keyName = this.configurationValue.observeAttr.keyName[key];  
270 - }}); 290 + .resources.find(resource => resource.id === +resourceId)
  291 + .keyName = this.configurationValue.observeAttr.keyName[key];
  292 + }
  293 + });
271 } 294 }
272 295
273 private validateKeyNameObjects = (nameJson: JsonObject, attributeArray: JsonArray, telemetryArray: JsonArray): {} => { 296 private validateKeyNameObjects = (nameJson: JsonObject, attributeArray: JsonArray, telemetryArray: JsonArray): {} => {
274 const keyName = JSON.parse(JSON.stringify(nameJson)); 297 const keyName = JSON.parse(JSON.stringify(nameJson));
275 let keyNameValidate = {}; 298 let keyNameValidate = {};
276 - const keyAttrTelemetry = attributeArray.concat(telemetryArray) ; 299 + const keyAttrTelemetry = attributeArray.concat(telemetryArray);
277 Object.keys(keyName).forEach(key => { 300 Object.keys(keyName).forEach(key => {
278 if (keyAttrTelemetry.includes(key)) { 301 if (keyAttrTelemetry.includes(key)) {
279 keyNameValidate[key] = keyName[key]; 302 keyNameValidate[key] = keyName[key];
280 } 303 }
281 }); 304 });
282 - return keyNameValidate; 305 + return keyNameValidate;
283 } 306 }
284 307
285 private updateObserveAttrTelemetryFromGroupToJson = (val: ObjectLwM2M[]): void => { 308 private updateObserveAttrTelemetryFromGroupToJson = (val: ObjectLwM2M[]): void => {
286 const observeArray: Array<string> = []; 309 const observeArray: Array<string> = [];
287 const attributeArray: Array<string> = []; 310 const attributeArray: Array<string> = [];
288 const telemetryArray: Array<string> = []; 311 const telemetryArray: Array<string> = [];
  312 + const attributeLwm2m: any = {};
289 const keyNameNew = {}; 313 const keyNameNew = {};
290 const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val)); 314 const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val));
291 observeJson.forEach(obj => { 315 observeJson.forEach(obj => {
  316 + if (isDefinedAndNotNull(obj.attributeLwm2m) && !isEmpty(obj.attributeLwm2m)) {
  317 + const pathObject = `/${obj.keyId}`;
  318 + attributeLwm2m[pathObject] = obj.attributeLwm2m;
  319 + }
292 if (obj.hasOwnProperty(INSTANCES) && Array.isArray(obj.instances)) { 320 if (obj.hasOwnProperty(INSTANCES) && Array.isArray(obj.instances)) {
293 obj.instances.forEach(instance => { 321 obj.instances.forEach(instance => {
  322 + if (isDefinedAndNotNull(instance.attributeLwm2m) && !isEmpty(instance.attributeLwm2m)) {
  323 + const pathInstance = `/${obj.keyId}/${instance.id}`;
  324 + attributeLwm2m[pathInstance] = instance.attributeLwm2m;
  325 + }
294 if (instance.hasOwnProperty(RESOURCES) && Array.isArray(instance.resources)) { 326 if (instance.hasOwnProperty(RESOURCES) && Array.isArray(instance.resources)) {
295 instance.resources.forEach(resource => { 327 instance.resources.forEach(resource => {
296 if (resource.attribute || resource.telemetry) { 328 if (resource.attribute || resource.telemetry) {
297 - let pathRes = `/${obj.keyId}/${instance.id}/${resource.id}`; 329 + const pathRes = `/${obj.keyId}/${instance.id}/${resource.id}`;
298 if (resource.observe) { 330 if (resource.observe) {
299 observeArray.push(pathRes); 331 observeArray.push(pathRes);
300 } 332 }
@@ -305,6 +337,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -305,6 +337,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
305 telemetryArray.push(pathRes); 337 telemetryArray.push(pathRes);
306 } 338 }
307 keyNameNew[pathRes] = resource.keyName; 339 keyNameNew[pathRes] = resource.keyName;
  340 + if (isDefinedAndNotNull(resource.attributeLwm2m) && !isEmpty(resource.attributeLwm2m)) {
  341 + attributeLwm2m[pathRes] = resource.attributeLwm2m;
  342 + }
308 } 343 }
309 }) 344 })
310 } 345 }
@@ -316,13 +351,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -316,13 +351,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
316 observe: observeArray, 351 observe: observeArray,
317 attribute: attributeArray, 352 attribute: attributeArray,
318 telemetry: telemetryArray, 353 telemetry: telemetryArray,
319 - keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew) 354 + keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew),
  355 + attributeLwm2m: attributeLwm2m
320 }; 356 };
321 } else { 357 } else {
322 this.configurationValue.observeAttr.observe = observeArray; 358 this.configurationValue.observeAttr.observe = observeArray;
323 this.configurationValue.observeAttr.attribute = attributeArray; 359 this.configurationValue.observeAttr.attribute = attributeArray;
324 this.configurationValue.observeAttr.telemetry = telemetryArray; 360 this.configurationValue.observeAttr.telemetry = telemetryArray;
325 - this.configurationValue.observeAttr.keyName = this.sortObjectKeyPathJson(KEY_NAME, keyNameNew); 361 + this.configurationValue.observeAttr.attributeLwm2m = attributeLwm2m;
326 } 362 }
327 } 363 }
328 364
@@ -392,6 +428,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -392,6 +428,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
392 this.removeObserveAttrTelemetryFromJson(TELEMETRY, value.keyId); 428 this.removeObserveAttrTelemetryFromJson(TELEMETRY, value.keyId);
393 this.removeObserveAttrTelemetryFromJson(ATTRIBUTE, value.keyId); 429 this.removeObserveAttrTelemetryFromJson(ATTRIBUTE, value.keyId);
394 this.removeKeyNameFromJson(value.keyId); 430 this.removeKeyNameFromJson(value.keyId);
  431 + this.removeAttributeLwm2mFromJson(value.keyId);
395 this.updateObserveAttrTelemetryObjectFormGroup(objectsOld); 432 this.updateObserveAttrTelemetryObjectFormGroup(objectsOld);
396 this.upDateJsonAllConfig(); 433 this.upDateJsonAllConfig();
397 } 434 }
@@ -413,4 +450,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro @@ -413,4 +450,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
413 } 450 }
414 }); 451 });
415 } 452 }
  453 +
  454 + private removeAttributeLwm2mFromJson = (keyId: string): void => {
  455 + debugger
  456 + const keyNameJson = this.configurationValue.observeAttr.attributeLwm2m;
  457 + Object.keys(keyNameJson).forEach(key => {
  458 + if (key.startsWith(`/${keyId}`)) {
  459 + delete keyNameJson[key];
  460 + }
  461 + });
  462 + }
416 } 463 }
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.html renamed from ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.html
@@ -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> &nbsp;&nbsp; (object&nbsp;[<b>{{data.objectKeyId}}</b>]) 20 + <b><i>{{data.objectName}}</i></b> (object <<b>{{data.objectKeyId}}</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()"
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.ts renamed from ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.ts
@@ -30,9 +30,10 @@ export interface Lwm2mObjectAddInstancesData { @@ -30,9 +30,10 @@ export interface Lwm2mObjectAddInstancesData {
30 30
31 @Component({ 31 @Component({
32 selector: 'tb-lwm2m-object-add-instances', 32 selector: 'tb-lwm2m-object-add-instances',
33 - templateUrl: './lwm2m-object-add-instances.component.html' 33 + templateUrl: './lwm2m-object-add-instances-dialog.component.html'
34 }) 34 })
35 -export class Lwm2mObjectAddInstancesComponent extends DialogComponent<Lwm2mObjectAddInstancesComponent, object> implements OnInit { 35 +export class Lwm2mObjectAddInstancesDialogComponent extends DialogComponent<Lwm2mObjectAddInstancesDialogComponent, object>
  36 + implements OnInit {
36 37
37 instancesFormGroup: FormGroup; 38 instancesFormGroup: FormGroup;
38 submitted = false; 39 submitted = false;
@@ -40,7 +41,7 @@ export class Lwm2mObjectAddInstancesComponent extends DialogComponent<Lwm2mObjec @@ -40,7 +41,7 @@ export class Lwm2mObjectAddInstancesComponent extends DialogComponent<Lwm2mObjec
40 constructor(protected store: Store<AppState>, 41 constructor(protected store: Store<AppState>,
41 protected router: Router, 42 protected router: Router,
42 @Inject(MAT_DIALOG_DATA) public data: Lwm2mObjectAddInstancesData, 43 @Inject(MAT_DIALOG_DATA) public data: Lwm2mObjectAddInstancesData,
43 - public dialogRef: MatDialogRef<Lwm2mObjectAddInstancesComponent, object>, 44 + public dialogRef: MatDialogRef<Lwm2mObjectAddInstancesDialogComponent, object>,
44 public fb: FormBuilder) { 45 public fb: FormBuilder) {
45 super(store, router, dialogRef); 46 super(store, router, dialogRef);
46 } 47 }
@@ -14,11 +14,11 @@ @@ -14,11 +14,11 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import {Component, forwardRef,} from '@angular/core'; 17 +import {Component, forwardRef} from '@angular/core';
18 import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms'; 18 import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
19 import {Store} from '@ngrx/store'; 19 import {Store} from '@ngrx/store';
20 import {AppState} from '@core/core.state'; 20 import {AppState} from '@core/core.state';
21 -import {INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER} from './profile-config.models'; 21 +import {INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER} from './lwm2m-profile-config.models';
22 22
23 @Component({ 23 @Component({
24 selector: 'tb-profile-lwm2m-object-add-instances-list', 24 selector: 'tb-profile-lwm2m-object-add-instances-list',
@@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
22 [selectable]="!disabled" 22 [selectable]="!disabled"
23 [removable]="!disabled" 23 [removable]="!disabled"
24 (removed)="remove(objectLwm2m)"> 24 (removed)="remove(objectLwm2m)">
25 - {{objectLwm2m.name}} 25 + {{objectLwm2m.name}} <{{objectLwm2m.keyId}}>
26 <mat-icon matChipRemove *ngIf="!disabled">close</mat-icon> 26 <mat-icon matChipRemove *ngIf="!disabled">close</mat-icon>
27 </mat-chip> 27 </mat-chip>
28 <input matInput type="text" placeholder="{{ !disabled ? ('device-profile.lwm2m.object-list' | translate) : '' }}" 28 <input matInput type="text" placeholder="{{ !disabled ? ('device-profile.lwm2m.object-list' | translate) : '' }}"
@@ -21,7 +21,7 @@ import {Store} from '@ngrx/store'; @@ -21,7 +21,7 @@ import {Store} from '@ngrx/store';
21 import {AppState} from '@core/core.state'; 21 import {AppState} from '@core/core.state';
22 import {Observable} from 'rxjs'; 22 import {Observable} from 'rxjs';
23 import {filter, map, mergeMap, publishReplay, refCount, tap} from 'rxjs/operators'; 23 import {filter, map, mergeMap, publishReplay, refCount, tap} from 'rxjs/operators';
24 -import {ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT} from './profile-config.models'; 24 +import {ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT} from './lwm2m-profile-config.models';
25 import {DeviceProfileService} from '@core/http/device-profile.service'; 25 import {DeviceProfileService} from '@core/http/device-profile.service';
26 import {Direction} from '@shared/models/page/sort-order'; 26 import {Direction} from '@shared/models/page/sort-order';
27 import {isDefined, isDefinedAndNotNull, isString} from '@core/utils'; 27 import {isDefined, isDefinedAndNotNull, isString} from '@core/utils';
@@ -19,58 +19,56 @@ @@ -19,58 +19,56 @@
19 <div fxLayout="row" fxFill formArrayName="resources" 19 <div fxLayout="row" fxFill formArrayName="resources"
20 *ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams"> 20 *ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams">
21 <div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i"> 21 <div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i">
22 - <div fxLayout="row" fxFill [fxShow]="!i">  
23 - <div fxFlex="55"></div>  
24 - <div fxFlex="10" fxLayoutAlign="start center">  
25 - <b>  
26 - <mat-label translate>device-profile.lwm2m.observe-label</mat-label>  
27 - </b> 22 + <div fxLayout="row" fxFill fxLayoutAlign="start center" [fxShow]="!i">
  23 + <div fxFlex="20">
  24 + <mat-label translate>device-profile.lwm2m.resource-label</mat-label>
28 </div> 25 </div>
29 <div fxFlex="10"> 26 <div fxFlex="10">
30 - <b>  
31 <mat-label translate>device-profile.lwm2m.attribute-label</mat-label> 27 <mat-label translate>device-profile.lwm2m.attribute-label</mat-label>
32 - </b>  
33 </div> 28 </div>
34 <div fxFlex="10"> 29 <div fxFlex="10">
35 - <b>  
36 <mat-label translate>device-profile.lwm2m.telemetry-label</mat-label> 30 <mat-label translate>device-profile.lwm2m.telemetry-label</mat-label>
37 - </b>  
38 </div> 31 </div>
39 - <div fxFlex="25">  
40 - <b> 32 + <div fxFlex="10">
  33 + <mat-label translate>device-profile.lwm2m.observe-label</mat-label>
  34 + </div>
  35 + <div fxFlex>
41 <mat-label translate>device-profile.lwm2m.key-name-label</mat-label> 36 <mat-label translate>device-profile.lwm2m.key-name-label</mat-label>
42 - </b>  
43 </div> 37 </div>
44 - </div>  
45 - <div fxLayout="row" fxFill fxLayoutAlign="center center">  
46 - <div fxFlex="55">  
47 - Resource[<b>{{resourceLwM2M.get('id').value}}</b>]  
48 - name:<b><i>{{resourceLwM2M.get('name').value}}</i></b> 38 + <div fxFlex="17" fxFlexOffset="0">
  39 + <mat-label translate>device-profile.lwm2m.attribute-lwm2m-label</mat-label>
49 </div> 40 </div>
50 - <div fxFlex="10" fxLayoutAlign="start center">  
51 - <mat-checkbox formControlName="observe" color="primary"  
52 - matTooltip="{{'device-profile.lwm2m.is-observe-tip' | translate}}"  
53 - matTooltipPosition="above">  
54 - </mat-checkbox> 41 + </div>
  42 + <div fxLayout="row" fxFill fxLayoutAlign="start center">
  43 + <div class="resource-name-lw" fxFlex="25"
  44 + matTooltip="{{'device-profile.lwm2m.resource-tip' | translate}}" matTooltipPosition="above">
  45 + <<b>{{resourceLwM2M.get('id').value}}</b>> <b><i>{{resourceLwM2M.get('name').value}}</i></b>
55 </div> 46 </div>
56 - <div fxFlex="10"> 47 + <div fxFlex="10" fxLayoutAlign="center center">
57 <mat-checkbox formControlName="attribute" color="warn" 48 <mat-checkbox formControlName="attribute" color="warn"
58 [checked]="updateObserve(i)" 49 [checked]="updateObserve(i)"
59 matTooltip="{{'device-profile.lwm2m.is-attr-tip' | translate}}" 50 matTooltip="{{'device-profile.lwm2m.is-attr-tip' | translate}}"
60 matTooltipPosition="above"> 51 matTooltipPosition="above">
61 </mat-checkbox> 52 </mat-checkbox>
62 </div> 53 </div>
63 - <div fxFlex="10"> 54 + <div fxFlex="10" fxLayoutAlign="center center">
64 <mat-checkbox formControlName="telemetry" color="primary" 55 <mat-checkbox formControlName="telemetry" color="primary"
65 [checked]="updateObserve(i)" 56 [checked]="updateObserve(i)"
66 matTooltip="{{'device-profile.lwm2m.is-telemetry-tip' | translate}}" 57 matTooltip="{{'device-profile.lwm2m.is-telemetry-tip' | translate}}"
67 matTooltipPosition="above"> 58 matTooltipPosition="above">
68 </mat-checkbox> 59 </mat-checkbox>
69 </div> 60 </div>
  61 + <div fxFlex="10" fxLayoutAlign="center center">
  62 + <mat-checkbox formControlName="observe" color="primary"
  63 + [disabled]="disableObserve(i)"
  64 + matTooltip="{{(disableObserve(i) ? 'device-profile.lwm2m.not-observe-tip' : 'device-profile.lwm2m.is-observe-tip') | translate}}"
  65 + matTooltipPosition="above">
  66 + </mat-checkbox>
  67 + </div>
70 <mat-form-field fxFlex="25"> 68 <mat-form-field fxFlex="25">
71 <mat-label *ngIf="resourceLwM2M.get('keyName').hasError('required')"> 69 <mat-label *ngIf="resourceLwM2M.get('keyName').hasError('required')">
72 - {{ 'device-profile.lwm2m.key-name_label' | translate }}</mat-label>  
73 - <input matInput type="text" formControlName="keyName" required 70 + {{ 'device-profile.lwm2m.key-name-label' | translate }}</mat-label>
  71 + <input class="resource-name-lw" matInput type="text" formControlName="keyName" required
74 matTooltip="{{'device-profile.lwm2m.key-name-tip' | translate}}" 72 matTooltip="{{'device-profile.lwm2m.key-name-tip' | translate}}"
75 (input)="updateValueKeyName($event, i)" 73 (input)="updateValueKeyName($event, i)"
76 matTooltipPosition="above"> 74 matTooltipPosition="above">
@@ -79,6 +77,16 @@ @@ -79,6 +77,16 @@
79 <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> 77 <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
80 </mat-error> 78 </mat-error>
81 </mat-form-field> 79 </mat-form-field>
  80 + <div fxFlex="20" class="resource-name-lw-end" fxFlexOffset="5">
  81 + <tb-profile-lwm2m-attributes
  82 + formControlName="attributeLwm2m"
  83 + [attributeLwm2m]="resourceLwM2M.get('attributeLwm2m').value"
  84 + [isAttributeTelemetry]="disableObserve(i)"
  85 + [destName]="getNameResourceLwm2m(resourceLwM2M.value)"
  86 + [disabled]="this.disabled"
  87 + (updateAttributeLwm2m)="updateAttributeLwm2m($event, i)">
  88 + </tb-profile-lwm2m-attributes>
  89 + </div>
82 </div> 90 </div>
83 </div> 91 </div>
84 </div> 92 </div>
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 16
17 import {Component, forwardRef, Input} from '@angular/core'; 17 import {Component, forwardRef, Input} from '@angular/core';
18 import {ControlValueAccessor, FormArray, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms'; 18 import {ControlValueAccessor, FormArray, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
19 -import {ResourceLwM2M} from '@home/components/profile/device/lwm2m/profile-config.models'; 19 +import {ResourceLwM2M, RESOURCES} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
20 import {Store} from '@ngrx/store'; 20 import {Store} from '@ngrx/store';
21 import {AppState} from '@core/core.state'; 21 import {AppState} from '@core/core.state';
22 import _ from 'lodash'; 22 import _ from 'lodash';
@@ -25,6 +25,7 @@ import {coerceBooleanProperty} from '@angular/cdk/coercion'; @@ -25,6 +25,7 @@ import {coerceBooleanProperty} from '@angular/cdk/coercion';
25 @Component({ 25 @Component({
26 selector: 'tb-profile-lwm2m-observe-attr-telemetry-resource', 26 selector: 'tb-profile-lwm2m-observe-attr-telemetry-resource',
27 templateUrl: './lwm2m-observe-attr-telemetry-resource.component.html', 27 templateUrl: './lwm2m-observe-attr-telemetry-resource.component.html',
  28 + styleUrls: ['./lwm2m-attributes.component.scss'],
28 providers: [ 29 providers: [
29 { 30 {
30 provide: NG_VALUE_ACCESSOR, 31 provide: NG_VALUE_ACCESSOR,
@@ -73,7 +74,7 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA @@ -73,7 +74,7 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
73 } 74 }
74 75
75 get resourceFormArray(): FormArray{ 76 get resourceFormArray(): FormArray{
76 - return this.resourceFormGroup.get('resources') as FormArray; 77 + return this.resourceFormGroup.get(RESOURCES) as FormArray;
77 } 78 }
78 79
79 setDisabledState(isDisabled: boolean): void { 80 setDisabledState(isDisabled: boolean): void {
@@ -89,6 +90,14 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA @@ -89,6 +90,14 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
89 this.resourceFormArray.at(index).patchValue({keyName: _.camelCase((event.target as HTMLInputElement).value)}); 90 this.resourceFormArray.at(index).patchValue({keyName: _.camelCase((event.target as HTMLInputElement).value)});
90 } 91 }
91 92
  93 + updateAttributeLwm2m = (event: Event, index: number): void => {
  94 + this.resourceFormArray.at(index).patchValue({attributeLwm2m: event});
  95 + }
  96 +
  97 + getNameResourceLwm2m = (resourceLwM2M: ResourceLwM2M): string => {
  98 + return '<' + resourceLwM2M.id +'> ' + resourceLwM2M.name;
  99 + }
  100 +
92 createResourceLwM2M(resourcesLwM2M: ResourceLwM2M[]): void { 101 createResourceLwM2M(resourcesLwM2M: ResourceLwM2M[]): void {
93 if (resourcesLwM2M.length === this.resourceFormArray.length) { 102 if (resourcesLwM2M.length === this.resourceFormArray.length) {
94 this.resourceFormArray.patchValue(resourcesLwM2M, {emitEvent: false}); 103 this.resourceFormArray.patchValue(resourcesLwM2M, {emitEvent: false});
@@ -101,7 +110,8 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA @@ -101,7 +110,8 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
101 observe: resourceLwM2M.observe, 110 observe: resourceLwM2M.observe,
102 attribute: resourceLwM2M.attribute, 111 attribute: resourceLwM2M.attribute,
103 telemetry: resourceLwM2M.telemetry, 112 telemetry: resourceLwM2M.telemetry,
104 - keyName: [resourceLwM2M.keyName, Validators.required] 113 + keyName: [resourceLwM2M.keyName, Validators.required],
  114 + attributeLwm2m: [resourceLwM2M.attributeLwm2m]
105 })); 115 }));
106 }); 116 });
107 } 117 }
@@ -128,6 +138,11 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA @@ -128,6 +138,11 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
128 updateObserve = (index: number): void =>{ 138 updateObserve = (index: number): void =>{
129 if (this.resourceFormArray.at(index).value.attribute === false && this.resourceFormArray.at(index).value.telemetry === false) { 139 if (this.resourceFormArray.at(index).value.attribute === false && this.resourceFormArray.at(index).value.telemetry === false) {
130 this.resourceFormArray.at(index).patchValue({observe: false}); 140 this.resourceFormArray.at(index).patchValue({observe: false});
  141 + this.resourceFormArray.at(index).patchValue({attributeLwm2m: {}});
131 } 142 }
132 } 143 }
  144 +
  145 + disableObserve = (index: number): boolean =>{
  146 + return !this.resourceFormArray.at(index).value.telemetry && !this.resourceFormArray.at(index).value.attribute;
  147 + }
133 } 148 }
@@ -20,17 +20,27 @@ @@ -20,17 +20,27 @@
20 <mat-expansion-panel 20 <mat-expansion-panel
21 *ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index;" 21 *ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index;"
22 [formGroupName]="i"> 22 [formGroupName]="i">
23 - <mat-expansion-panel-header>  
24 - <mat-panel-title fxLayoutAlign="start center">  
25 - <b><i>{{ objectLwM2M.get('name').value}}</i></b>&nbsp;&lt;id: {{ objectLwM2M.get('keyId').value}}> 23 + <mat-expansion-panel-header >
  24 + <mat-panel-title fxLayoutAlign="start center" >
  25 + <b><i>{{ objectLwM2M.get('name').value}}</i></b>&nbsp;&lt;{{ objectLwM2M.get('keyId').value}}>
  26 + <div fxFlex class="resource-name-lw-end">
  27 + <tb-profile-lwm2m-attributes
  28 + formControlName="attributeLwm2m"
  29 + [attributeLwm2m]="objectLwM2M.get('attributeLwm2m').value"
  30 + [isAttributeTelemetry]="disableObserveObject(i)"
  31 + [destName]="getNameObjectLwm2m( objectLwM2M.get('name').value, objectLwM2M.get('keyId').value)"
  32 + [disabled]="this.disabled"
  33 + (updateAttributeLwm2m)="updateAttributeLwm2mObject($event, objectLwM2M.get('keyId').value)">
  34 + </tb-profile-lwm2m-attributes>
  35 + </div>
26 </mat-panel-title> 36 </mat-panel-title>
27 - <mat-panel-description fxLayoutAlign="end center" *ngIf="!disabled"> 37 + <mat-panel-description fxFlex="5" fxLayoutAlign="end center" *ngIf="!disabled">
28 <button type="button" 38 <button type="button"
29 [fxShow]="objectLwM2M.get('multiple').value" 39 [fxShow]="objectLwM2M.get('multiple').value"
30 mat-button mat-icon-button (click)="addInstances($event, objectLwM2M.value)" 40 mat-button mat-icon-button (click)="addInstances($event, objectLwM2M.value)"
31 matTooltip="{{'device-profile.lwm2m.add-instances-tip' | translate}}" 41 matTooltip="{{'device-profile.lwm2m.add-instances-tip' | translate}}"
32 matTooltipPosition="above"> 42 matTooltipPosition="above">
33 - <mat-icon class="material-icons">{{'add'}}</mat-icon> 43 + <mat-icon class="material-icons">{{'note_add'}}</mat-icon>
34 </button> 44 </button>
35 </mat-panel-description> 45 </mat-panel-description>
36 </mat-expansion-panel-header> 46 </mat-expansion-panel-header>
@@ -45,22 +55,10 @@ @@ -45,22 +55,10 @@
45 <mat-panel-title> 55 <mat-panel-title>
46 <div class="tb-panel-title-height" fxFlex="100"> 56 <div class="tb-panel-title-height" fxFlex="100">
47 <div fxLayout="row" fxFill> 57 <div fxLayout="row" fxFill>
48 - <div fxFlex="44">  
49 - Instance [<b>{{instances.get('id').value}}</b>] 58 + <div fxFlex="22">
  59 + {{'device-profile.lwm2m.instance-label' | translate}} <<b>{{instances.get('id').value}}</b>>
50 </div> 60 </div>
51 - <div class="checkbox-padding" fxFlex="10">  
52 - <mat-checkbox color="primary"  
53 - [disabled]="this.disabled"  
54 - [checked]="getChecked(instances, 'observe')"  
55 - (click)="$event.stopPropagation()"  
56 - (change)="changeInstanceResourcesCheckBox($event.checked, instances, 'observe')"  
57 - (keydown)="$event.stopPropagation()"  
58 - [indeterminate]="getIndeterminate(instances, 'observe')"  
59 - matTooltip="{{'device-profile.lwm2m.is-observe-tip' | translate}}"  
60 - matTooltipPosition="above">  
61 - </mat-checkbox>  
62 - </div>  
63 - <div class="checkbox-padding" fxFlex="10"> 61 + <div class="checkbox-padding" fxFlex="10">
64 <mat-checkbox color="warn" 62 <mat-checkbox color="warn"
65 [disabled]="this.disabled" 63 [disabled]="this.disabled"
66 [checked]="getChecked(instances, 'attribute')" 64 [checked]="getChecked(instances, 'attribute')"
@@ -84,7 +82,29 @@ @@ -84,7 +82,29 @@
84 matTooltipPosition="above"> 82 matTooltipPosition="above">
85 </mat-checkbox> 83 </mat-checkbox>
86 </div> 84 </div>
87 - <div class="checkbox-padding" fxFlex="25"> 85 + <div class="checkbox-padding" fxFlex="10">
  86 + <mat-checkbox color="primary"
  87 + [disabled]="this.disabled"
  88 + [checked]="getChecked(instances, 'observe')"
  89 + (click)="$event.stopPropagation()"
  90 + (change)="changeInstanceResourcesCheckBox($event.checked, instances, 'observe')"
  91 + (keydown)="$event.stopPropagation()"
  92 + [indeterminate]="getIndeterminate(instances, 'observe')"
  93 + matTooltip="{{'device-profile.lwm2m.is-observe-tip' | translate}}"
  94 + matTooltipPosition="above">
  95 + </mat-checkbox>
  96 + </div>
  97 + <div fxFlex="10">
  98 + </div>
  99 + <div fxFlex="37" class="resource-name-lw-end" fxFlexOffset="5" disabled="false">
  100 + <tb-profile-lwm2m-attributes
  101 + formControlName="attributeLwm2m"
  102 + [attributeLwm2m]="instances.get('attributeLwm2m').value"
  103 + [isAttributeTelemetry]="disableObserveInstance(instances)"
  104 + [destName]="getNameInstanceLwm2m(instances.value, objectLwM2M.get('keyId').value)"
  105 + [disabled]="this.disabled"
  106 + (updateAttributeLwm2m)="updateAttributeLwm2mInstance($event, y, objectLwM2M.get('keyId').value)">
  107 + </tb-profile-lwm2m-attributes>
88 </div> 108 </div>
89 </div> 109 </div>
90 </div> 110 </div>
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss renamed from ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.css
@@ -13,14 +13,6 @@ @@ -13,14 +13,6 @@
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 -.vertical-padding {  
17 - padding: 0 0 10px 20px;  
18 -}  
19 -  
20 -.left-padding {  
21 - padding-left: 5px;  
22 -}  
23 -  
24 .tb-panel-title-height { 16 .tb-panel-title-height {
25 user-select: none; 17 user-select: none;
26 min-height: 32px; 18 min-height: 32px;
@@ -28,21 +20,5 @@ @@ -28,21 +20,5 @@
28 20
29 .checkbox-padding { 21 .checkbox-padding {
30 padding-left: 22px; 22 padding-left: 22px;
31 -}  
32 -  
33 -.label-resource {  
34 - /*padding: 4px;*/  
35 - color: #002699;  
36 - /*text-transform: uppercase;*/  
37 - background: rgba(220, 220, 220, .35);  
38 - border-radius: 20px;  
39 - width: inherit;  
40 - /*margin: 10px 0;*/  
41 - overflow: hidden;  
42 - font-size: 15px;  
43 - text-overflow: ellipsis;  
44 - white-space: nowrap;  
45 - opacity: .8;  
46 text-align:center; 23 text-align:center;
47 } 24 }
48 -  
@@ -14,7 +14,6 @@ @@ -14,7 +14,6 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -  
18 import {Component, forwardRef, Input} from '@angular/core'; 17 import {Component, forwardRef, Input} from '@angular/core';
19 import { 18 import {
20 AbstractControl, 19 AbstractControl,
@@ -28,19 +27,29 @@ import { @@ -28,19 +27,29 @@ import {
28 import {Store} from '@ngrx/store'; 27 import {Store} from '@ngrx/store';
29 import {AppState} from '@core/core.state'; 28 import {AppState} from '@core/core.state';
30 import {coerceBooleanProperty} from '@angular/cdk/coercion'; 29 import {coerceBooleanProperty} from '@angular/cdk/coercion';
31 -import {CLIENT_LWM2M, Instance, INSTANCES, ObjectLwM2M, ResourceLwM2M, RESOURCES} from './profile-config.models'; 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';
32 import {deepClone, isDefinedAndNotNull, isEqual, isUndefined} from '@core/utils'; 41 import {deepClone, isDefinedAndNotNull, isEqual, isUndefined} from '@core/utils';
33 import {MatDialog} from '@angular/material/dialog'; 42 import {MatDialog} from '@angular/material/dialog';
34 import {TranslateService} from '@ngx-translate/core'; 43 import {TranslateService} from '@ngx-translate/core';
35 import { 44 import {
36 - Lwm2mObjectAddInstancesComponent,  
37 - Lwm2mObjectAddInstancesData  
38 -} from '@home/components/profile/device/lwm2m/lwm2m-object-add-instances.component'; 45 + Lwm2mObjectAddInstancesData,
  46 + Lwm2mObjectAddInstancesDialogComponent
  47 +} from '@home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component';
39 48
40 @Component({ 49 @Component({
41 selector: 'tb-profile-lwm2m-observe-attr-telemetry', 50 selector: 'tb-profile-lwm2m-observe-attr-telemetry',
42 templateUrl: './lwm2m-observe-attr-telemetry.component.html', 51 templateUrl: './lwm2m-observe-attr-telemetry.component.html',
43 - styleUrls: ['./lwm2m-observe-attr-telemetry.component.css'], 52 + styleUrls: [ './lwm2m-observe-attr-telemetry.component.scss'],
44 providers: [ 53 providers: [
45 { 54 {
46 provide: NG_VALUE_ACCESSOR, 55 provide: NG_VALUE_ACCESSOR,
@@ -56,6 +65,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor @@ -56,6 +65,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
56 65
57 valuePrev = null as any; 66 valuePrev = null as any;
58 observeAttrTelemetryFormGroup: FormGroup; 67 observeAttrTelemetryFormGroup: FormGroup;
  68 + resources = RESOURCES;
59 69
60 get required(): boolean { 70 get required(): boolean {
61 return this.requiredValue; 71 return this.requiredValue;
@@ -143,6 +153,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor @@ -143,6 +153,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
143 name: objectLwM2M.name, 153 name: objectLwM2M.name,
144 multiple: objectLwM2M.multiple, 154 multiple: objectLwM2M.multiple,
145 mandatory: objectLwM2M.mandatory, 155 mandatory: objectLwM2M.mandatory,
  156 + attributeLwm2m: objectLwM2M.attributeLwm2m,
146 instances: this.createInstanceLwM2M(objectLwM2M.instances) 157 instances: this.createInstanceLwM2M(objectLwM2M.instances)
147 }); 158 });
148 })); 159 }));
@@ -152,6 +163,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor @@ -152,6 +163,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
152 return this.fb.array(instancesLwM2M.map((instanceLwM2M) => { 163 return this.fb.array(instancesLwM2M.map((instanceLwM2M) => {
153 return this.fb.group({ 164 return this.fb.group({
154 id: instanceLwM2M.id, 165 id: instanceLwM2M.id,
  166 + attributeLwm2m: {value: instanceLwM2M.attributeLwm2m, disabled: this.disabled},
155 resources: {value: instanceLwM2M.resources, disabled: this.disabled} 167 resources: {value: instanceLwM2M.resources, disabled: this.disabled}
156 }); 168 });
157 })); 169 }));
@@ -222,7 +234,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor @@ -222,7 +234,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
222 $event.stopPropagation(); 234 $event.stopPropagation();
223 $event.preventDefault(); 235 $event.preventDefault();
224 } 236 }
225 - this.dialog.open<Lwm2mObjectAddInstancesComponent, Lwm2mObjectAddInstancesData, object>(Lwm2mObjectAddInstancesComponent, { 237 + this.dialog.open<Lwm2mObjectAddInstancesDialogComponent, Lwm2mObjectAddInstancesData, object>(Lwm2mObjectAddInstancesDialogComponent, {
226 disableClose: true, 238 disableClose: true,
227 panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], 239 panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
228 data: { 240 data: {
@@ -240,6 +252,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor @@ -240,6 +252,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
240 } 252 }
241 253
242 private updateInstancesIds = (data: Lwm2mObjectAddInstancesData): void => { 254 private updateInstancesIds = (data: Lwm2mObjectAddInstancesData): void => {
  255 + debugger
243 const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls 256 const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls
244 .find(e => e.value.keyId === data.objectKeyId) as FormGroup; 257 .find(e => e.value.keyId === data.objectKeyId) as FormGroup;
245 const instancesArray = objectLwM2MFormGroup.value.instances as Instance []; 258 const instancesArray = objectLwM2MFormGroup.value.instances as Instance [];
@@ -249,6 +262,8 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor @@ -249,6 +262,8 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
249 r.attribute = false; 262 r.attribute = false;
250 r.telemetry = false; 263 r.telemetry = false;
251 r.observe = false; 264 r.observe = false;
  265 + r.keyName = {};
  266 + r.attributeLwm2m = {};
252 }); 267 });
253 const valueOld = this.instancesToSetId(instancesArray); 268 const valueOld = this.instancesToSetId(instancesArray);
254 if (!isEqual(valueOld, data.instancesIds)) { 269 if (!isEqual(valueOld, data.instancesIds)) {
@@ -286,6 +301,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor @@ -286,6 +301,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
286 private pushInstance = (instancesFormArray: FormArray, x: number, instanceNew: Instance): void => { 301 private pushInstance = (instancesFormArray: FormArray, x: number, instanceNew: Instance): void => {
287 instancesFormArray.push(this.fb.group({ 302 instancesFormArray.push(this.fb.group({
288 id: x, 303 id: x,
  304 + attributeLwm2m: instanceNew.attributeLwm2m,
289 resources: {value: instanceNew.resources, disabled: this.disabled} 305 resources: {value: instanceNew.resources, disabled: this.disabled}
290 })); 306 }));
291 } 307 }
@@ -297,4 +313,54 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor @@ -297,4 +313,54 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
297 private instancesToSetId = (instances: Instance[]): Set<number> => { 313 private instancesToSetId = (instances: Instance[]): Set<number> => {
298 return new Set(instances.map(x => x.id)); 314 return new Set(instances.map(x => x.id));
299 } 315 }
  316 +
  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;
  365 + }
300 } 366 }
@@ -14,16 +14,19 @@ @@ -14,16 +14,19 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { NgModule } from '@angular/core';  
18 -import { Lwm2mDeviceProfileTransportConfigurationComponent } from './lwm2m-device-profile-transport-configuration.component';  
19 -import { Lwm2mObjectListComponent } from './lwm2m-object-list.component';  
20 -import { Lwm2mObserveAttrTelemetryComponent } from './lwm2m-observe-attr-telemetry.component';  
21 -import { Lwm2mObserveAttrTelemetryResourceComponent } from './lwm2m-observe-attr-telemetry-resource.component';  
22 -import { Lwm2mDeviceConfigServerComponent } from './lwm2m-device-config-server.component';  
23 -import { Lwm2mObjectAddInstancesComponent } from './lwm2m-object-add-instances.component';  
24 -import { Lwm2mObjectAddInstancesListComponent } from './lwm2m-object-add-instances-list.component';  
25 -import { CommonModule } from '@angular/common';  
26 -import { SharedModule } from '@app/shared/shared.module'; 17 +import {NgModule} from '@angular/core';
  18 +import {Lwm2mDeviceProfileTransportConfigurationComponent} from './lwm2m-device-profile-transport-configuration.component';
  19 +import {Lwm2mObjectListComponent} from './lwm2m-object-list.component';
  20 +import {Lwm2mObserveAttrTelemetryComponent} from './lwm2m-observe-attr-telemetry.component';
  21 +import {Lwm2mObserveAttrTelemetryResourceComponent} from './lwm2m-observe-attr-telemetry-resource.component';
  22 +import {Lwm2mAttributesDialogComponent} from './lwm2m-attributes-dialog.component';
  23 +import {Lwm2mAttributesComponent} from './lwm2m-attributes.component';
  24 +import {Lwm2mAttributesKeyListComponent} from './lwm2m-attributes-key-list.component';
  25 +import {Lwm2mDeviceConfigServerComponent} from './lwm2m-device-config-server.component';
  26 +import {Lwm2mObjectAddInstancesDialogComponent} from './lwm2m-object-add-instances-dialog.component';
  27 +import {Lwm2mObjectAddInstancesListComponent} from './lwm2m-object-add-instances-list.component';
  28 +import {CommonModule} from '@angular/common';
  29 +import {SharedModule} from '@app/shared/shared.module';
27 30
28 @NgModule({ 31 @NgModule({
29 declarations: 32 declarations:
@@ -32,8 +35,11 @@ import { SharedModule } from '@app/shared/shared.module'; @@ -32,8 +35,11 @@ import { SharedModule } from '@app/shared/shared.module';
32 Lwm2mObjectListComponent, 35 Lwm2mObjectListComponent,
33 Lwm2mObserveAttrTelemetryComponent, 36 Lwm2mObserveAttrTelemetryComponent,
34 Lwm2mObserveAttrTelemetryResourceComponent, 37 Lwm2mObserveAttrTelemetryResourceComponent,
  38 + Lwm2mAttributesDialogComponent,
  39 + Lwm2mAttributesComponent,
  40 + Lwm2mAttributesKeyListComponent,
35 Lwm2mDeviceConfigServerComponent, 41 Lwm2mDeviceConfigServerComponent,
36 - Lwm2mObjectAddInstancesComponent, 42 + Lwm2mObjectAddInstancesDialogComponent,
37 Lwm2mObjectAddInstancesListComponent 43 Lwm2mObjectAddInstancesListComponent
38 ], 44 ],
39 imports: [ 45 imports: [
@@ -45,8 +51,11 @@ import { SharedModule } from '@app/shared/shared.module'; @@ -45,8 +51,11 @@ import { SharedModule } from '@app/shared/shared.module';
45 Lwm2mObjectListComponent, 51 Lwm2mObjectListComponent,
46 Lwm2mObserveAttrTelemetryComponent, 52 Lwm2mObserveAttrTelemetryComponent,
47 Lwm2mObserveAttrTelemetryResourceComponent, 53 Lwm2mObserveAttrTelemetryResourceComponent,
  54 + Lwm2mAttributesDialogComponent,
  55 + Lwm2mAttributesComponent,
  56 + Lwm2mAttributesKeyListComponent,
48 Lwm2mDeviceConfigServerComponent, 57 Lwm2mDeviceConfigServerComponent,
49 - Lwm2mObjectAddInstancesComponent, 58 + Lwm2mObjectAddInstancesDialogComponent,
50 Lwm2mObjectAddInstancesListComponent 59 Lwm2mObjectAddInstancesListComponent
51 ], 60 ],
52 providers: [ 61 providers: [
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts renamed from ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts
@@ -16,7 +16,9 @@ @@ -16,7 +16,9 @@
16 16
17 export const PAGE_SIZE_LIMIT = 50; 17 export const PAGE_SIZE_LIMIT = 50;
18 export const INSTANCES = 'instances'; 18 export const INSTANCES = 'instances';
  19 +export const INSTANCE = 'instance';
19 export const RESOURCES = 'resources'; 20 export const RESOURCES = 'resources';
  21 +export const ATTRIBUTE_LWM2M = 'attributeLwm2m';
20 export const CLIENT_LWM2M = 'clientLwM2M'; 22 export const CLIENT_LWM2M = 'clientLwM2M';
21 export const CLIENT_LWM2M_SETTINGS = 'clientLwM2mSettings'; 23 export const CLIENT_LWM2M_SETTINGS = 'clientLwM2mSettings';
22 export const OBSERVE_ATTR_TELEMETRY = 'observeAttrTelemetry'; 24 export const OBSERVE_ATTR_TELEMETRY = 'observeAttrTelemetry';
@@ -33,7 +35,7 @@ export const DEFAULT_CLIENT_HOLD_OFF_TIME = 1; @@ -33,7 +35,7 @@ export const DEFAULT_CLIENT_HOLD_OFF_TIME = 1;
33 export const DEFAULT_LIFE_TIME = 300; 35 export const DEFAULT_LIFE_TIME = 300;
34 export const DEFAULT_MIN_PERIOD = 1; 36 export const DEFAULT_MIN_PERIOD = 1;
35 export const DEFAULT_NOTIF_IF_DESIBLED = true; 37 export const DEFAULT_NOTIF_IF_DESIBLED = true;
36 -export const DEFAULT_BINDING = 'U'; 38 +export const DEFAULT_BINDING = 'UQ';
37 export const DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT = 0; 39 export const DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT = 0;
38 export const LEN_MAX_PUBLIC_KEY_RPK = 182; 40 export const LEN_MAX_PUBLIC_KEY_RPK = 182;
39 export const LEN_MAX_PUBLIC_KEY_X509 = 3000; 41 export const LEN_MAX_PUBLIC_KEY_X509 = 3000;
@@ -42,6 +44,44 @@ export const KEY_REGEXP_NUMBER = /^(\-?|\+?)\d*$/; @@ -42,6 +44,44 @@ export const KEY_REGEXP_NUMBER = /^(\-?|\+?)\d*$/;
42 export const INSTANCES_ID_VALUE_MIN = 0; 44 export const INSTANCES_ID_VALUE_MIN = 0;
43 export const INSTANCES_ID_VALUE_MAX = 65535; 45 export const INSTANCES_ID_VALUE_MAX = 65535;
44 46
  47 +export enum ATTRIBUTE_LWM2M_ENUM {
  48 + dim = 'dim',
  49 + ver = 'ver',
  50 + pmin = 'pmin',
  51 + pmax = 'pmax',
  52 + gt = 'gt',
  53 + lt = 'lt',
  54 + st = 'st'
  55 +}
  56 +
  57 +export const ATTRIBUTE_LWM2M_LABEL = new Map<ATTRIBUTE_LWM2M_ENUM, string>(
  58 + [
  59 + [ATTRIBUTE_LWM2M_ENUM.dim, 'dim='],
  60 + [ATTRIBUTE_LWM2M_ENUM.ver, 'ver='],
  61 + [ATTRIBUTE_LWM2M_ENUM.pmin, 'pmin='],
  62 + [ATTRIBUTE_LWM2M_ENUM.pmax, 'pmax='],
  63 + [ATTRIBUTE_LWM2M_ENUM.gt, '>'],
  64 + [ATTRIBUTE_LWM2M_ENUM.lt, '<'],
  65 + [ATTRIBUTE_LWM2M_ENUM.st, 'st='],
  66 +
  67 + ]
  68 +);
  69 +
  70 +export const ATTRIBUTE_LWM2M_MAP = new Map<ATTRIBUTE_LWM2M_ENUM, string>(
  71 + [
  72 + [ATTRIBUTE_LWM2M_ENUM.dim, 'Dimension'],
  73 + [ATTRIBUTE_LWM2M_ENUM.ver, 'Object version'],
  74 + [ATTRIBUTE_LWM2M_ENUM.pmin, 'Minimum period'],
  75 + [ATTRIBUTE_LWM2M_ENUM.pmax, 'Maximum period'],
  76 + [ATTRIBUTE_LWM2M_ENUM.gt, 'Greater than'],
  77 + [ATTRIBUTE_LWM2M_ENUM.lt, 'Lesser than'],
  78 + [ATTRIBUTE_LWM2M_ENUM.st, 'Step'],
  79 +
  80 + ]
  81 +);
  82 +
  83 +export const ATTRIBUTE_KEYS = Object.keys(ATTRIBUTE_LWM2M_ENUM) as string[];
  84 +
45 export enum SECURITY_CONFIG_MODE { 85 export enum SECURITY_CONFIG_MODE {
46 PSK = 'PSK', 86 PSK = 'PSK',
47 RPK = 'RPK', 87 RPK = 'RPK',
@@ -54,7 +94,7 @@ export const SECURITY_CONFIG_MODE_NAMES = new Map<SECURITY_CONFIG_MODE, string>( @@ -54,7 +94,7 @@ export const SECURITY_CONFIG_MODE_NAMES = new Map<SECURITY_CONFIG_MODE, string>(
54 [SECURITY_CONFIG_MODE.PSK, 'Pre-Shared Key'], 94 [SECURITY_CONFIG_MODE.PSK, 'Pre-Shared Key'],
55 [SECURITY_CONFIG_MODE.RPK, 'Raw Public Key'], 95 [SECURITY_CONFIG_MODE.RPK, 'Raw Public Key'],
56 [SECURITY_CONFIG_MODE.X509, 'X.509 Certificate'], 96 [SECURITY_CONFIG_MODE.X509, 'X.509 Certificate'],
57 - [SECURITY_CONFIG_MODE.NO_SEC, 'No Security'], 97 + [SECURITY_CONFIG_MODE.NO_SEC, 'No Security']
58 ] 98 ]
59 ); 99 );
60 100
@@ -90,7 +130,7 @@ interface BootstrapSecurityConfig { @@ -90,7 +130,7 @@ interface BootstrapSecurityConfig {
90 lwm2mServer: ServerSecurityConfig; 130 lwm2mServer: ServerSecurityConfig;
91 } 131 }
92 132
93 -export interface ProfileConfigModels { 133 +export interface Lwm2mProfileConfigModels {
94 clientLwM2mSettings: ClientLwM2mSettings; 134 clientLwM2mSettings: ClientLwM2mSettings;
95 observeAttr: ObservableAttributes; 135 observeAttr: ObservableAttributes;
96 bootstrap: BootstrapSecurityConfig; 136 bootstrap: BootstrapSecurityConfig;
@@ -100,11 +140,13 @@ export interface ProfileConfigModels { @@ -100,11 +140,13 @@ export interface ProfileConfigModels {
100 export interface ClientLwM2mSettings { 140 export interface ClientLwM2mSettings {
101 clientOnlyObserveAfterConnect: number; 141 clientOnlyObserveAfterConnect: number;
102 } 142 }
  143 +
103 export interface ObservableAttributes { 144 export interface ObservableAttributes {
104 observe: string[]; 145 observe: string[];
105 attribute: string[]; 146 attribute: string[];
106 telemetry: string[]; 147 telemetry: string[];
107 keyName: {}; 148 keyName: {};
  149 + attributeLwm2m: {};
108 } 150 }
109 151
110 export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig { 152 export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig {
@@ -151,21 +193,22 @@ function getDefaultProfileObserveAttrConfig(): ObservableAttributes { @@ -151,21 +193,22 @@ function getDefaultProfileObserveAttrConfig(): ObservableAttributes {
151 observe: [], 193 observe: [],
152 attribute: [], 194 attribute: [],
153 telemetry: [], 195 telemetry: [],
154 - keyName: {} 196 + keyName: {},
  197 + attributeLwm2m: {}
155 }; 198 };
156 } 199 }
157 200
158 -function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings { 201 +export function getDefaultProfileConfig(hostname?: any): Lwm2mProfileConfigModels {
159 return { 202 return {
160 - clientOnlyObserveAfterConnect: 1 203 + clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig(),
  204 + observeAttr: getDefaultProfileObserveAttrConfig(),
  205 + bootstrap: getDefaultProfileBootstrapSecurityConfig((hostname) ? hostname : DEFAULT_HOST_NAME)
161 }; 206 };
162 } 207 }
163 208
164 -export function getDefaultProfileConfig(hostname?: any): ProfileConfigModels { 209 +function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings {
165 return { 210 return {
166 - clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig(),  
167 - observeAttr: getDefaultProfileObserveAttrConfig(),  
168 - bootstrap: getDefaultProfileBootstrapSecurityConfig((hostname) ? hostname : DEFAULT_HOST_NAME) 211 + clientOnlyObserveAfterConnect: 1
169 }; 212 };
170 } 213 }
171 214
@@ -175,11 +218,13 @@ export interface ResourceLwM2M { @@ -175,11 +218,13 @@ export interface ResourceLwM2M {
175 observe: boolean; 218 observe: boolean;
176 attribute: boolean; 219 attribute: boolean;
177 telemetry: boolean; 220 telemetry: boolean;
178 - keyName: string; 221 + keyName: {};
  222 + attributeLwm2m?: {};
179 } 223 }
180 224
181 export interface Instance { 225 export interface Instance {
182 id: number; 226 id: number;
  227 + attributeLwm2m?: {};
183 resources: ResourceLwM2M[]; 228 resources: ResourceLwM2M[];
184 } 229 }
185 230
@@ -190,11 +235,12 @@ export interface Instance { @@ -190,11 +235,12 @@ export interface Instance {
190 * mandatory == false => Optional 235 * mandatory == false => Optional
191 */ 236 */
192 export interface ObjectLwM2M { 237 export interface ObjectLwM2M {
  238 +
193 id: number; 239 id: number;
194 keyId: string; 240 keyId: string;
195 name: string; 241 name: string;
196 multiple?: boolean; 242 multiple?: boolean;
197 mandatory?: boolean; 243 mandatory?: boolean;
  244 + attributeLwm2m?: {};
198 instances?: Instance []; 245 instances?: Instance [];
199 } 246 }
200 -  
@@ -1171,17 +1171,32 @@ @@ -1171,17 +1171,32 @@
1171 "instances-list": "Instances list", 1171 "instances-list": "Instances list",
1172 "instances-input": "Input Instance Id value", 1172 "instances-input": "Input Instance Id value",
1173 "instances-input-holder": "Input Instance number...", 1173 "instances-input-holder": "Input Instance number...",
1174 - "resource-label": "Resource", 1174 + "instance-label": "Instance",
  1175 + "resource-label": "<id> Resource name",
1175 "observe-label": "Observe", 1176 "observe-label": "Observe",
1176 "attribute-label": "Attribute", 1177 "attribute-label": "Attribute",
1177 "telemetry-label": "Telemetry", 1178 "telemetry-label": "Telemetry",
1178 "key-name-label": "Key Name", 1179 "key-name-label": "Key Name",
  1180 + "attribute-lwm2m-label": "AttrLwm2m",
  1181 + "resource-tip": "ID & Original Name of the Resource",
1179 "is-observe-tip": "Is Observe", 1182 "is-observe-tip": "Is Observe",
  1183 + "not-observe-tip": "To observe select telemetry or attributes first",
1180 "is-attr-tip": "Is Attribute", 1184 "is-attr-tip": "Is Attribute",
1181 "is-telemetry-tip": "Is Telemetry", 1185 "is-telemetry-tip": "Is Telemetry",
1182 - "key-name-tip": "Key Name", 1186 + "key-name-tip": "Key Name in Camel format",
  1187 + "attribute-lwm2m-tip": "Attributes Lwm2m",
  1188 + "attribute-lwm2m-disable-tip": "To edit Attributes Lwm2m select telemetry or attributes first",
  1189 + "valid-attribute-lwm2m-key": "Name have be only '{{attrEnums}}'",
  1190 + "valid-attribute-lwm2m-value": "Value have be Long, Double and greater than zero or null/empty",
  1191 + "no-data": "No attributes",
1183 "key-name": "Key Name", 1192 "key-name": "Key Name",
1184 - "key-name_label": "Key Name in Camel format", 1193 + "attribute-lwm2m-name": "Name attribute",
  1194 + "attribute-lwm2m-value": "Value",
  1195 + "attribute-lwm2m-toolbar-edit": "Edit attributes Lwm2m",
  1196 + "attribute-lwm2m-toolbar-view": "View Attributes Lwm2m",
  1197 + "attribute-lwm2m-destination": "Destination:",
  1198 + "attribute-lwm2m-add-tip": "Add attribute lwm2m",
  1199 + "attribute-lwm2m-remove-tip": "Remove attribute lwm2m",
1185 "required": " value is required.", 1200 "required": " value is required.",
1186 "mode": "Security config mode", 1201 "mode": "Security config mode",
1187 "pattern_hex_dec": "{ count, plural, 0 {must be hex decimal format} other {must be # characters} }", 1202 "pattern_hex_dec": "{ count, plural, 0 {must be hex decimal format} other {must be # characters} }",