Browse Source

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
pull/4400/head
nickAS21 5 years ago
committed by GitHub
parent
commit
c634859e90
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
  2. 16
      application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java
  3. 1
      application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java
  4. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
  5. 2
      ui-ngx/src/app/core/http/device-profile.service.ts
  6. 53
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.html
  7. 90
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.ts
  8. 83
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.html
  9. 227
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts
  10. 32
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.html
  11. 61
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.scss
  12. 159
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.ts
  13. 18
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts
  14. 89
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts
  15. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.html
  16. 7
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.ts
  17. 4
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts
  18. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html
  19. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts
  20. 62
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html
  21. 21
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.ts
  22. 62
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html
  23. 24
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss
  24. 80
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts
  25. 33
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts
  26. 70
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts
  27. 21
      ui-ngx/src/assets/locale/locale.constant-en_US.json

21
application/src/main/java/org/thingsboard/server/controller/TbResourceController.java

@ -30,7 +30,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
@ -42,10 +41,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.StringJoiner;
@Slf4j
@RestController
@ -111,25 +108,13 @@ public class TbResourceController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/resource", method = RequestMethod.POST)
@ResponseBody
public List<TbResource> saveResources(@RequestBody List<TbResource> resources) throws ThingsboardException {
public TbResource saveResource(@RequestBody TbResource resource) throws ThingsboardException {
try {
List<TbResource> addResources = new ArrayList<>();
StringJoiner noSaveResources = new StringJoiner("; ");
resources.forEach(resource -> {
try {
resource.setTenantId(getTenantId());
checkEntity(resource.getId(), resource, Resource.TB_RESOURCE);
addResources.add(addResource(resource));
} catch (Exception e) {
noSaveResources.add(resource.getFileName());
log.warn("Fail save resource: [{}]", resource.getFileName(), e);
return addResource(resource);
}
});
if (noSaveResources.length() > 0) {
throw new ThingsboardException(String.format("Fail save resource: %s", noSaveResources.toString()), ThingsboardErrorCode.INVALID_ARGUMENTS);
}
return addResources;
} catch (Exception e) {
catch (Exception e) {
throw handleException(e);
}
}

16
application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java

@ -32,7 +32,6 @@ import org.thingsboard.server.common.data.security.Authority;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -110,7 +109,7 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
TbResource savedResource = save(resource);
loginDifferentTenant();
doPostWithTypedResponse("/api/resource", Collections.singletonList(savedResource), new TypeReference<>(){}, status().isBadRequest());
doPostWithTypedResponse("/api/resource", savedResource, new TypeReference<>(){}, status().isForbidden());
deleteDifferentTenant();
}
@ -148,18 +147,15 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
@Test
public void testFindTenantTbResources() throws Exception {
List<TbResource> resourcesToSave = new ArrayList<>();
List<TbResourceInfo> resources = new ArrayList<>();
for (int i = 0; i < 173; i++) {
TbResource resource = new TbResource();
resource.setTitle("Resource" + i);
resource.setResourceType(ResourceType.JKS);
resource.setFileName(i + DEFAULT_FILE_NAME);
resource.setData("Test Data");
resourcesToSave.add(resource);
resources.add(new TbResourceInfo(save(resource)));
}
List<TbResourceInfo> resources =save(resourcesToSave).stream().map(TbResourceInfo::new).collect(Collectors.toList());
List<TbResourceInfo> loadedResources = new ArrayList<>();
PageLink pageLink = new PageLink(24);
PageData<TbResourceInfo> pageData;
@ -287,10 +283,6 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes
}
private TbResource save(TbResource tbResource) throws Exception {
return save(Collections.singletonList(tbResource)).get(0);
}
private List<TbResource> save(List<TbResource> tbResources) throws Exception {
return doPostWithTypedResponse("/api/resource", tbResources, new TypeReference<>(){});
return doPostWithTypedResponse("/api/resource", tbResource, new TypeReference<>(){});
}
}

1
application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java

