Browse Source

UI: Refactoring LWM2M device profile transport configuration attributes settings

pull/4780/head
Vladyslav_Prykhodko 5 years ago
parent
commit
c9b83a4c88
  1. 10
      common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java
  2. 15
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.html
  3. 48
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-dialog.component.ts
  4. 83
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.html
  5. 69
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.scss
  6. 224
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes-key-list.component.ts
  7. 10
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.html
  8. 52
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.scss
  9. 80
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-attributes.component.ts
  10. 22
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html
  11. 13
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html
  12. 72
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts
  13. 38
      ui-ngx/src/assets/locale/locale.constant-en_US.json

10
common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java

@ -24,10 +24,10 @@ public class ObjectAttributes {
private Long dim; private Long dim;
private String ver; private String ver;
private Long pmin; private Integer pmin;
private Long pmax; private Integer pmax;
private Double gt; private Float gt;
private Double lt; private Float lt;
private Double st; private Float st;
} }

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

@ -15,12 +15,11 @@
limitations under the License. limitations under the License.
--> -->
<form [formGroup]="attributeLwm2mDialogFormGroup" (ngSubmit)="save()" style="width: 500px;"> <form [formGroup]="attributeFormGroup" (ngSubmit)="save()" style="min-width: 500px;">
<mat-toolbar color="primary"> <mat-toolbar color="primary">
<div fxFlex fxLayout="column" fxLayoutAlign="start"> <h2>
<h2>{{ (readonly ? 'device-profile.lwm2m.attribute-lwm2m-toolbar-view' : {{ (readonly ? 'device-profile.lwm2m.view-attributes' : 'device-profile.lwm2m.edit-attributes') | translate : {name: name} }}
'device-profile.lwm2m.attribute-lwm2m-toolbar-edit') | translate }}</h2> </h2>
</div>
<span fxFlex></span> <span fxFlex></span>
<button mat-icon-button <button mat-icon-button
(click)="cancel()" (click)="cancel()"
@ -32,8 +31,8 @@
</mat-progress-bar> </mat-progress-bar>
<div mat-dialog-content> <div mat-dialog-content>
<tb-lwm2m-attributes-key-list <tb-lwm2m-attributes-key-list
formControlName="keyFilters" [isResource]="isResource"
titleText="{{data.destName}}"> formControlName="attributes">
</tb-lwm2m-attributes-key-list> </tb-lwm2m-attributes-key-list>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
@ -46,7 +45,7 @@
<button mat-raised-button color="primary" <button mat-raised-button color="primary"
*ngIf="!readonly" *ngIf="!readonly"
type="submit" type="submit"
[disabled]="(isLoading$ | async) || attributeLwm2mDialogFormGroup.invalid || !attributeLwm2mDialogFormGroup.dirty"> [disabled]="(isLoading$ | async) || attributeFormGroup.invalid || !attributeFormGroup.dirty">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</button> </button>
</div> </div>

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

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; import { Component, Inject, SkipSelf } from '@angular/core';
import { ErrorStateMatcher } from '@angular/material/core'; import { ErrorStateMatcher } from '@angular/material/core';
import { DialogComponent } from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@ -22,12 +22,13 @@ import { AppState } from '@core/core.state';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms'; import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm } from '@angular/forms';
import { JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; import { AttributesNameValueMap } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
export interface Lwm2mAttributesDialogData { export interface Lwm2mAttributesDialogData {
readonly: boolean; readonly: boolean;
attributeLwm2m: JsonObject; attributes: AttributesNameValueMap;
destName: string; modelName: string;
isResource: boolean;
} }
@Component({ @Component({
@ -36,42 +37,37 @@ export interface Lwm2mAttributesDialogData {
styleUrls: ['./lwm2m-attributes.component.scss'], styleUrls: ['./lwm2m-attributes.component.scss'],
providers: [{provide: ErrorStateMatcher, useExisting: Lwm2mAttributesDialogComponent}], providers: [{provide: ErrorStateMatcher, useExisting: Lwm2mAttributesDialogComponent}],
}) })
export class Lwm2mAttributesDialogComponent extends DialogComponent<Lwm2mAttributesDialogComponent, object> export class Lwm2mAttributesDialogComponent
implements OnInit, ErrorStateMatcher { extends DialogComponent<Lwm2mAttributesDialogComponent, AttributesNameValueMap> implements ErrorStateMatcher {
readonly = this.data.readonly; readonly: boolean;
name: string;
attributeLwm2m = this.data.attributeLwm2m; isResource: boolean;
submitted = false;
dirtyValue = false; private submitted = false;
attributeLwm2mDialogFormGroup: FormGroup; attributeFormGroup: FormGroup;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected router: Router, protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: Lwm2mAttributesDialogData, @Inject(MAT_DIALOG_DATA) private data: Lwm2mAttributesDialogData,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher, @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public dialogRef: MatDialogRef<Lwm2mAttributesDialogComponent, object>, public dialogRef: MatDialogRef<Lwm2mAttributesDialogComponent, AttributesNameValueMap>,
private fb: FormBuilder) { private fb: FormBuilder) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.attributeLwm2mDialogFormGroup = this.fb.group({ this.readonly = data.readonly;
keyFilters: [{}, []] this.name = data.modelName;
}); this.isResource = data.isResource;
this.attributeLwm2mDialogFormGroup.patchValue({keyFilters: this.attributeLwm2m});
this.attributeLwm2mDialogFormGroup.get('keyFilters').valueChanges.subscribe((attributes) => { this.attributeFormGroup = this.fb.group({
this.attributeLwm2m = attributes; attributes: [data.attributes]
}); });
if (this.readonly) { if (this.readonly) {
this.attributeLwm2mDialogFormGroup.disable({emitEvent: false}); this.attributeFormGroup.disable({emitEvent: false});
} }
} }
ngOnInit(): void {
}
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const originalErrorState = this.errorStateMatcher.isErrorState(control, form); const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
const customErrorState = !!(control && control.invalid && this.submitted); const customErrorState = !!(control && control.invalid && this.submitted);
@ -80,7 +76,7 @@ export class Lwm2mAttributesDialogComponent extends DialogComponent<Lwm2mAttribu
save(): void { save(): void {
this.submitted = true; this.submitted = true;
this.dialogRef.close(this.attributeLwm2m); this.dialogRef.close(this.attributeFormGroup.get('attributes').value);
} }
cancel(): void { cancel(): void {

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

@ -15,69 +15,60 @@
limitations under the License. limitations under the License.
--> -->
<section fxLayout="column" class="tb-kv-map" [formGroup]="kvListFormGroup"> <section fxLayout="column" class="name-value-map" [formGroup]="attributesValueFormGroup">
<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;"> <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> <label fxFlex="40" class="tb-title no-padding" style="min-width: 230px;" translate>device-profile.lwm2m.attribute-name</label>
<mat-label fxFlex class="tb-title no-padding" translate>device-profile.lwm2m.attribute-lwm2m-value</mat-label> <label fxFlex="60" class="tb-title no-padding" translate>device-profile.lwm2m.attribute-value</label>
<div [fxShow]="!disabled" style="width: 40px;"></div> <span [fxShow]="!disabled" style="width: 40px;"></span>
</div> </div>
<div fxLayout="column" formArrayName="keyVals" <div fxLayout="column" class="map-list"
*ngFor="let keyValControl of keyValsFormArray().controls; let $index = index"> *ngFor="let nameValueControl of attributesValueFormArray().controls; let $index = index"
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> [formGroup]="nameValueControl">
<mat-form-field class="mat-block" style="max-heights: 400px"> <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field fxFlex="40" floatLabel="always" hideRequiredMarker>
<mat-label></mat-label> <mat-label></mat-label>
<mat-select [formControl]="keyValControl.get('key')"> <mat-select formControlName="name" required>
<mat-option *ngFor="let attributeLwm2m of attrKeys" <mat-option *ngFor="let attributeName of attributeNames" [value]="attributeName"
[value]="attributeLwm2m"> [disabled]="isDisabledAttributeName(attributeName, $index)">
{{ attributeLwm2mMap.get(attrKey[attributeLwm2m]) }} {{ attributeNameTranslationMap.get(attributeName) | translate }}
</mat-option> </mat-option>
</mat-select> </mat-select>
<mat-error *ngIf="nameValueControl.get('name').hasError('required')">
{{ 'device-profile.lwm2m.attribute-name-required' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field fxFlex floatLabel="always" hideRequiredMarker class="mat-block" <mat-form-field fxFlex="60" floatLabel="always" hideRequiredMarker>
style="max-height: 40px;">
<mat-label></mat-label> <mat-label></mat-label>
<input [formControl]="keyValControl.get('value')" matInput <input formControlName="value" matInput required
placeholder="{{ ('key-val.value') | translate }}"/> placeholder="{{ 'key-val.value' | translate }}">
<mat-error fxLayout="row" *ngIf="nameValueControl.get('value').hasError('required')">
{{ 'device-profile.lwm2m.attribute-value-required' | translate }}
</mat-error>
<mat-error fxLayout="row" *ngIf="nameValueControl.get('value').hasError('min') ||
nameValueControl.get('value').hasError('pattern')">
{{ 'device-profile.lwm2m.attribute-value-pattern' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<button mat-button mat-icon-button color="primary" <button *ngIf="!disabled"
[fxShow]="!disabled" mat-icon-button color="primary" style="min-width: 40px;"
type="button" type="button"
(click)="removeKeyVal($index)" (click)="removeKeyVal($index)"
[disabled]="isLoading$ | async" matTooltip="{{ 'device-profile.lwm2m.remove-attribute' | translate }}"
matTooltip="{{ 'device-profile.lwm2m.attribute-lwm2m-remove-tip' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>
</button> </button>
</div> </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> </div>
<span [fxShow]="!keyValsFormArray().length" <div [fxShow]="!attributesValueFormArray().length"
fxLayoutAlign="center center" [ngClass]="{'disabled': disabled}" fxLayoutAlign="center center"
class="no-data-found" translate>{{noDataText ? noDataText : 'device-profile.lwm2m.no-data'}}</span> class="map-list" translate>device-profile.lwm2m.no-attributes-set</div>
<div style="margin-top: 8px;"> <div style="margin-top: 9px;" *ngIf="!disabled && isAddEnabled">
<button mat-button mat-raised-button color="primary" <button mat-stroked-button color="primary"
[fxShow]="!disabled"
[disabled]="isLoading$ | async" [disabled]="isLoading$ | async"
(click)="addKeyVal()"
type="button" type="button"
matTooltip="{{ 'device-profile.lwm2m.attribute-lwm2m-add-tip' | translate }}" (click)="addKeyVal()">
matTooltipPosition="above"> <mat-icon class="button-icon">add_circle_outline</mat-icon>
{{ 'action.add' | translate }} {{ 'device-profile.lwm2m.add-attribute' | translate }}
</button> </button>
</div> </div>
</section> </section>

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

@ -0,0 +1,69 @@
/**
* 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 {
.name-value-map {
span.no-data-found {
position: relative;
display: flex;
height: 40px;
&.disabled {
color: rgba(0, 0, 0, .38);
}
}
.map-list{
height: 45px;
}
}
}
:host ::ng-deep {
.mat-form-field-wrapper {
padding-bottom: 0;
}
.mat-form-field-infix {
border-top: 0;
}
.mat-form-field-underline {
bottom: 0;
}
.button-icon{
font-size: 20px;
width: 20px;
height: 20px;
}
.map-list {
mat-form-field {
.mat-form-field-wrapper {
padding-bottom: 0;
.mat-form-field-infix {
border-top-width: 0.2em;
width: auto;
min-width: auto;
}
.mat-form-field-underline {
bottom: 0;
}
.mat-form-field-subscript-wrapper{
margin-top: 1.8em;
}
}
}
}
}

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

@ -14,35 +14,36 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, forwardRef, Input, OnDestroy } from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
FormArray, FormArray,
FormBuilder, FormBuilder,
FormControl,
FormGroup, FormGroup,
NG_VALIDATORS, NG_VALIDATORS,
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
Validator, Validator,
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs';
import { import {
ATTRIBUTE_KEYS, AttributeName,
ATTRIBUTE_LWM2M_ENUM, AttributeNameTranslationMap,
ATTRIBUTE_LWM2M_MAP AttributesNameValue,
AttributesNameValueMap,
valueValidatorByAttributeName
} from './lwm2m-profile-config.models'; } from './lwm2m-profile-config.models';
import { isDefinedAndNotNull, isEmpty, isEmptyStr, isUndefinedOrNull } from '@core/utils'; import { isUndefinedOrNull } from '@core/utils';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'tb-lwm2m-attributes-key-list', selector: 'tb-lwm2m-attributes-key-list',
templateUrl: './lwm2m-attributes-key-list.component.html', templateUrl: './lwm2m-attributes-key-list.component.html',
styleUrls: ['./lwm2m-attributes.component.scss'], styleUrls: ['./lwm2m-attributes-key-list.component.scss'],
providers: [ providers: [
{ {
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
@ -56,39 +57,46 @@ import { PageComponent } from '@shared/components/page.component';
} }
] ]
}) })
export class Lwm2mAttributesKeyListComponent extends PageComponent implements ControlValueAccessor, OnInit, Validator { export class Lwm2mAttributesKeyListComponent extends PageComponent implements ControlValueAccessor, OnDestroy, OnDestroy, Validator {
attrKeys = ATTRIBUTE_KEYS;
attrKey = ATTRIBUTE_LWM2M_ENUM;
attributeLwm2mMap = ATTRIBUTE_LWM2M_MAP; attributeNames;
attributeNameTranslationMap = AttributeNameTranslationMap;
@Input() disabled: boolean; @Input() disabled: boolean;
@Input() titleText: string; @Input()
isResource = false;
@Input() noDataText: string; attributesValueFormGroup: FormGroup;
kvListFormGroup: FormGroup;
private propagateChange = null; private propagateChange = null;
private valueChange$: Subscription = null;
private valueChangeSubscription: Subscription = null; private destroy$ = new Subject();
private usedAttributesName: AttributeName[] = [];
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private fb: FormBuilder) { private fb: FormBuilder) {
super(store); super(store);
this.attributesValueFormGroup = this.fb.group({
attributesValue: this.fb.array([])
});
} }
ngOnInit(): void { ngOnInit() {
this.kvListFormGroup = this.fb.group({}); if (this.isResource) {
this.kvListFormGroup.addControl('keyVals', this.attributeNames = Object.values(AttributeName);
this.fb.array([])); } else {
this.attributeNames = Object.values(AttributeName)
.filter(item => ![AttributeName.lt, AttributeName.gt, AttributeName.st].includes(item));
}
} }
keyValsFormArray(): FormArray { ngOnDestroy() {
return this.kvListFormGroup.get('keyVals') as FormArray; if (this.valueChange$) {
this.valueChange$.unsubscribe();
}
this.destroy$.next();
this.destroy$.complete();
} }
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
@ -101,127 +109,111 @@ export class Lwm2mAttributesKeyListComponent extends PageComponent implements Co
setDisabledState(isDisabled: boolean): void { setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled; this.disabled = isDisabled;
if (this.disabled) { if (this.disabled) {
this.kvListFormGroup.disable({emitEvent: false}); this.attributesValueFormGroup.disable({emitEvent: false});
} else { } else {
this.kvListFormGroup.enable({emitEvent: false}); this.attributesValueFormGroup.enable({emitEvent: false});
} }
} }
writeValue(keyValMap: { [key: string]: string }): void { writeValue(keyValMap: AttributesNameValueMap): void {
if (this.valueChangeSubscription) { if (this.valueChange$) {
this.valueChangeSubscription.unsubscribe(); this.valueChange$.unsubscribe();
} }
const keyValsControls: Array<AbstractControl> = []; const attributesValueControls: Array<AbstractControl> = [];
if (keyValMap) { if (keyValMap) {
for (const property of Object.keys(keyValMap)) { (Object.keys(keyValMap) as AttributeName[]).forEach(name => {
if (Object.prototype.hasOwnProperty.call(keyValMap, property)) { attributesValueControls.push(this.createdFormGroup({name, value: keyValMap[name]}));
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.attributesValueFormGroup.setControl('attributesValue', this.fb.array(attributesValueControls));
this.valueChangeSubscription = this.kvListFormGroup.valueChanges.subscribe(() => {
// this.updateValidate();
this.updateModel();
});
if (this.disabled) { if (this.disabled) {
this.kvListFormGroup.disable({emitEvent: false}); this.attributesValueFormGroup.disable({emitEvent: false});
} else { } else {
this.kvListFormGroup.enable({emitEvent: false}); this.attributesValueFormGroup.enable({emitEvent: false});
} }
this.valueChange$ = this.attributesValueFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
this.updateUsedAttributesName();
}
attributesValueFormArray(): FormArray {
return this.attributesValueFormGroup.get('attributesValue') as FormArray;
} }
public removeKeyVal(index: number) { public removeKeyVal(index: number) {
(this.kvListFormGroup.get('keyVals') as FormArray).removeAt(index); this.attributesValueFormArray().removeAt(index);
} }
public addKeyVal() { public addKeyVal() {
const keyValsFormArray = this.kvListFormGroup.get('keyVals') as FormArray; this.attributesValueFormArray().push(this.createdFormGroup());
keyValsFormArray.push(this.fb.group({ this.attributesValueFormGroup.updateValueAndValidity({emitEvent: false});
key: ['', [Validators.required, this.attributeLwm2mKeyValidator]], if (this.attributesValueFormGroup.invalid) {
value: ['', []] this.updateModel();
}));
}
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() { private createdFormGroup(value?: AttributesNameValue): FormGroup {
const kvList = this.kvListFormGroup.get('keyVals') as FormArray; if (isUndefinedOrNull(value)) {
kvList.controls.forEach(fg => { value = {
if (fg.get('key').value === 'ver') { name: this.getFirstUnusedAttributesName(),
fg.get('value').setValidators(null); value: null
fg.get('value').setErrors(null); };
} }
else { const form = this.fb.group({
fg.get('value').setValidators(this.attributeLwm2mValueNumberValidator); name: [value.name, Validators.required],
fg.get('value').setErrors(this.attributeLwm2mValueNumberValidator(fg.get('value'))); value: [value.value, valueValidatorByAttributeName(value.name)]
}
}); });
form.get('name').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(name => {
form.get('value').setValidators(valueValidatorByAttributeName(name));
form.get('value').updateValueAndValidity();
});
return form;
}
public validate() {
return this.attributesValueFormGroup.valid ? null : {
attributesValue: {
valid: false
}
};
} }
private updateModel() { private updateModel() {
this.updateValidate(); const value: AttributesNameValue[] = this.attributesValueFormGroup.get('attributesValue').value;
if (this.validate() === null) { const attributesNameValueMap: AttributesNameValueMap = {};
const kvList: { key: string; value: string }[] = this.kvListFormGroup.get('keyVals').value; value.forEach(attribute => {
const keyValMap: { [key: string]: string | number } = {}; attributesNameValueMap[attribute.name] = attribute.value;
kvList.forEach((entry) => { });
if (isUndefinedOrNull(entry.value) || entry.key === 'ver' || isEmptyStr(entry.value.toString())) { this.updateUsedAttributesName();
keyValMap[entry.key] = entry.value.toString(); this.propagateChange(attributesNameValueMap);
} else {
keyValMap[entry.key] = Number(entry.value);
}
});
this.propagateChange(keyValMap);
}
else {
this.propagateChange(null);
}
} }
public isDisabledAttributeName(type: AttributeName, index: number): boolean {
const usedIndex = this.usedAttributesName.indexOf(type);
return usedIndex > -1 && usedIndex !== index;
}
private attributeLwm2mKeyValidator = (control: AbstractControl) => { private getFirstUnusedAttributesName(): AttributeName {
const key = control.value as string; for (const attributeName of this.attributeNames) {
if (isDefinedAndNotNull(key) && !isEmpty(key)) { if (this.usedAttributesName.indexOf(attributeName) === -1) {
if (!ATTRIBUTE_KEYS.includes(key)) { return attributeName;
return {
validAttributeKey: true
};
} }
} }
return null; return null;
} }
private attributeLwm2mValueNumberValidator = (control: AbstractControl) => { private updateUsedAttributesName() {
if (isNaN(Number(control.value)) || Number(control.value) < 0) { this.usedAttributesName = [];
return { const value: AttributesNameValue[] = this.attributesValueFormGroup.get('attributesValue').value;
validAttributeValue: true value.forEach((attributesValue, index) => {
}; this.usedAttributesName[index] = attributesValue.name;
} });
return null;
} }
private attributeLwm2mValueValidator = (property: string): object[] => { get isAddEnabled(): boolean {
return property === 'ver' ? [] : [this.attributeLwm2mValueNumberValidator]; return this.attributesValueFormArray().length !== this.attributeNames.length;
} }
} }

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

@ -15,18 +15,14 @@
limitations under the License. limitations under the License.
--> -->
<div fxLayout="row" [formGroup]="attributeLwm2mFormGroup"> <div fxLayout="row" [fxHide]="disabled && isEmpty()" fxLayoutAlign="end center" matTooltip="{{ tooltipSetAttributesTelemetry | translate }}" matTooltipPosition="above">
<div fxFlex fxLayout="column" class="resource-name-lw-end" fxLayoutAlign="center"
[matTooltip]="isToolTipLabel()" matTooltipPosition="above">
{{attributeLwm2mToString()}}
</div>
<button type="button" <button type="button"
[disabled]="isDisableBtn()" [disabled]="isDisableBtn()"
mat-button mat-icon-button mat-button mat-icon-button
(click)="editAttributesLwm2m($event)" (click)="editAttributesLwm2m($event)"
[matTooltip]="(isIconView() ? 'action.view' : isIconEditAdd() ? 'action.edit' : 'action.add' ) | translate" matTooltip="{{ tooltipButton | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon <mat-icon
class="material-icons">{{isIconView() ? 'visibility' : isIconEditAdd() ? 'edit' : 'add' }}</mat-icon> class="material-icons">{{ iconButton }}</mat-icon>
</button> </button>
</div> </div>

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

@ -14,48 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
:host { :host {
.tb-kv-map { .resource-name-lw-end{
span.no-data-found { white-space: nowrap;
position: relative; overflow: hidden;
display: flex; text-overflow: ellipsis;
height: 40px; text-align:end;
//width: 80px;
&.disabled { cursor: pointer;
color: rgba(0, 0, 0, .38);
}
}
} }
}
:host ::ng-deep { .resource-name-lw{
.mat-form-field-wrapper { white-space: nowrap;
padding-bottom: 0; overflow: hidden;
} text-overflow: ellipsis;
.mat-form-field-infix { cursor: pointer;
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;
} }

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

@ -17,11 +17,10 @@
import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core'; import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { deepClone, isDefinedAndNotNull, isEmpty } from '@core/utils'; import { isEmpty, isUndefinedOrNull } from '@core/utils';
import { Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData } from './lwm2m-attributes-dialog.component'; import { Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData } from './lwm2m-attributes-dialog.component';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core'; import { AttributesNameValueMap } from './lwm2m-profile-config.models';
import { ATTRIBUTE_LWM2M_LABEL } from './lwm2m-profile-config.models';
@Component({ @Component({
@ -36,22 +35,21 @@ import { ATTRIBUTE_LWM2M_LABEL } from './lwm2m-profile-config.models';
}) })
export class Lwm2mAttributesComponent implements ControlValueAccessor { export class Lwm2mAttributesComponent implements ControlValueAccessor {
attributeLwm2mFormGroup: FormGroup; attributeLwm2mFormGroup: FormGroup;
attributeLwm2mLabel = ATTRIBUTE_LWM2M_LABEL;
private requiredValue: boolean; private requiredValue: boolean;
@Input()
attributeLwm2m: {};
@Input() @Input()
isAttributeTelemetry: boolean; isAttributeTelemetry: boolean;
@Input() @Input()
destName: string; modelName: string;
@Input() @Input()
disabled: boolean; disabled: boolean;
@Input()
isResource = false;
@Output() @Output()
updateAttributeLwm2m = new EventEmitter<any>(); updateAttributeLwm2m = new EventEmitter<any>();
@ -64,8 +62,7 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor {
} }
constructor(private dialog: MatDialog, constructor(private dialog: MatDialog,
private fb: FormBuilder, private fb: FormBuilder) {}
private translate: TranslateService) {}
registerOnChange(fn: any): void { registerOnChange(fn: any): void {
this.propagateChange = fn; this.propagateChange = fn;
@ -85,63 +82,66 @@ export class Lwm2mAttributesComponent implements ControlValueAccessor {
ngOnInit() { ngOnInit() {
this.attributeLwm2mFormGroup = this.fb.group({ this.attributeLwm2mFormGroup = this.fb.group({
attributeLwm2m: [this.attributeLwm2m] attributes: [{}]
}); });
} }
writeValue(value: {} | null): void {} writeValue(value: AttributesNameValueMap | null) {
this.attributeLwm2mFormGroup.patchValue({attributes: value}, {emitEvent: false});
attributeLwm2mToString = (): string => {
return this.isIconEditAdd () ? this.attributeLwm2mLabelToString() : this.translate.instant('device-profile.lwm2m.no-data');
} }
private attributeLwm2mLabelToString = (): string => { get attributesValueMap(): AttributesNameValueMap {
let label = JSON.stringify(this.attributeLwm2m); return this.attributeLwm2mFormGroup.get('attributes').value;
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 { isDisableBtn(): boolean {
return this.disabled || this.isAttributeTelemetry ? !(isDefinedAndNotNull(this.attributeLwm2m) && return !this.disabled && this.isAttributeTelemetry;
!isEmpty(this.attributeLwm2m) && this.disabled) : this.disabled;
} }
isIconView(): boolean { isEmpty(): boolean {
return this.isAttributeTelemetry || this.disabled; const value = this.attributesValueMap;
return isUndefinedOrNull(value) || isEmpty(value);
} }
isIconEditAdd(): boolean { get tooltipSetAttributesTelemetry(): string {
return isDefinedAndNotNull(this.attributeLwm2m) && !isEmpty(this.attributeLwm2m); return this.isDisableBtn() ? 'device-profile.lwm2m.edit-attributes-select' : '';
} }
isToolTipLabel(): string { get tooltipButton(): string {
return this.disabled ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip') : if (this.disabled) {
this.isAttributeTelemetry ? this.translate.instant('device-profile.lwm2m.attribute-lwm2m-disable-tip') : return 'device-profile.lwm2m.view-attribute';
this.translate.instant('device-profile.lwm2m.attribute-lwm2m-tip'); } else if (this.isEmpty()) {
return 'device-profile.lwm2m.add-attribute';
}
return 'device-profile.lwm2m.edit-attribute';
}
get iconButton(): string {
if (this.disabled) {
return 'visibility';
} else if (this.isEmpty()) {
return 'add';
}
return 'edit';
} }
public editAttributesLwm2m = ($event: Event): void => { public editAttributesLwm2m = ($event: Event): void => {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
this.dialog.open<Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData, object>(Lwm2mAttributesDialogComponent, { this.dialog.open<Lwm2mAttributesDialogComponent, Lwm2mAttributesDialogData, AttributesNameValueMap>(Lwm2mAttributesDialogComponent, {
disableClose: true, disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: { data: {
readonly: this.disabled, readonly: this.disabled,
attributeLwm2m: this.disabled ? this.attributeLwm2m : deepClone(this.attributeLwm2m), attributes: this.attributesValueMap,
destName: this.destName modelName: this.modelName,
isResource: this.isResource
} }
}).afterClosed().subscribe((result) => { }).afterClosed().subscribe((result) => {
if (result) { if (result) {
this.attributeLwm2m = result; this.attributeLwm2mFormGroup.patchValue({attributeLwm2m: result});
this.attributeLwm2mFormGroup.patchValue({attributeLwm2m: this.attributeLwm2m}); this.updateAttributeLwm2m.next(result);
this.updateAttributeLwm2m.next(this.attributeLwm2m);
} }
}); });
} }

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

@ -20,27 +20,24 @@
*ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams"> *ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams">
<div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i"> <div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i">
<div fxLayout="row" fxFill fxLayoutAlign="start center" [fxShow]="!i"> <div fxLayout="row" fxFill fxLayoutAlign="start center" [fxShow]="!i">
<div fxFlex="20"> <div fxFlex="30">
<mat-label translate>device-profile.lwm2m.resource-label</mat-label> <mat-label translate>device-profile.lwm2m.resource-label</mat-label>
</div> </div>
<div fxFlex="10"> <div fxFlex="10" style="text-align: center">
<mat-label translate>device-profile.lwm2m.attribute-label</mat-label> <mat-label translate>device-profile.lwm2m.attribute-label</mat-label>
</div> </div>
<div fxFlex="10"> <div fxFlex="10" style="text-align: center">
<mat-label translate>device-profile.lwm2m.telemetry-label</mat-label> <mat-label translate>device-profile.lwm2m.telemetry-label</mat-label>
</div> </div>
<div fxFlex="10"> <div fxFlex="10" style="text-align: center">
<mat-label translate>device-profile.lwm2m.observe-label</mat-label> <mat-label translate>device-profile.lwm2m.observe-label</mat-label>
</div> </div>
<div fxFlex> <div fxFlex>
<mat-label translate>device-profile.lwm2m.key-name-label</mat-label> <mat-label translate>device-profile.lwm2m.key-name-label</mat-label>
</div> </div>
<div fxFlex="17" fxFlexOffset="0">
<mat-label translate>device-profile.lwm2m.attribute-lwm2m-label</mat-label>
</div>
</div> </div>
<div fxLayout="row" fxFill fxLayoutAlign="start center"> <div fxLayout="row" fxFill fxLayoutAlign="start center">
<div class="resource-name-lw" fxFlex="25" <div class="resource-name-lw" fxFlex="30"
matTooltip="{{'device-profile.lwm2m.resource-tip' | translate}}" matTooltipPosition="above"> matTooltip="{{'device-profile.lwm2m.resource-tip' | translate}}" matTooltipPosition="above">
<<b>{{resourceLwM2M.get('id').value}}</b>> <b><i>{{resourceLwM2M.get('name').value}}</i></b> <<b>{{resourceLwM2M.get('id').value}}</b>> <b><i>{{resourceLwM2M.get('name').value}}</i></b>
</div> </div>
@ -65,7 +62,7 @@
matTooltipPosition="above"> matTooltipPosition="above">
</mat-checkbox> </mat-checkbox>
</div> </div>
<mat-form-field fxFlex="25"> <mat-form-field fxFlex="33">
<mat-label *ngIf="resourceLwM2M.get('keyName').hasError('required')"> <mat-label *ngIf="resourceLwM2M.get('keyName').hasError('required')">
{{ 'device-profile.lwm2m.key-name-label' | translate }}</mat-label> {{ 'device-profile.lwm2m.key-name-label' | translate }}</mat-label>
<input class="resource-name-lw" matInput type="text" formControlName="keyName" required <input class="resource-name-lw" matInput type="text" formControlName="keyName" required
@ -77,12 +74,13 @@
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<div fxFlex="20" class="resource-name-lw-end" fxFlexOffset="5"> <span fxFlex></span>
<div class="resource-name-lw-end">
<tb-profile-lwm2m-attributes <tb-profile-lwm2m-attributes
formControlName="attributeLwm2m" formControlName="attributeLwm2m"
[attributeLwm2m]="resourceLwM2M.get('attributeLwm2m').value"
[isAttributeTelemetry]="disableObserve(i)" [isAttributeTelemetry]="disableObserve(i)"
[destName]="getNameResourceLwm2m(resourceLwM2M.value)" isResource="true"
[modelName]="getNameResourceLwm2m(resourceLwM2M.value)"
[disabled]="this.disabled" [disabled]="this.disabled"
(updateAttributeLwm2m)="updateAttributeLwm2m($event, i)"> (updateAttributeLwm2m)="updateAttributeLwm2m($event, i)">
</tb-profile-lwm2m-attributes> </tb-profile-lwm2m-attributes>

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

@ -26,17 +26,15 @@
<div fxFlex class="resource-name-lw-end"> <div fxFlex class="resource-name-lw-end">
<tb-profile-lwm2m-attributes <tb-profile-lwm2m-attributes
formControlName="attributeLwm2m" formControlName="attributeLwm2m"
[attributeLwm2m]="objectLwM2M.get('attributeLwm2m').value"
[isAttributeTelemetry]="disableObserveObject(i)" [isAttributeTelemetry]="disableObserveObject(i)"
[destName]="getNameObjectLwm2m( objectLwM2M.get('name').value, objectLwM2M.get('keyId').value)" [modelName]="getNameObjectLwm2m( objectLwM2M.get('name').value, objectLwM2M.get('keyId').value)"
[disabled]="this.disabled" [disabled]="this.disabled"
(updateAttributeLwm2m)="updateAttributeLwm2mObject($event, objectLwM2M.get('keyId').value)"> (updateAttributeLwm2m)="updateAttributeLwm2mObject($event, objectLwM2M.get('keyId').value)">
</tb-profile-lwm2m-attributes> </tb-profile-lwm2m-attributes>
</div> </div>
</mat-panel-title> </mat-panel-title>
<mat-panel-description fxFlex="5" fxLayoutAlign="end center" *ngIf="!disabled"> <mat-panel-description fxFlex="5" fxLayoutAlign="end center" *ngIf="!disabled && objectLwM2M.get('multiple').value">
<button type="button" <button type="button"
[fxShow]="objectLwM2M.get('multiple').value"
mat-button mat-icon-button (click)="addInstances($event, objectLwM2M.value)" mat-button mat-icon-button (click)="addInstances($event, objectLwM2M.value)"
matTooltip="{{'device-profile.lwm2m.add-instances-tip' | translate}}" matTooltip="{{'device-profile.lwm2m.add-instances-tip' | translate}}"
matTooltipPosition="above"> matTooltipPosition="above">
@ -56,7 +54,7 @@
<mat-panel-title> <mat-panel-title>
<div class="tb-panel-title-height" fxFlex="100"> <div class="tb-panel-title-height" fxFlex="100">
<div fxLayout="row" fxFill> <div fxLayout="row" fxFill>
<div fxFlex="22"> <div fxFlex="30">
{{'device-profile.lwm2m.instance-label' | translate}} <<b>{{instances.get('id').value}}</b>> {{'device-profile.lwm2m.instance-label' | translate}} <<b>{{instances.get('id').value}}</b>>
</div> </div>
<div class="checkbox-padding" fxFlex="10"> <div class="checkbox-padding" fxFlex="10">
@ -95,14 +93,13 @@
matTooltipPosition="above"> matTooltipPosition="above">
</mat-checkbox> </mat-checkbox>
</div> </div>
<div fxFlex="10"> <div fxFlex="7">
</div> </div>
<div fxFlex="37" class="resource-name-lw-end" fxFlexOffset="5"> <div fxFlex="37" class="resource-name-lw-end" fxFlexOffset="5">
<tb-profile-lwm2m-attributes <tb-profile-lwm2m-attributes
formControlName="attributeLwm2m" formControlName="attributeLwm2m"
[attributeLwm2m]="instances.get('attributeLwm2m').value"
[isAttributeTelemetry]="disableObserveInstance(instances)" [isAttributeTelemetry]="disableObserveInstance(instances)"
[destName]="getNameInstanceLwm2m(instances.value, objectLwM2M.get('keyId').value)" [modelName]="getNameInstanceLwm2m(instances.value, objectLwM2M.get('keyId').value)"
[disabled]="this.disabled" [disabled]="this.disabled"
(updateAttributeLwm2m)="updateAttributeLwm2mInstance($event, y, objectLwM2M.get('keyId').value)"> (updateAttributeLwm2m)="updateAttributeLwm2mInstance($event, y, objectLwM2M.get('keyId').value)">
</tb-profile-lwm2m-attributes> </tb-profile-lwm2m-attributes>

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

@ -14,6 +14,8 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { ValidatorFn, Validators } from '@angular/forms';
export const PAGE_SIZE_LIMIT = 50; export const PAGE_SIZE_LIMIT = 50;
export const INSTANCES = 'instances'; export const INSTANCES = 'instances';
export const INSTANCE = 'instance'; export const INSTANCE = 'instance';
@ -74,44 +76,29 @@ export const BingingModeTranslationsMap = new Map<BingingMode, string>(
[BingingMode.SQ, 'device-profile.lwm2m.binding-type.sq'] [BingingMode.SQ, 'device-profile.lwm2m.binding-type.sq']
] ]
); );
// TODO: wait release Leshan for issues: https://github.com/eclipse/leshan/issues/1026
export enum ATTRIBUTE_LWM2M_ENUM { export enum AttributeName {
dim = 'dim',
ver = 'ver',
pmin = 'pmin', pmin = 'pmin',
pmax = 'pmax', pmax = 'pmax',
gt = 'gt', gt = 'gt',
lt = 'lt', lt = 'lt',
st = 'st' st = 'st'
// epmin = 'epmin',
// epmax = 'epmax'
} }
export const ATTRIBUTE_LWM2M_LABEL = new Map<ATTRIBUTE_LWM2M_ENUM, string>( export const AttributeNameTranslationMap = new Map<AttributeName, string>(
[ [
[ATTRIBUTE_LWM2M_ENUM.dim, 'dim='], [AttributeName.pmin, 'device-profile.lwm2m.attributes-name.min-period'],
[ATTRIBUTE_LWM2M_ENUM.ver, 'ver='], [AttributeName.pmax, 'device-profile.lwm2m.attributes-name.max-period'],
[ATTRIBUTE_LWM2M_ENUM.pmin, 'pmin='], [AttributeName.gt, 'device-profile.lwm2m.attributes-name.greater-than'],
[ATTRIBUTE_LWM2M_ENUM.pmax, 'pmax='], [AttributeName.lt, 'device-profile.lwm2m.attributes-name.less-than'],
[ATTRIBUTE_LWM2M_ENUM.gt, '>'], [AttributeName.st, 'device-profile.lwm2m.attributes-name.step'],
[ATTRIBUTE_LWM2M_ENUM.lt, '<'], // [AttributeName.epmin, 'device-profile.lwm2m.attributes-name.min-evaluation-period'],
[ATTRIBUTE_LWM2M_ENUM.st, 'st='] // [AttributeName.epmax, 'device-profile.lwm2m.attributes-name.max-evaluation-period']
]
);
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 securityConfigMode { export enum securityConfigMode {
PSK = 'PSK', PSK = 'PSK',
RPK = 'RPK', RPK = 'RPK',
@ -182,7 +169,7 @@ export interface ObservableAttributes {
attribute: string[]; attribute: string[];
telemetry: string[]; telemetry: string[];
keyName: {}; keyName: {};
attributeLwm2m: {}; attributeLwm2m?: AttributesNameValueMap;
} }
export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig { export function getDefaultBootstrapServersSecurityConfig(): BootstrapServersSecurityConfig {
@ -241,12 +228,12 @@ export interface ResourceLwM2M {
attribute: boolean; attribute: boolean;
telemetry: boolean; telemetry: boolean;
keyName: string; keyName: string;
attributeLwm2m?: {}; attributeLwm2m?: AttributesNameValueMap;
} }
export interface Instance { export interface Instance {
id: number; id: number;
attributeLwm2m?: {}; attributeLwm2m?: AttributesNameValueMap;
resources: ResourceLwM2M[]; resources: ResourceLwM2M[];
} }
@ -257,12 +244,33 @@ export interface Instance {
* mandatory == false => Optional * mandatory == false => Optional
*/ */
export interface ObjectLwM2M { export interface ObjectLwM2M {
id: number; id: number;
keyId: string; keyId: string;
name: string; name: string;
multiple?: boolean; multiple?: boolean;
mandatory?: boolean; mandatory?: boolean;
attributeLwm2m?: {}; attributeLwm2m?: AttributesNameValueMap;
instances?: Instance []; instances?: Instance [];
} }
export type AttributesNameValueMap = {
[key in AttributeName]?: number;
};
export interface AttributesNameValue {
name: AttributeName;
value: number;
}
export function valueValidatorByAttributeName(attributeName: AttributeName): ValidatorFn[] {
const validators = [Validators.required];
switch (attributeName) {
case AttributeName.pmin:
case AttributeName.pmax:
// case AttributeName.epmin:
// case AttributeName.epmax:
validators.push(Validators.min(0), Validators.pattern('[0-9]*'));
break;
}
return validators;
}

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

@ -1231,27 +1231,26 @@
"attribute-label": "Attribute", "attribute-label": "Attribute",
"telemetry-label": "Telemetry", "telemetry-label": "Telemetry",
"key-name-label": "Key Name", "key-name-label": "Key Name",
"attribute-lwm2m-label": "AttrLwm2m",
"resource-tip": "ID & Original Name of the Resource (only Operations isReadable)", "resource-tip": "ID & Original Name of the Resource (only Operations isReadable)",
"is-observe-tip": "Is Observe", "is-observe-tip": "Is Observe",
"not-observe-tip": "To observe select telemetry or attributes first", "not-observe-tip": "To observe select telemetry or attributes first",
"is-attr-tip": "Is Attribute", "is-attr-tip": "Is Attribute",
"is-telemetry-tip": "Is Telemetry", "is-telemetry-tip": "Is Telemetry",
"key-name-tip": "Key Name in Camel format", "key-name-tip": "Key Name in Camel format",
"attribute-lwm2m-tip": "Attributes Lwm2m", "edit-attributes-select": "To edit attributes select telemetry or attributes",
"attribute-lwm2m-disable-tip": "To edit Attributes Lwm2m select telemetry or attributes first", "no-attributes-set": "No attributes set",
"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": "Key Name",
"attribute-lwm2m-name": "Name attribute", "attribute-name": "Name attribute",
"attribute-lwm2m-value": "Value", "attribute-name-required": "Name attribute is required.",
"attribute-lwm2m-toolbar-edit": "Edit attributes Lwm2m", "attribute-value": "Attribute value",
"attribute-lwm2m-toolbar-view": "View Attributes Lwm2m", "attribute-value-required": "Attribute value is required.",
"attribute-lwm2m-destination": "Destination:", "attribute-value-pattern": "Attribute value must be a positive integer.",
"attribute-lwm2m-add-tip": "Add attribute lwm2m", "edit-attributes": "Edit attributes: {{ name }}",
"attribute-lwm2m-remove-tip": "Remove attribute lwm2m", "view-attributes": "View attributes: {{ name }}",
"required": " value is required.", "add-attribute": "Add attribute",
"edit-attribute": "Edit attribute",
"view-attribute": "View attribute",
"remove-attribute": "Remove attribute",
"mode": "Security config mode", "mode": "Security config mode",
"pattern_hex_dec": "{ count, plural, 0 {must be hex decimal format} other {must be # characters} }", "pattern_hex_dec": "{ count, plural, 0 {must be hex decimal format} other {must be # characters} }",
"servers": "Servers", "servers": "Servers",
@ -1318,7 +1317,16 @@
"fw-update-recourse-required": "Firmware update CoAP recourse is required.", "fw-update-recourse-required": "Firmware update CoAP recourse is required.",
"sw-update-recourse": "Software update CoAP recourse", "sw-update-recourse": "Software update CoAP recourse",
"sw-update-recourse-required": "Software update CoAP recourse is required.", "sw-update-recourse-required": "Software update CoAP recourse is required.",
"config-json-tab": "Json Config Profile Device" "config-json-tab": "Json Config Profile Device",
"attributes-name": {
"min-period": "Minimum period",
"max-period": "Maximum period",
"greater-than": "Greater than",
"less-than": "Less than",
"step": "Step",
"min-evaluation-period": "Minimum evaluation period",
"max-evaluation-period": "Maximum evaluation period"
}
}, },
"snmp": { "snmp": {
"add-communication-config": "Add communication config", "add-communication-config": "Add communication config",

Loading…
Cancel
Save