@ -28,6 +28,7 @@ import java.util.Arrays;
@ClasspathSuite.ClassnameFilters({
// "org.thingsboard.server.controller.sql.WebsocketApiSqlTest",
// "org.thingsboard.server.controller.sql.EntityQueryControllerSqlTest",
// "org.thingsboard.server.controller.sql.TbResourceControllerSqlTest",
"org.thingsboard.server.controller.sql.*Test",
})
public class ControllerSqlTestSuite {

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java

@ -122,7 +122,7 @@ public class LwM2mTransportRequest {
DownlinkRequest request = null;
ContentFormat contentFormat = contentFormatParam != null ? ContentFormat.fromName(contentFormatParam.toUpperCase()) : null;
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
ResourceModel resource = lwM2MClient.getResourceModel(target);
ResourceModel resource = lwM2MClient.getResourceModel(targetIdVer);
timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
switch (typeOper) {
case GET_TYPE_OPER_READ:

2
ui-ngx/src/app/core/http/device-profile.service.ts

@ -22,7 +22,7 @@ import {Observable} from 'rxjs';
import {PageData} from '@shared/models/page/page-data';
import {DeviceProfile, DeviceProfileInfo, DeviceTransportType} from '@shared/models/device.models';
import {isDefinedAndNotNull, isEmptyStr} from '@core/utils';
import {ObjectLwM2M, ServerSecurityConfig} from '@home/components/profile/device/lwm2m/profile-config.models';
import {ObjectLwM2M, ServerSecurityConfig} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
import {SortOrder} from '@shared/models/page/sort-order';
@Injectable({

53
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.html

@ -0,0 +1,53 @@
<!--
Copyright © 2016-2021 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<form [formGroup]="attributeLwm2mDialogFormGroup" (ngSubmit)="save()" style="width: 500px;">
<mat-toolbar color="primary">
<div fxFlex fxLayout="column" fxLayoutAlign="start">
<h2>{{ (readonly ? 'device-profile.lwm2m.attribute-lwm2m-toolbar-view' :
'device-profile.lwm2m.attribute-lwm2m-toolbar-edit') | translate }}</h2>
</div>
<span fxFlex></span>
<button mat-icon-button
(click)="cancel()"
type="button">
<mat-icon class="material-icons">close</mat-icon>
</button>
</mat-toolbar>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div mat-dialog-content>
<tb-lwm2m-attributes-key-list
formControlName="keyFilters"
titleText="{{data.destName}}">
</tb-lwm2m-attributes-key-list>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial>
{{ (readonly ? 'action.close' : 'action.cancel') | translate }}
</button>
<button mat-raised-button color="primary"
*ngIf="!readonly"
type="submit"
[disabled]="(isLoading$ | async) || attributeLwm2mDialogFormGroup.invalid || !attributeLwm2mDialogFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div>
</form>

90
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.ts

@ -0,0 +1,90 @@
///
/// Copyright © 2016-2021 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {Component, Inject, OnInit, SkipSelf} from "@angular/core";
import {ErrorStateMatcher} from "@angular/material/core";
import {DialogComponent} from "@shared/components/dialog.component";
import {Store} from "@ngrx/store";
import {AppState} from "@core/core.state";
import {Router} from "@angular/router";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm} from "@angular/forms";
import {TranslateService} from "@ngx-translate/core";
import {JsonObject} from "@angular/compiler-cli/ngcc/src/packages/entry_point";
export interface Lwm2mAttributesDialogData {
readonly: boolean;
attributeLwm2m: JsonObject;
destName: string
}
@Component({
selector: 'tb-lwm2m-attributes-dialog',
templateUrl: './lwm2m-attributes-dialog.component.html',
styleUrls: ['./lwm2m-attributes.component.scss'],
providers: [{provide: ErrorStateMatcher, useExisting: Lwm2mAttributesDialogComponent}],
})
export class Lwm2mAttributesDialogComponent extends DialogComponent<Lwm2mAttributesDialogComponent, Object> implements OnInit, ErrorStateMatcher {
readonly = this.data.readonly;
attributeLwm2m = this.data.attributeLwm2m;
submitted = false;
dirtyValue = false;
attributeLwm2mDialogFormGroup: FormGroup;
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: Lwm2mAttributesDialogData,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public dialogRef: MatDialogRef<Lwm2mAttributesDialogComponent, object>,
private fb: FormBuilder,
public translate: TranslateService) {
super(store, router, dialogRef);
this.attributeLwm2mDialogFormGroup = this.fb.group({
keyFilters: [{}, []]
});
this.attributeLwm2mDialogFormGroup.patchValue({keyFilters: this.attributeLwm2m});
this.attributeLwm2mDialogFormGroup.get('keyFilters').valueChanges.subscribe((attributes) => {
this.attributeLwm2m = attributes;
});
if (this.readonly) {
this.attributeLwm2mDialogFormGroup.disable({emitEvent: false});
}
}
ngOnInit(): void {
}
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
const customErrorState = !!(control && control.invalid && this.submitted);
return originalErrorState || customErrorState;
}
save(): void {
this.submitted = true;
this.dialogRef.close(this.attributeLwm2m);
}
cancel(): void {
this.dialogRef.close(null);
}
}

83
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.html

@ -0,0 +1,83 @@
<!--
Copyright © 2016-2021 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section fxLayout="column" class="tb-kv-map" [formGroup]="kvListFormGroup">
<div>
<mat-label translate class="tb-title no-padding">device-profile.lwm2m.attribute-lwm2m-destination</mat-label>
<mat-label class="tb-editor-area-title-panel">{{ titleText }}</mat-label>
</div>
<div fxLayout="row" fxLayoutGap="8px" style="max-height: 40px; margin-top: 8px;">
<mat-label fxFlex class="tb-title no-padding" translate>device-profile.lwm2m.attribute-lwm2m-name</mat-label>
<mat-label fxFlex class="tb-title no-padding" translate>device-profile.lwm2m.attribute-lwm2m-value</mat-label>
<div [fxShow]="!disabled" style="width: 40px;"></div>
</div>
<div fxLayout="column" formArrayName="keyVals"
*ngFor="let keyValControl of keyValsFormArray().controls; let $index = index">
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field class="mat-block" style="max-heights: 400px">
<mat-label></mat-label>
<mat-select [formControl]="keyValControl.get('key')">
<mat-option *ngFor="let attributeLwm2m of attrKeys"
[value]="attributeLwm2m">
{{ attributeLwm2mMap.get(attrKey[attributeLwm2m]) }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex floatLabel="always" hideRequiredMarker class="mat-block"
style="max-height: 40px;">
<mat-label></mat-label>
<input [formControl]="keyValControl.get('value')" matInput
placeholder="{{ ('key-val.value') | translate }}"/>
</mat-form-field>
<button mat-button mat-icon-button color="primary"
[fxShow]="!disabled"
type="button"
(click)="removeKeyVal($index)"
[disabled]="isLoading$ | async"
matTooltip="{{ 'device-profile.lwm2m.attribute-lwm2m-remove-tip' | translate }}"
matTooltipPosition="above">
<mat-icon>close</mat-icon>
</button>
</div>
<mat-error *ngIf="keyValControl.get('key').hasError('required')" style="font-size: smaller">
{{ 'device-profile.lwm2m.key-name' | translate }}
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
<mat-error fxLayout="row" *ngIf="keyValControl.get('key').hasError('validAttributeKey')"
style="font-size: smaller">
{{ 'device-profile.lwm2m.valid-attribute-lwm2m-key' | translate: {attrEnums: attrKeys} }}
</mat-error>
<mat-error fxLayout="row" *ngIf="keyValControl.get('value').hasError('validAttributeValue')"
style="font-size: smaller">
{{ 'device-profile.lwm2m.valid-attribute-lwm2m-value' | translate: {attrEnums: attrKeys} }}
</mat-error>
</div>
<span [fxShow]="!keyValsFormArray().length"
fxLayoutAlign="center center" [ngClass]="{'disabled': disabled}"
class="no-data-found" translate>{{noDataText ? noDataText : 'device-profile.lwm2m.no-data'}}</span>
<div style="margin-top: 8px;">
<button mat-button mat-raised-button color="primary"
[fxShow]="!disabled"
[disabled]="isLoading$ | async"
(click)="addKeyVal()"
type="button"
matTooltip="{{ 'device-profile.lwm2m.attribute-lwm2m-add-tip' | translate }}"
matTooltipPosition="above">
{{ 'action.add' | translate }}
</button>
</div>
</section>

227
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts

@ -0,0 +1,227 @@
///
/// Copyright © 2016-2021 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {Component, forwardRef, Input, OnInit} from "@angular/core";
import {
AbstractControl,
ControlValueAccessor,
FormArray,
FormBuilder,
FormControl,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
Validator,
Validators
} from "@angular/forms";
import {Subscription} from "rxjs";
import {PageComponent} from "@shared/components/page.component";
import {Store} from "@ngrx/store";
import {AppState} from "@core/core.state";
import {
ATTRIBUTE_KEYS,
ATTRIBUTE_LWM2M_ENUM,
ATTRIBUTE_LWM2M_MAP
} from "@home/components/profile/device/lwm2m/lwm2m-profile-config.models";
import {isDefinedAndNotNull, isEmpty, isEmptyStr, isUndefinedOrNull} from "@core/utils";
@Component({
selector: 'tb-lwm2m-attributes-key-list',
templateUrl: './lwm2m-attributes-key-list.component.html',
styleUrls: ['./lwm2m-attributes.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Lwm2mAttributesKeyListComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => Lwm2mAttributesKeyListComponent),
multi: true,
}
]
})
export class Lwm2mAttributesKeyListComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator {
attrKeys = ATTRIBUTE_KEYS;
attrKey = ATTRIBUTE_LWM2M_ENUM;
attributeLwm2mMap = ATTRIBUTE_LWM2M_MAP;
@Input() disabled: boolean;
@Input() titleText: string;
@Input() noDataText: string;
kvListFormGroup: FormGroup;
private propagateChange = null;
private valueChangeSubscription: Subscription = null;
constructor(protected store: Store<AppState>,
private fb: FormBuilder) {
super(store);
}
ngOnInit(): void {
this.kvListFormGroup = this.fb.group({});
this.kvListFormGroup.addControl('keyVals',
this.fb.array([]));
}
keyValsFormArray(): FormArray {
return this.kvListFormGroup.get('keyVals') as FormArray;
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.kvListFormGroup.disable({emitEvent: false});
} else {
this.kvListFormGroup.enable({emitEvent: false});
}
}
writeValue(keyValMap: { [key: string]: string }): void {
if (this.valueChangeSubscription) {
this.valueChangeSubscription.unsubscribe();
}
const keyValsControls: Array<AbstractControl> = [];
if (keyValMap) {
for (const property of Object.keys(keyValMap)) {
if (Object.prototype.hasOwnProperty.call(keyValMap, property)) {
keyValsControls.push(this.fb.group({
key: [property, [Validators.required, this.attributeLwm2mKeyValidator]],
value: [keyValMap[property], this.attributeLwm2mValueValidator(property)]
}));
}
}
}
this.kvListFormGroup.setControl('keyVals', this.fb.array(keyValsControls));
this.valueChangeSubscription = this.kvListFormGroup.valueChanges.subscribe(() => {
// this.updateValidate();
this.updateModel();
});
if (this.disabled) {
this.kvListFormGroup.disable({emitEvent: false});
} else {
this.kvListFormGroup.enable({emitEvent: false});
}
}
public removeKeyVal(index: number) {
(this.kvListFormGroup.get('keyVals') as FormArray).removeAt(index);
}
public addKeyVal() {
const keyValsFormArray = this.kvListFormGroup.get('keyVals') as FormArray;
keyValsFormArray.push(this.fb.group({
key: ['', [Validators.required, this.attributeLwm2mKeyValidator]],
value: ['', []]
}));
}
public validate(c?: FormControl) {
const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value;
let valid = true;
for (const entry of kvList) {
if (isUndefinedOrNull(entry.key) || isEmptyStr(entry.key) || !ATTRIBUTE_KEYS.includes(entry.key)) {
valid = false;
break;
}
if (entry.key !== 'ver' && isNaN(Number(entry.value))) {
valid = false;
break;
}
}
return (valid) ? null : {
keyVals: {
valid: false,
},
};
}
private updateValidate (c?: FormControl) {
const kvList = this.kvListFormGroup.get('keyVals') as FormArray;
kvList.controls.forEach(fg => {
if (fg.get('key').value==='ver') {
fg.get('value').setValidators(null);
fg.get('value').setErrors(null);
}
else {
fg.get('value').setValidators(this.attributeLwm2mValueNumberValidator);
fg.get('value').setErrors(this.attributeLwm2mValueNumberValidator(fg.get('value')));
}
});
}
private updateModel() {
this.updateValidate();
if (this.validate() === null) {
const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value;
const keyValMap: { [key: string]: string | number } = {};
kvList.forEach((entry) => {
if (isUndefinedOrNull(entry.value) || entry.key === 'ver' || isEmptyStr(entry.value.toString())) {
keyValMap[entry.key] = entry.value.toString();
} else {
keyValMap[entry.key] = Number(entry.value)
}
});
this.propagateChange(keyValMap);
}
else {
this.propagateChange(null);
}
}
private attributeLwm2mKeyValidator = (control: AbstractControl) => {
const key = control.value as string;
if (isDefinedAndNotNull(key) && !isEmpty(key)) {
if (!ATTRIBUTE_KEYS.includes(key)) {
return {
validAttributeKey: true
};
}
}
return null;
}
private attributeLwm2mValueNumberValidator = (control: AbstractControl) => {
if (isNaN(Number(control.value)) || Number(control.value) < 0) {
return {
'validAttributeValue': true
};
}
return null;
}
private attributeLwm2mValueValidator = (property: string): Object [] => {
return property === 'ver'? [] : [this.attributeLwm2mValueNumberValidator];
}
}

32
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.html

@ -0,0 +1,32 @@
<!--
Copyright © 2016-2021 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div fxLayout="row" [formGroup]="attributeLwm2mFormGroup">
<div fxFlex fxLayout="column" class="resource-name-lw-end" fxLayoutAlign="center"
[matTooltip]="isToolTipLabel()" matTooltipPosition="above">
{{attributeLwm2mToString()}}
</div>
<button type="button"
[disabled]="isDisableBtn()"
mat-button mat-icon-button
(click)="editAttributesLwm2m($event)"
[matTooltip]="(isIconView() ? 'action.view' : isIconEditAdd() ? 'action.edit' : 'action.add' ) | translate"
matTooltipPosition="above">
<mat-icon
class="material-icons">{{isIconView() ? 'visibility' : isIconEditAdd() ? 'edit' : 'add' }}</mat-icon>
</button>
</div>

61
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.scss

@ -0,0 +1,61 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
.tb-kv-map {
span.no-data-found {
position: relative;
display: flex;
height: 40px;
&.disabled {
color: rgba(0, 0, 0, .38);
}
}
}
}
:host ::ng-deep {
.mat-form-field-wrapper {
padding-bottom: 0;
}
.mat-form-field-infix {
border-top: 0;
}
.mat-form-field-underline {
bottom: 0;
}
}
.vertical-padding {
padding: 0 0 10px 20px;
}
.resource-name-lw-end{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align:end;
//width: 80px;
cursor: pointer;
}
.resource-name-lw{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
}

159
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.ts

@ -0,0 +1,159 @@
///
/// Copyright © 2016-2021 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {Component, EventEmitter, forwardRef, Inject, Input, Output} from "@angular/core";
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR} from "@angular/forms";
import {coerceBooleanProperty} from "@angular/cdk/coercion";
import {Store} from "@ngrx/store";
import {AppState} from "@core/core.state";
import {DeviceProfileService} from "@core/http/device-profile.service";
import {WINDOW} from "@core/services/window.service";
import {deepClone, isDefinedAndNotNull, isEmpty} from "@core/utils";
import {
Lwm2mAttributesDialogComponent,
Lwm2mAttributesDialogData
} from "@home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component";
import {MatDialog} from "@angular/material/dialog";
import {TranslateService} from "@ngx-translate/core";
import {ATTRIBUTE_LWM2M_LABEL} from "@home/components/profile/device/lwm2m/lwm2m-profile-config.models";
@Component({
selector: 'tb-profile-lwm2m-attributes',
templateUrl: './lwm2m-attributes.component.html',
styleUrls: ['./lwm2m-attributes.component.scss'],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Lwm2mAttributesComponent),
multi: true
}]
})
export class Lwm2mAttributesComponent implements ControlValueAccessor {
attributeLwm2mFormGroup: FormGroup;
attributeLwm2mLabel = ATTRIBUTE_LWM2M_LABEL;
private requiredValue: boolean;
private dirty = false;
@Input()
attributeLwm2m: {};
@Input()
isAttributeTelemetry: boolean;
@Input()
destName: string;
@Input()
disabled: boolean;
@Output()
updateAttributeLwm2m = new EventEmitter<any>();
@Input()
set required(value: boolean) {
this.requiredValue = coerceBooleanProperty(value);
}
private propagateChange = (v: any) => {
}
constructor(private store: Store<AppState>,
private dialog: MatDialog,
private fb: FormBuilder,
private deviceProfileService: DeviceProfileService,
private translate: TranslateService,
@Inject(WINDOW) private window: Window) {}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (isDisabled) {
this.attributeLwm2mFormGroup.disable({emitEvent: false});
} else {
this.attributeLwm2mFormGroup.enable({emitEvent: false});
}
}
ngOnInit() {
this.attributeLwm2mFormGroup = this.fb.group({
attributeLwm2m: [this.attributeLwm2m]
});
}
writeValue(value: {} | null): void {}
attributeLwm2mToString = (): string => {
return this.isIconEditAdd () ? this.attributeLwm2mLabelToString() : this.translate.instant('device-profile.lwm2m.no-data');
}
private attributeLwm2mLabelToString = (): string => {
let label = JSON.stringify(this.attributeLwm2m);
label = deepClone(label.replace('{', ''));
label = deepClone(label.replace('}', ''));
this.attributeLwm2mLabel.forEach((value: string, key: string) => {
const dest = '\"' + key + '\"\:';
label = deepClone(label.replace(dest, value));
});
return label;
}
isDisableBtn (): boolean {
return this.disabled || this.isAttributeTelemetry ? !(isDefinedAndNotNull(this.attributeLwm2m) &&
!isEmpty(this.attributeLwm2m) && this.disabled) : this.disabled;
}
isIconView (): boolean {
return this.isAttributeTelemetry || this.disabled;
}
isIconEditAdd (): boolean {
return isDefinedAndNotNull(this.attributeLwm2m) && !isEmpty(this.attributeLwm2m);
}
isToolTipLabel (): string {
return this.disabled ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip') :
this.isAttributeTelemetry ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-disable-tip') :
this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip');
}
public editAttributesLwm2m = ($event: Event): void => {
if ($event) {
$event.stopPropagation();
}
this.dialog.open<Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData, Object>(Lwm2mAttributesDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
readonly: this.disabled,
attributeLwm2m: this.disabled ? this.attributeLwm2m : deepClone(this.attributeLwm2m),
destName: this.destName
}
}).afterClosed().subscribe((result) => {
if (result) {
this.attributeLwm2m = result;
this.attributeLwm2mFormGroup.patchValue({attributeLwm2m: this.attributeLwm2m});
this.updateAttributeLwm2m.next(this.attributeLwm2m);
}
});
}
}

18
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.ts

@ -14,8 +14,8 @@
/// limitations under the License.
///
import { Component, forwardRef, Inject, Input } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {Component, forwardRef, Inject, Input} from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
import {
DEFAULT_CLIENT_HOLD_OFF_TIME,
DEFAULT_ID_SERVER,
@ -27,13 +27,13 @@ import {
SECURITY_CONFIG_MODE,
SECURITY_CONFIG_MODE_NAMES,
ServerSecurityConfig
} from './profile-config.models';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { WINDOW } from '@core/services/window.service';
import { pairwise, startWith } from 'rxjs/operators';
import { DeviceProfileService } from '@core/http/device-profile.service';
} from './lwm2m-profile-config.models';
import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {WINDOW} from '@core/services/window.service';
import {pairwise, startWith} from 'rxjs/operators';
import {DeviceProfileService} from '@core/http/device-profile.service';
@Component({
selector: 'tb-profile-lwm2m-device-config-server',

89
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts

@ -22,19 +22,20 @@ import {AppState} from '@app/core/core.state';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {
ATTRIBUTE,
DEFAULT_BINDING,
getDefaultProfileConfig,
INSTANCES,
KEY_NAME,
Lwm2mProfileConfigModels,
ModelValue,
ObjectLwM2M,
OBSERVE,
OBSERVE_ATTR_TELEMETRY,
ProfileConfigModels,
RESOURCES,
TELEMETRY
} from './profile-config.models';
} from './lwm2m-profile-config.models';
import {DeviceProfileService} from '@core/http/device-profile.service';
import {deepClone, isDefinedAndNotNull, isUndefined} from '@core/utils';
import {deepClone, isDefinedAndNotNull, isEmpty, isUndefined} from '@core/utils';
import {WINDOW} from '@core/services/window.service';
import {JsonArray, JsonObject} from '@angular/compiler-cli/ngcc/src/packages/entry_point';
import {Direction} from '@shared/models/page/sort-order';
@ -50,7 +51,7 @@ import {Direction} from '@shared/models/page/sort-order';
})
export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validators {
private configurationValue: ProfileConfigModels;
private configurationValue: Lwm2mProfileConfigModels;
private requiredValue: boolean;
private disabled = false;
@ -70,7 +71,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
this.requiredValue = coerceBooleanProperty(value);
}
private propagateChange = (v: any) => { }
private propagateChange = (v: any) => {
}
constructor(private store: Store<AppState>,
private fb: FormBuilder,
@ -84,7 +86,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
lifetime: [null, Validators.required],
defaultMinPeriod: [null, Validators.required],
notifIfDisabled: [true, []],
binding: ['U', Validators.required],
binding: [DEFAULT_BINDING, Validators.required],
bootstrapServer: [null, Validators.required],
lwm2mServer: [null, Validators.required],
});
@ -118,7 +120,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
}
}
writeValue(value: ProfileConfigModels | null): void {
writeValue(value: Lwm2mProfileConfigModels | null): void {
this.configurationValue = (Object.keys(value).length === 0) ? getDefaultProfileConfig() : value;
this.lwm2mDeviceConfigFormGroup.patchValue({
configurationJson: this.configurationValue
@ -171,7 +173,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
}
private updateObserveAttrTelemetryObjectFormGroup = (objectsList: ObjectLwM2M[]): void => {
this.lwm2mDeviceProfileFormGroup.patchValue({
this.lwm2mDeviceProfileFormGroup.patchValue({
observeAttrTelemetry: deepClone(this.getObserveAttrTelemetryObjects(objectsList))
},
{emitEvent: false});
@ -198,15 +200,15 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private getObserveAttrTelemetryObjects = (objectList: ObjectLwM2M[]): object => {
const objectLwM2MS = deepClone(objectList);
if (this.configurationValue.observeAttr && objectLwM2MS.length > 0) {
const observeArray = this.configurationValue.observeAttr.observe;
const attributeArray = this.configurationValue.observeAttr.attribute;
const telemetryArray = this.configurationValue.observeAttr.telemetry;
let keyNameJson = this.configurationValue.observeAttr.keyName;
if (this.includesNotZeroInstance(attributeArray, telemetryArray)) {
this.addInstances(attributeArray, telemetryArray, objectLwM2MS);
}
if (isDefinedAndNotNull(observeArray) && observeArray.length > 0) {
this.updateObserveAttrTelemetryObjects(observeArray, objectLwM2MS, OBSERVE);
if (isDefinedAndNotNull(this.configurationValue.observeAttr.observe) &&
this.configurationValue.observeAttr.observe.length > 0) {
this.updateObserveAttrTelemetryObjects(this.configurationValue.observeAttr.observe, objectLwM2MS, OBSERVE);
}
if (isDefinedAndNotNull(attributeArray) && attributeArray.length > 0) {
this.updateObserveAttrTelemetryObjects(attributeArray, objectLwM2MS, ATTRIBUTE);
@ -214,6 +216,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
if (isDefinedAndNotNull(telemetryArray) && telemetryArray.length > 0) {
this.updateObserveAttrTelemetryObjects(telemetryArray, objectLwM2MS, TELEMETRY);
}
if (isDefinedAndNotNull(this.configurationValue.observeAttr.attributeLwm2m)) {
this.updateAttributeLwm2m(objectLwM2MS);
}
if (isDefinedAndNotNull(keyNameJson)) {
this.configurationValue.observeAttr.keyName = this.validateKeyNameObjects(keyNameJson, attributeArray, telemetryArray);
this.upDateJsonAllConfig();
@ -256,45 +261,72 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
[nameParameter] = true;
}
});
}
private updateAttributeLwm2m = (objectLwM2MS: ObjectLwM2M[]): void => {
Object.keys(this.configurationValue.observeAttr.attributeLwm2m).forEach(key => {
const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String);
let objectLwM2M = objectLwM2MS.find(objectLwm2m => objectLwm2m.keyId === objectKeyId);
if (objectLwM2M && instanceId) {
let instance = objectLwM2M.instances.find(instance => instance.id === +instanceId)
if (instance && resourceId) {
instance.resources.find(resource => resource.id === +resourceId)
.attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key];
} else if (instance) {
instance.attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key];
}
} else if (objectLwM2M) {
objectLwM2M.attributeLwm2m = this.configurationValue.observeAttr.attributeLwm2m[key];
}
});
}
private updateKeyNameObjects = (clientObserveAttrTelemetry: ObjectLwM2M[]): void => {
private updateKeyNameObjects = (objectLwM2MS: ObjectLwM2M[]): void => {
Object.keys(this.configurationValue.observeAttr.keyName).forEach(key => {
const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String);
const objectLwM2M = clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.keyId === objectKeyId)
const objectLwM2M = objectLwM2MS.find(objectLwm2m => objectLwm2m.keyId === objectKeyId)
if (objectLwM2M) {
objectLwM2M.instances.find(instance => instance.id === +instanceId)
.resources.find(resource => resource.id === +resourceId)
.keyName = this.configurationValue.observeAttr.keyName[key];
}});
.resources.find(resource => resource.id === +resourceId)
.keyName = this.configurationValue.observeAttr.keyName[key];
}
});
}
private validateKeyNameObjects = (nameJson: JsonObject, attributeArray: JsonArray, telemetryArray: JsonArray): {} => {
const keyName = JSON.parse(JSON.stringify(nameJson));
let keyNameValidate = {};
const keyAttrTelemetry = attributeArray.concat(telemetryArray) ;
const keyAttrTelemetry = attributeArray.concat(telemetryArray);
Object.keys(keyName).forEach(key => {
if (keyAttrTelemetry.includes(key)) {
keyNameValidate[key] = keyName[key];
}
});
return keyNameValidate;
return keyNameValidate;
}
private updateObserveAttrTelemetryFromGroupToJson = (val: ObjectLwM2M[]): void => {
const observeArray: Array<string> = [];
const attributeArray: Array<string> = [];
const telemetryArray: Array<string> = [];
const attributeLwm2m: any = {};
const keyNameNew = {};
const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val));
observeJson.forEach(obj => {
if (isDefinedAndNotNull(obj.attributeLwm2m) && !isEmpty(obj.attributeLwm2m)) {
const pathObject = `/${obj.keyId}`;
attributeLwm2m[pathObject] = obj.attributeLwm2m;
}
if (obj.hasOwnProperty(INSTANCES) && Array.isArray(obj.instances)) {
obj.instances.forEach(instance => {
if (isDefinedAndNotNull(instance.attributeLwm2m) && !isEmpty(instance.attributeLwm2m)) {
const pathInstance = `/${obj.keyId}/${instance.id}`;
attributeLwm2m[pathInstance] = instance.attributeLwm2m;
}
if (instance.hasOwnProperty(RESOURCES) && Array.isArray(instance.resources)) {
instance.resources.forEach(resource => {
if (resource.attribute || resource.telemetry) {
let pathRes = `/${obj.keyId}/${instance.id}/${resource.id}`;
const pathRes = `/${obj.keyId}/${instance.id}/${resource.id}`;
if (resource.observe) {
observeArray.push(pathRes);
}
@ -305,6 +337,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
telemetryArray.push(pathRes);
}
keyNameNew[pathRes] = resource.keyName;
if (isDefinedAndNotNull(resource.attributeLwm2m) && !isEmpty(resource.attributeLwm2m)) {
attributeLwm2m[pathRes] = resource.attributeLwm2m;
}
}
})
}
@ -316,13 +351,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
observe: observeArray,
attribute: attributeArray,
telemetry: telemetryArray,
keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew)
keyName: this.sortObjectKeyPathJson(KEY_NAME, keyNameNew),
attributeLwm2m: attributeLwm2m
};
} else {
this.configurationValue.observeAttr.observe = observeArray;
this.configurationValue.observeAttr.attribute = attributeArray;
this.configurationValue.observeAttr.telemetry = telemetryArray;
this.configurationValue.observeAttr.keyName = this.sortObjectKeyPathJson(KEY_NAME, keyNameNew);
this.configurationValue.observeAttr.attributeLwm2m = attributeLwm2m;
}
}
@ -392,6 +428,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
this.removeObserveAttrTelemetryFromJson(TELEMETRY, value.keyId);
this.removeObserveAttrTelemetryFromJson(ATTRIBUTE, value.keyId);
this.removeKeyNameFromJson(value.keyId);
this.removeAttributeLwm2mFromJson(value.keyId);
this.updateObserveAttrTelemetryObjectFormGroup(objectsOld);
this.upDateJsonAllConfig();
}
@ -413,4 +450,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
}
});
}
private removeAttributeLwm2mFromJson = (keyId: string): void => {
debugger
const keyNameJson = this.configurationValue.observeAttr.attributeLwm2m;
Object.keys(keyNameJson).forEach(key => {
if (key.startsWith(`/${keyId}`)) {
delete keyNameJson[key];
}
});
}
}

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.html → ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.html

@ -17,7 +17,7 @@
-->
<form [formGroup]="instancesFormGroup" (ngSubmit)="add()" style="min-width: 400px;">
<mat-toolbar fxLayout="row" color="primary">
<b><i>{{data.objectName}}</i></b> &nbsp;&nbsp; (object&nbsp;[<b>{{data.objectKeyId}}</b>])
<b><i>{{data.objectName}}</i></b> (object <<b>{{data.objectKeyId}}</b>>)
<span fxFlex></span>
<button mat-button mat-icon-button
(click)="cancel()"

7
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.ts → ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.ts

@ -30,9 +30,10 @@ export interface Lwm2mObjectAddInstancesData {
@Component({
selector: 'tb-lwm2m-object-add-instances',
templateUrl: './lwm2m-object-add-instances.component.html'
templateUrl: './lwm2m-object-add-instances-dialog.component.html'
})
export class Lwm2mObjectAddInstancesComponent extends DialogComponent<Lwm2mObjectAddInstancesComponent, object> implements OnInit {
export class Lwm2mObjectAddInstancesDialogComponent extends DialogComponent<Lwm2mObjectAddInstancesDialogComponent, object>
implements OnInit {
instancesFormGroup: FormGroup;
submitted = false;
@ -40,7 +41,7 @@ export class Lwm2mObjectAddInstancesComponent extends DialogComponent<Lwm2mObjec
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: Lwm2mObjectAddInstancesData,
public dialogRef: MatDialogRef<Lwm2mObjectAddInstancesComponent, object>,
public dialogRef: MatDialogRef<Lwm2mObjectAddInstancesDialogComponent, object>,
public fb: FormBuilder) {
super(store, router, dialogRef);
}

4
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts

@ -14,11 +14,11 @@
/// limitations under the License.
///
import {Component, forwardRef,} from '@angular/core';
import {Component, forwardRef} from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import {INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER} from './profile-config.models';
import {INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER} from './lwm2m-profile-config.models';
@Component({
selector: 'tb-profile-lwm2m-object-add-instances-list',

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html

@ -22,7 +22,7 @@
[selectable]="!disabled"
[removable]="!disabled"
(removed)="remove(objectLwm2m)">
{{objectLwm2m.name}}
{{objectLwm2m.name}} <{{objectLwm2m.keyId}}>
<mat-icon matChipRemove *ngIf="!disabled">close</mat-icon>
</mat-chip>
<input matInput type="text" placeholder="{{ !disabled ? ('device-profile.lwm2m.object-list' | translate) : '' }}"

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts

@ -21,7 +21,7 @@ import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import {Observable} from 'rxjs';
import {filter, map, mergeMap, publishReplay, refCount, tap} from 'rxjs/operators';
import {ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT} from './profile-config.models';
import {ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT} from './lwm2m-profile-config.models';
import {DeviceProfileService} from '@core/http/device-profile.service';
import {Direction} from '@shared/models/page/sort-order';
import {isDefined, isDefinedAndNotNull, isString} from '@core/utils';

62
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html

@ -19,58 +19,56 @@
<div fxLayout="row" fxFill formArrayName="resources"
*ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams">
<div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i">
<div fxLayout="row" fxFill [fxShow]="!i">
<div fxFlex="55"></div>
<div fxFlex="10" fxLayoutAlign="start center">
<b>
<mat-label translate>device-profile.lwm2m.observe-label</mat-label>
</b>
<div fxLayout="row" fxFill fxLayoutAlign="start center" [fxShow]="!i">
<div fxFlex="20">
<mat-label translate>device-profile.lwm2m.resource-label</mat-label>
</div>
<div fxFlex="10">
<b>
<mat-label translate>device-profile.lwm2m.attribute-label</mat-label>
</b>
</div>
<div fxFlex="10">
<b>
<mat-label translate>device-profile.lwm2m.telemetry-label</mat-label>
</b>
</div>
<div fxFlex="25">
<b>
<div fxFlex="10">
<mat-label translate>device-profile.lwm2m.observe-label</mat-label>
</div>
<div fxFlex>
<mat-label translate>device-profile.lwm2m.key-name-label</mat-label>
</b>
</div>
</div>
<div fxLayout="row" fxFill fxLayoutAlign="center center">
<div fxFlex="55">
Resource[<b>{{resourceLwM2M.get('id').value}}</b>]
name:<b><i>{{resourceLwM2M.get('name').value}}</i></b>
<div fxFlex="17" fxFlexOffset="0">
<mat-label translate>device-profile.lwm2m.attribute-lwm2m-label</mat-label>
</div>
<div fxFlex="10" fxLayoutAlign="start center">
<mat-checkbox formControlName="observe" color="primary"
matTooltip="{{'device-profile.lwm2m.is-observe-tip' | translate}}"
matTooltipPosition="above">
</mat-checkbox>
</div>
<div fxLayout="row" fxFill fxLayoutAlign="start center">
<div class="resource-name-lw" fxFlex="25"
matTooltip="{{'device-profile.lwm2m.resource-tip' | translate}}" matTooltipPosition="above">
<<b>{{resourceLwM2M.get('id').value}}</b>> <b><i>{{resourceLwM2M.get('name').value}}</i></b>
</div>
<div fxFlex="10">
<div fxFlex="10" fxLayoutAlign="center center">
<mat-checkbox formControlName="attribute" color="warn"
[checked]="updateObserve(i)"
matTooltip="{{'device-profile.lwm2m.is-attr-tip' | translate}}"
matTooltipPosition="above">
</mat-checkbox>
</div>
<div fxFlex="10">
<div fxFlex="10" fxLayoutAlign="center center">
<mat-checkbox formControlName="telemetry" color="primary"
[checked]="updateObserve(i)"
matTooltip="{{'device-profile.lwm2m.is-telemetry-tip' | translate}}"
matTooltipPosition="above">
</mat-checkbox>
</div>
<div fxFlex="10" fxLayoutAlign="center center">
<mat-checkbox formControlName="observe" color="primary"
[disabled]="disableObserve(i)"
matTooltip="{{(disableObserve(i) ? 'device-profile.lwm2m.not-observe-tip' : 'device-profile.lwm2m.is-observe-tip') | translate}}"
matTooltipPosition="above">
</mat-checkbox>
</div>
<mat-form-field fxFlex="25">
<mat-label *ngIf="resourceLwM2M.get('keyName').hasError('required')">
{{ 'device-profile.lwm2m.key-name_label' | translate }}</mat-label>
<input matInput type="text" formControlName="keyName" required
{{ 'device-profile.lwm2m.key-name-label' | translate }}</mat-label>
<input class="resource-name-lw" matInput type="text" formControlName="keyName" required
matTooltip="{{'device-profile.lwm2m.key-name-tip' | translate}}"
(input)="updateValueKeyName($event, i)"
matTooltipPosition="above">
@ -79,6 +77,16 @@
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
</mat-form-field>
<div fxFlex="20" class="resource-name-lw-end" fxFlexOffset="5">
<tb-profile-lwm2m-attributes
formControlName="attributeLwm2m"
[attributeLwm2m]="resourceLwM2M.get('attributeLwm2m').value"
[isAttributeTelemetry]="disableObserve(i)"
[destName]="getNameResourceLwm2m(resourceLwM2M.value)"
[disabled]="this.disabled"
(updateAttributeLwm2m)="updateAttributeLwm2m($event, i)">
</tb-profile-lwm2m-attributes>
</div>
</div>
</div>
</div>

21
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.ts

@ -16,7 +16,7 @@
import {Component, forwardRef, Input} from '@angular/core';
import {ControlValueAccessor, FormArray, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
import {ResourceLwM2M} from '@home/components/profile/device/lwm2m/profile-config.models';
import {ResourceLwM2M, RESOURCES} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import _ from 'lodash';
@ -25,6 +25,7 @@ import {coerceBooleanProperty} from '@angular/cdk/coercion';
@Component({
selector: 'tb-profile-lwm2m-observe-attr-telemetry-resource',
templateUrl: './lwm2m-observe-attr-telemetry-resource.component.html',
styleUrls: ['./lwm2m-attributes.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
@ -73,7 +74,7 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
}
get resourceFormArray(): FormArray{
return this.resourceFormGroup.get('resources') as FormArray;
return this.resourceFormGroup.get(RESOURCES) as FormArray;
}
setDisabledState(isDisabled: boolean): void {
@ -89,6 +90,14 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
this.resourceFormArray.at(index).patchValue({keyName: _.camelCase((event.target as HTMLInputElement).value)});
}
updateAttributeLwm2m = (event: Event, index: number): void => {
this.resourceFormArray.at(index).patchValue({attributeLwm2m: event});
}
getNameResourceLwm2m = (resourceLwM2M: ResourceLwM2M): string => {
return '<' + resourceLwM2M.id +'> ' + resourceLwM2M.name;
}
createResourceLwM2M(resourcesLwM2M: ResourceLwM2M[]): void {
if (resourcesLwM2M.length === this.resourceFormArray.length) {
this.resourceFormArray.patchValue(resourcesLwM2M, {emitEvent: false});
@ -101,7 +110,8 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
observe: resourceLwM2M.observe,
attribute: resourceLwM2M.attribute,
telemetry: resourceLwM2M.telemetry,
keyName: [resourceLwM2M.keyName, Validators.required]
keyName: [resourceLwM2M.keyName, Validators.required],
attributeLwm2m: [resourceLwM2M.attributeLwm2m]
}));
});
}
@ -128,6 +138,11 @@ export class Lwm2mObserveAttrTelemetryResourceComponent implements ControlValueA
updateObserve = (index: number): void =>{
if (this.resourceFormArray.at(index).value.attribute === false && this.resourceFormArray.at(index).value.telemetry === false) {
this.resourceFormArray.at(index).patchValue({observe: false});
this.resourceFormArray.at(index).patchValue({attributeLwm2m: {}});
}
}
disableObserve = (index: number): boolean =>{
return !this.resourceFormArray.at(index).value.telemetry && !this.resourceFormArray.at(index).value.attribute;
}
}

62
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html

@ -20,17 +20,27 @@
<mat-expansion-panel
*ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index;"
[formGroupName]="i">
<mat-expansion-panel-header>
<mat-panel-title fxLayoutAlign="start center">
<b><i>{{ objectLwM2M.get('name').value}}</i></b>&nbsp;&lt;id: {{ objectLwM2M.get('keyId').value}}>
<mat-expansion-panel-header >
<mat-panel-title fxLayoutAlign="start center" >
<b><i>{{ objectLwM2M.get('name').value}}</i></b>&nbsp;&lt;{{ objectLwM2M.get('keyId').value}}>
<div fxFlex class="resource-name-lw-end">
<tb-profile-lwm2m-attributes
formControlName="attributeLwm2m"
[attributeLwm2m]="objectLwM2M.get('attributeLwm2m').value"
[isAttributeTelemetry]="disableObserveObject(i)"
[destName]="getNameObjectLwm2m( objectLwM2M.get('name').value, objectLwM2M.get('keyId').value)"
[disabled]="this.disabled"
(updateAttributeLwm2m)="updateAttributeLwm2mObject($event, objectLwM2M.get('keyId').value)">
</tb-profile-lwm2m-attributes>
</div>
</mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" *ngIf="!disabled">
<mat-panel-description fxFlex="5" fxLayoutAlign="end center" *ngIf="!disabled">
<button type="button"
[fxShow]="objectLwM2M.get('multiple').value"
mat-button mat-icon-button (click)="addInstances($event, objectLwM2M.value)"
matTooltip="{{'device-profile.lwm2m.add-instances-tip' | translate}}"
matTooltipPosition="above">
<mat-icon class="material-icons">{{'add'}}</mat-icon>
<mat-icon class="material-icons">{{'note_add'}}</mat-icon>
</button>
</mat-panel-description>
</mat-expansion-panel-header>
@ -45,22 +55,10 @@
<mat-panel-title>
<div class="tb-panel-title-height" fxFlex="100">
<div fxLayout="row" fxFill>
<div fxFlex="44">
Instance [<b>{{instances.get('id').value}}</b>]
<div fxFlex="22">
{{'device-profile.lwm2m.instance-label' | translate}} <<b>{{instances.get('id').value}}</b>>
</div>
<div class="checkbox-padding" fxFlex="10">
<mat-checkbox color="primary"
[disabled]="this.disabled"
[checked]="getChecked(instances, 'observe')"
(click)="$event.stopPropagation()"
(change)="changeInstanceResourcesCheckBox($event.checked, instances, 'observe')"
(keydown)="$event.stopPropagation()"
[indeterminate]="getIndeterminate(instances, 'observe')"
matTooltip="{{'device-profile.lwm2m.is-observe-tip' | translate}}"
matTooltipPosition="above">
</mat-checkbox>
</div>
<div class="checkbox-padding" fxFlex="10">
<div class="checkbox-padding" fxFlex="10">
<mat-checkbox color="warn"
[disabled]="this.disabled"
[checked]="getChecked(instances, 'attribute')"
@ -84,7 +82,29 @@
matTooltipPosition="above">
</mat-checkbox>
</div>
<div class="checkbox-padding" fxFlex="25">
<div class="checkbox-padding" fxFlex="10">
<mat-checkbox color="primary"
[disabled]="this.disabled"
[checked]="getChecked(instances, 'observe')"
(click)="$event.stopPropagation()"
(change)="changeInstanceResourcesCheckBox($event.checked, instances, 'observe')"
(keydown)="$event.stopPropagation()"
[indeterminate]="getIndeterminate(instances, 'observe')"
matTooltip="{{'device-profile.lwm2m.is-observe-tip' | translate}}"
matTooltipPosition="above">
</mat-checkbox>
</div>
<div fxFlex="10">
</div>
<div fxFlex="37" class="resource-name-lw-end" fxFlexOffset="5" disabled="false">
<tb-profile-lwm2m-attributes
formControlName="attributeLwm2m"
[attributeLwm2m]="instances.get('attributeLwm2m').value"
[isAttributeTelemetry]="disableObserveInstance(instances)"
[destName]="getNameInstanceLwm2m(instances.value, objectLwM2M.get('keyId').value)"
[disabled]="this.disabled"
(updateAttributeLwm2m)="updateAttributeLwm2mInstance($event, y, objectLwM2M.get('keyId').value)">
</tb-profile-lwm2m-attributes>
</div>
</div>
</div>

24
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.css → ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss

@ -13,14 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.vertical-padding {
padding: 0 0 10px 20px;
}
.left-padding {
padding-left: 5px;
}
.tb-panel-title-height {
user-select: none;
min-height: 32px;
@ -28,21 +20,5 @@
.checkbox-padding {
padding-left: 22px;
}
.label-resource {
/*padding: 4px;*/
color: #002699;
/*text-transform: uppercase;*/
background: rgba(220, 220, 220, .35);
border-radius: 20px;
width: inherit;
/*margin: 10px 0;*/
overflow: hidden;
font-size: 15px;
text-overflow: ellipsis;
white-space: nowrap;
opacity: .8;
text-align:center;
}

80
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts

@ -14,7 +14,6 @@
/// limitations under the License.
///
import {Component, forwardRef, Input} from '@angular/core';
import {
AbstractControl,
@ -28,19 +27,29 @@ import {
import {Store} from '@ngrx/store';
import {AppState} from '@core/core.state';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {CLIENT_LWM2M, Instance, INSTANCES, ObjectLwM2M, ResourceLwM2M, RESOURCES} from './profile-config.models';
import {
ATTRIBUTE,
ATTRIBUTE_LWM2M,
CLIENT_LWM2M,
Instance,
INSTANCES,
ObjectLwM2M,
ResourceLwM2M,
RESOURCES,
TELEMETRY
} from './lwm2m-profile-config.models';
import {deepClone, isDefinedAndNotNull, isEqual, isUndefined} from '@core/utils';
import {MatDialog} from '@angular/material/dialog';
import {TranslateService} from '@ngx-translate/core';
import {
Lwm2mObjectAddInstancesComponent,
Lwm2mObjectAddInstancesData
} from '@home/components/profile/device/lwm2m/lwm2m-object-add-instances.component';
Lwm2mObjectAddInstancesData,
Lwm2mObjectAddInstancesDialogComponent
} from '@home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component';
@Component({
selector: 'tb-profile-lwm2m-observe-attr-telemetry',
templateUrl: './lwm2m-observe-attr-telemetry.component.html',
styleUrls: ['./lwm2m-observe-attr-telemetry.component.css'],
styleUrls: [ './lwm2m-observe-attr-telemetry.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
@ -56,6 +65,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
valuePrev = null as any;
observeAttrTelemetryFormGroup: FormGroup;
resources = RESOURCES;
get required(): boolean {
return this.requiredValue;
@ -143,6 +153,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
name: objectLwM2M.name,
multiple: objectLwM2M.multiple,
mandatory: objectLwM2M.mandatory,
attributeLwm2m: objectLwM2M.attributeLwm2m,
instances: this.createInstanceLwM2M(objectLwM2M.instances)
});
}));
@ -152,6 +163,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
return this.fb.array(instancesLwM2M.map((instanceLwM2M) => {
return this.fb.group({
id: instanceLwM2M.id,
attributeLwm2m: {value: instanceLwM2M.attributeLwm2m, disabled: this.disabled},
resources: {value: instanceLwM2M.resources, disabled: this.disabled}
});
}));
@ -222,7 +234,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
$event.stopPropagation();
$event.preventDefault();
}
this.dialog.open<Lwm2mObjectAddInstancesComponent, Lwm2mObjectAddInstancesData, object>(Lwm2mObjectAddInstancesComponent, {
this.dialog.open<Lwm2mObjectAddInstancesDialogComponent, Lwm2mObjectAddInstancesData, object>(Lwm2mObjectAddInstancesDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
@ -240,6 +252,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
}
private updateInstancesIds = (data: Lwm2mObjectAddInstancesData): void => {
debugger
const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls
.find(e => e.value.keyId === data.objectKeyId) as FormGroup;
const instancesArray = objectLwM2MFormGroup.value.instances as Instance [];
@ -249,6 +262,8 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
r.attribute = false;
r.telemetry = false;
r.observe = false;
r.keyName = {};
r.attributeLwm2m = {};
});
const valueOld = this.instancesToSetId(instancesArray);
if (!isEqual(valueOld, data.instancesIds)) {
@ -286,6 +301,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
private pushInstance = (instancesFormArray: FormArray, x: number, instanceNew: Instance): void => {
instancesFormArray.push(this.fb.group({
id: x,
attributeLwm2m: instanceNew.attributeLwm2m,
resources: {value: instanceNew.resources, disabled: this.disabled}
}));
}
@ -297,4 +313,54 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
private instancesToSetId = (instances: Instance[]): Set<number> => {
return new Set(instances.map(x => x.id));
}
getNameObjectLwm2m = (objectName: string, idVerObj: string): string => {
return objectName + ' <' + idVerObj + '>';
}
getNameInstanceLwm2m = (instance: Instance, idVerObj: string): string => {
return ' instance <' + idVerObj + '/' + instance.id +'>';
}
updateAttributeLwm2mObject = (event: Event, objectKeyId: number): void => {
const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls
.find(e => e.value.keyId === objectKeyId) as FormGroup;
objectLwM2MFormGroup.patchValue({attributeLwm2m: event});
}
updateAttributeLwm2mInstance = (event: Event, indexInstance: number, objectKeyId: number): void => {
const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls
.find(e => e.value.keyId === objectKeyId) as FormGroup;
const instancesFormArray = objectLwM2MFormGroup.get(INSTANCES) as FormArray;
instancesFormArray.at(indexInstance).patchValue({attributeLwm2m: event});
}
disableObserveInstance = (instance: AbstractControl): boolean => {
const checkedAttrTelemetry = this.observeInstance(instance);
if (checkedAttrTelemetry) {
instance.get(ATTRIBUTE_LWM2M).patchValue(null);
}
return checkedAttrTelemetry;
}
disableObserveObject = (index: number): boolean => {
const object = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).at(index) as FormGroup;
const instances = object.controls.instances as FormArray;
const checkedAttrTelemetry = instances.controls.filter(instance => !this.disableObserveInstance(instance));
if (checkedAttrTelemetry.length === 0) {
object.controls.attributeLwm2m.patchValue(null);
}
return checkedAttrTelemetry.length === 0;
}
observeInstance = (instance: AbstractControl): boolean => {
const resources = instance.get(RESOURCES).value as ResourceLwM2M[];
if (isDefinedAndNotNull(resources)) {
const checkedAttribute = resources.filter(resource => resource[ATTRIBUTE]);
const checkedTelemetry = resources.filter(resource => resource[TELEMETRY]);
return checkedAttribute.length === 0 && checkedTelemetry.length === 0;
}
return false;
}
}

33
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts

@ -14,16 +14,19 @@
/// limitations under the License.
///
import { NgModule } from '@angular/core';
import { Lwm2mDeviceProfileTransportConfigurationComponent } from './lwm2m-device-profile-transport-configuration.component';
import { Lwm2mObjectListComponent } from './lwm2m-object-list.component';
import { Lwm2mObserveAttrTelemetryComponent } from './lwm2m-observe-attr-telemetry.component';
import { Lwm2mObserveAttrTelemetryResourceComponent } from './lwm2m-observe-attr-telemetry-resource.component';
import { Lwm2mDeviceConfigServerComponent } from './lwm2m-device-config-server.component';
import { Lwm2mObjectAddInstancesComponent } from './lwm2m-object-add-instances.component';
import { Lwm2mObjectAddInstancesListComponent } from './lwm2m-object-add-instances-list.component';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@app/shared/shared.module';
import {NgModule} from '@angular/core';
import {Lwm2mDeviceProfileTransportConfigurationComponent} from './lwm2m-device-profile-transport-configuration.component';
import {Lwm2mObjectListComponent} from './lwm2m-object-list.component';
import {Lwm2mObserveAttrTelemetryComponent} from './lwm2m-observe-attr-telemetry.component';
import {Lwm2mObserveAttrTelemetryResourceComponent} from './lwm2m-observe-attr-telemetry-resource.component';
import {Lwm2mAttributesDialogComponent} from './lwm2m-attributes-dialog.component';
import {Lwm2mAttributesComponent} from './lwm2m-attributes.component';
import {Lwm2mAttributesKeyListComponent} from './lwm2m-attributes-key-list.component';
import {Lwm2mDeviceConfigServerComponent} from './lwm2m-device-config-server.component';
import {Lwm2mObjectAddInstancesDialogComponent} from './lwm2m-object-add-instances-dialog.component';
import {Lwm2mObjectAddInstancesListComponent} from './lwm2m-object-add-instances-list.component';
import {CommonModule} from '@angular/common';
import {SharedModule} from '@app/shared/shared.module';
@NgModule({
declarations:
@ -32,8 +35,11 @@ import { SharedModule } from '@app/shared/shared.module';
Lwm2mObjectListComponent,
Lwm2mObserveAttrTelemetryComponent,
Lwm2mObserveAttrTelemetryResourceComponent,
Lwm2mAttributesDialogComponent,
Lwm2mAttributesComponent,
Lwm2mAttributesKeyListComponent,
Lwm2mDeviceConfigServerComponent,
Lwm2mObjectAddInstancesComponent,
Lwm2mObjectAddInstancesDialogComponent,
Lwm2mObjectAddInstancesListComponent
],
imports: [
@ -45,8 +51,11 @@ import { SharedModule } from '@app/shared/shared.module';
Lwm2mObjectListComponent,
Lwm2mObserveAttrTelemetryComponent,
Lwm2mObserveAttrTelemetryResourceComponent,
Lwm2mAttributesDialogComponent,
Lwm2mAttributesComponent,
Lwm2mAttributesKeyListComponent,
Lwm2mDeviceConfigServerComponent,
Lwm2mObjectAddInstancesComponent,
Lwm2mObjectAddInstancesDialogComponent,
Lwm2mObjectAddInstancesListComponent
],
providers: [

70
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts → ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts

@ -16,7 +16,9 @@
export const PAGE_SIZE_LIMIT = 50;
export const INSTANCES = 'instances';
export const INSTANCE = 'instance';
export const RESOURCES = 'resources';
export const ATTRIBUTE_LWM2M = 'attributeLwm2m';
export const CLIENT_LWM2M = 'clientLwM2M';
export const CLIENT_LWM2M_SETTINGS = 'clientLwM2mSettings';
export const OBSERVE_ATTR_TELEMETRY = 'observeAttrTelemetry';
@ -33,7 +35,7 @@ export const DEFAULT_CLIENT_HOLD_OFF_TIME = 1;
export const DEFAULT_LIFE_TIME = 300;
export const DEFAULT_MIN_PERIOD = 1;
export const DEFAULT_NOTIF_IF_DESIBLED = true;
export const DEFAULT_BINDING = 'U';
export const DEFAULT_BINDING = 'UQ';
export const DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT = 0;
export const LEN_MAX_PUBLIC_KEY_RPK = 182;
export const LEN_MAX_PUBLIC_KEY_X509 = 3000;
@ -42,6 +44,44 @@ export const KEY_REGEXP_NUMBER = /^(\-?|\+?)\d*$/;
export const INSTANCES_ID_VALUE_MIN = 0;
export const INSTANCES_ID_VALUE_MAX = 65535;
export enum ATTRIBUTE_LWM2M_ENUM {
dim = 'dim',
ver = 'ver',
pmin = 'pmin',
pmax = 'pmax',
gt = 'gt',
lt = 'lt',
st = 'st'
}
export const ATTRIBUTE_LWM2M_LABEL = new Map<ATTRIBUTE_LWM2M_ENUM, string>(
[
[ATTRIBUTE_LWM2M_ENUM.dim, 'dim='],
[ATTRIBUTE_LWM2M_ENUM.ver, 'ver='],
[ATTRIBUTE_LWM2M_ENUM.pmin, 'pmin='],
[ATTRIBUTE_LWM2M_ENUM.pmax, 'pmax='],
[ATTRIBUTE_LWM2M_ENUM.gt, '>'],
[ATTRIBUTE_LWM2M_ENUM.lt, '<'],
[ATTRIBUTE_LWM2M_ENUM.st, 'st='],
]
);
export const ATTRIBUTE_LWM2M_MAP = new Map<ATTRIBUTE_LWM2M_ENUM, string>(
[
[ATTRIBUTE_LWM2M_ENUM.dim, 'Dimension'],
[ATTRIBUTE_LWM2M_ENUM.ver, 'Object version'],
[ATTRIBUTE_LWM2M_ENUM.pmin, 'Minimum period'],
[ATTRIBUTE_LWM2M_ENUM.pmax, 'Maximum period'],
[ATTRIBUTE_LWM2M_ENUM.gt, 'Greater than'],
[ATTRIBUTE_LWM2M_ENUM.lt, 'Lesser than'],
[ATTRIBUTE_LWM2M_ENUM.st, 'Step'],
]
);
export const ATTRIBUTE_KEYS = Object.keys(ATTRIBUTE_LWM2M_ENUM) as string[];
export enum SECURITY_CONFIG_MODE {
PSK = 'PSK',
RPK = 'RPK',
@ -54,7 +94,7 @@ export const SECURITY_CONFIG_MODE_NAMES = new Map<SECURITY_CONFIG_MODE, string>(
[SECURITY_CONFIG_MODE.PSK, 'Pre-Shared Key'],
[SECURITY_CONFIG_MODE.RPK, 'Raw Public Key'],
[SECURITY_CONFIG_MODE.X509, 'X.509 Certificate'],
[SECURITY_CONFIG_MODE.NO_SEC, 'No Security'],
[SECURITY_CONFIG_MODE.NO_SEC, 'No Security']
]
);
@ -90,7 +130,7 @@ interface BootstrapSecurityConfig {
lwm2mServer: ServerSecurityConfig;
}
export interface ProfileConfigModels {
export interface Lwm2mProfileConfigModels {
clientLwM2mSettings: ClientLwM2mSettings;
observeAttr: ObservableAttributes;
bootstrap: BootstrapSecurityConfig;
@ -100,11 +140,13 @@ export interface ProfileConfigModels {
export interface ClientLwM2mSettings {
clientOnlyObserveAfterConnect: number;
}
export interface ObservableAttributes {
observe: string[];
attribute: string[];
telemetry: string[];
keyName: {};
attributeLwm2m: {};
}
export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig {
@ -151,21 +193,22 @@ function getDefaultProfileObserveAttrConfig(): ObservableAttributes {
observe: [],
attribute: [],
telemetry: [],
keyName: {}
keyName: {},
attributeLwm2m: {}
};
}
function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings {
export function getDefaultProfileConfig(hostname?: any): Lwm2mProfileConfigModels {
return {
clientOnlyObserveAfterConnect: 1
clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig(),
observeAttr: getDefaultProfileObserveAttrConfig(),
bootstrap: getDefaultProfileBootstrapSecurityConfig((hostname) ? hostname : DEFAULT_HOST_NAME)
};
}
export function getDefaultProfileConfig(hostname?: any): ProfileConfigModels {
function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings {
return {
clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig(),
observeAttr: getDefaultProfileObserveAttrConfig(),
bootstrap: getDefaultProfileBootstrapSecurityConfig((hostname) ? hostname : DEFAULT_HOST_NAME)
clientOnlyObserveAfterConnect: 1
};
}
@ -175,11 +218,13 @@ export interface ResourceLwM2M {
observe: boolean;
attribute: boolean;
telemetry: boolean;
keyName: string;
keyName: {};
attributeLwm2m?: {};
}
export interface Instance {
id: number;
attributeLwm2m?: {};
resources: ResourceLwM2M[];
}
@ -190,11 +235,12 @@ export interface Instance {
* mandatory == false => Optional
*/
export interface ObjectLwM2M {
id: number;
keyId: string;
name: string;
multiple?: boolean;
mandatory?: boolean;
attributeLwm2m?: {};
instances?: Instance [];
}

21
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1171,17 +1171,32 @@
"instances-list": "Instances list",
"instances-input": "Input Instance Id value",
"instances-input-holder": "Input Instance number...",
"resource-label": "Resource",
"instance-label": "Instance",
"resource-label": "<id> Resource name",
"observe-label": "Observe",
"attribute-label": "Attribute",
"telemetry-label": "Telemetry",
"key-name-label": "Key Name",
"attribute-lwm2m-label": "AttrLwm2m",
"resource-tip": "ID & Original Name of the Resource",
"is-observe-tip": "Is Observe",
"not-observe-tip": "To observe select telemetry or attributes first",
"is-attr-tip": "Is Attribute",
"is-telemetry-tip": "Is Telemetry",
"key-name-tip": "Key Name",
"key-name-tip": "Key Name in Camel format",
"attribute-lwm2m-tip": "Attributes Lwm2m",
"attribute-lwm2m-disable-tip": "To edit Attributes Lwm2m select telemetry or attributes first",
"valid-attribute-lwm2m-key": "Name have be only '{{attrEnums}}'",
"valid-attribute-lwm2m-value": "Value have be Long, Double and greater than zero or null/empty",
"no-data": "No attributes",
"key-name": "Key Name",
"key-name_label": "Key Name in Camel format",
"attribute-lwm2m-name": "Name attribute",
"attribute-lwm2m-value": "Value",
"attribute-lwm2m-toolbar-edit": "Edit attributes Lwm2m",
"attribute-lwm2m-toolbar-view": "View Attributes Lwm2m",
"attribute-lwm2m-destination": "Destination:",
"attribute-lwm2m-add-tip": "Add attribute lwm2m",
"attribute-lwm2m-remove-tip": "Remove attribute lwm2m",
"required": " value is required.",
"mode": "Security config mode",
"pattern_hex_dec": "{ count, plural, 0 {must be hex decimal format} other {must be # characters} }",

Loading…
Cancel
Save