UI: Refactoring LwM2M

This commit is contained in:
Vladyslav_Prykhodko 2021-01-26 19:20:36 +02:00
parent f770f40797
commit c14e2c81b1
4 changed files with 94 additions and 129 deletions

View File

@ -82,7 +82,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private deviceProfileService: DeviceProfileService,
@Inject(WINDOW) private window: Window) {
this.lwm2mDeviceProfileFormGroup = this.fb.group({
objectIds: [[], Validators.required],
objectIds: [null, Validators.required],
observeAttrTelemetry: [null, Validators.required],
shortId: [null, Validators.required],
lifetime: [null, Validators.required],
@ -96,15 +96,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
configurationJson: [null, Validators.required]
});
this.lwm2mDeviceProfileFormGroup.valueChanges.subscribe((value) => {
if (!this.disabled) {
this.updateDeviceProfileValue(value);
}
this.updateDeviceProfileValue(value);
});
this.lwm2mDeviceConfigFormGroup.valueChanges.subscribe(() => {
console.warn('config form');
if (!this.disabled) {
this.updateModel();
}
this.updateModel();
});
this.sortFunction = this.sortObjectKeyPathJson;
}
@ -188,7 +183,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private updateDeviceProfileValue(config): void {
if (this.lwm2mDeviceProfileFormGroup.valid) {
this.upDateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M);
this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M);
this.configurationValue.bootstrap.bootstrapServer = config.bootstrapServer;
this.configurationValue.bootstrap.lwm2mServer = config.lwm2mServer;
const bootstrapServers = this.configurationValue.bootstrap.servers;
@ -225,9 +220,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
this.updateKeyNameObjects(keyNameJson, clientObserveAttrTelemetry);
}
}
clientObserveAttrTelemetry.forEach(obj => {
obj.instances.sort((a, b) => a.id - b.id);
});
return {clientLwM2M: clientObserveAttrTelemetry};
}
@ -239,8 +231,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private addInstances = (attribute: string[], telemetry: string[], clientObserveAttrTelemetry: ObjectLwM2M[]): void => {
const instancesPath = attribute.concat(telemetry)
.filter(instance => !instance.includes('/0/'))
.map(instance => this.convertPathToInstance(instance))
.sort();
.map(instance => instance.slice(1, instance.lastIndexOf('/')))
.sort(this.sortPath);
new Set(instancesPath).forEach(path => {
const pathParameter = Array.from(path.split('/'), Number);
@ -253,24 +245,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
});
}
private convertPathToInstance = (path: string): string => {
const [objectId, instanceId] = path.substring(1).split('/');
return `${objectId}/${instanceId}`;
}
private updateObserveAttrTelemetryObjects = (parameters: string[], clientObserveAttrTelemetry: ObjectLwM2M[],
nameParameter: string): void => {
parameters.forEach(parameter => {
const [objectId, instanceId, resourceId] = Array.from(parameter.substring(1).split('/'), Number);
clientObserveAttrTelemetry
.forEach(key => {
if (key.id === objectId) {
const instance = key.instances.find(itrInstance => itrInstance.id === instanceId);
if (isDefinedAndNotNull(instance)) {
instance.resources.find(resource => resource.id === resourceId)[nameParameter] = true;
}
}
});
clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.id === objectId)
.instances.find(itrInstance => itrInstance.id === instanceId)
.resources.find(resource => resource.id === resourceId)
[nameParameter] = true;
});
}
@ -278,62 +260,60 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
const keyName = JSON.parse(JSON.stringify(nameJson));
Object.keys(keyName).forEach(key => {
const [objectId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), Number);
clientObserveAttrTelemetry
.forEach(object => {
if (object.id === objectId) {
object.instances
.find(instance => instance.id === instanceId).resources
.find(resource => resource.id === resourceId).keyName = keyName[key];
}
});
clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.id === objectId)
.instances.find(instance => instance.id === instanceId)
.resources.find(resource => resource.id === resourceId)
.keyName = keyName[key];
});
}
private upDateObserveAttrTelemetryFromGroupToJson = (val: ObjectLwM2M[]): void => {
private updateObserveAttrTelemetryFromGroupToJson = (val: ObjectLwM2M[]): void => {
const observeArray: Array<string> = [];
const attributeArray: Array<string> = [];
const telemetryArray: Array<string> = [];
const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val));
const paths = new Set<string>();
let pathObj;
let pathInst;
let pathRes;
observeJson.forEach(obj => {
Object.entries(obj).forEach(([key, value]) => {
for (const [key, value] of Object.entries(obj)) {
if (key === 'id') {
pathObj = value;
}
if (key === 'instances') {
const instancesJson = JSON.parse(JSON.stringify(value)) as Instance[];
const instancesJson = value as Instance[];
if (instancesJson.length > 0) {
instancesJson.forEach(instance => {
Object.entries(instance).forEach(([instanceKey, instanceValue]) => {
for (const [instanceKey, instanceValue] of Object.entries(instance)) {
if (instanceKey === 'id') {
pathInst = instanceValue;
}
if (instanceKey === 'resources') {
const resourcesJson = JSON.parse(JSON.stringify(instanceValue)) as ResourceLwM2M[];
const resourcesJson = instanceValue as ResourceLwM2M[];
if (resourcesJson.length > 0) {
resourcesJson.forEach(res => {
Object.entries(res).forEach(([resourceKey, resourceValue]) => {
for (const [resourceKey, idResource] of Object.entries(res)) {
if (resourceKey === 'id') {
// pathRes = resourceValue
pathRes = '/' + pathObj + '/' + pathInst + '/' + resourceValue;
} else if (resourceKey === 'observe' && resourceValue) {
pathRes = `/${pathObj}/${pathInst}/${idResource}`;
} else if (resourceKey === 'observe' && idResource) {
observeArray.push(pathRes);
} else if (resourceKey === 'attribute' && resourceValue) {
} else if (resourceKey === 'attribute' && idResource) {
attributeArray.push(pathRes);
} else if (resourceKey === 'telemetry' && resourceValue) {
paths.add(pathRes);
} else if (resourceKey === 'telemetry' && idResource) {
telemetryArray.push(pathRes);
paths.add(pathRes);
}
});
}
});
}
}
});
}
});
}
}
});
}
});
if (isUndefined(this.configurationValue[this.observeAttr])) {
this.configurationValue[this.observeAttr] = {
@ -346,7 +326,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
this.configurationValue[this.observeAttr][this.attribute] = attributeArray;
this.configurationValue[this.observeAttr][this.telemetry] = telemetryArray;
}
this.updateKeyName();
this.updateKeyName(paths);
}
sortObjectKeyPathJson = (key: string, value: object): object => {
@ -366,23 +346,13 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
}
private sortPath = (a, b): number => {
const aLC = Array.from(a.substring(1).split('/'), Number);
const bLC = Array.from(b.substring(1).split('/'), Number);
return aLC[0] === bLC[0] ? aLC[1] - bLC[1] : aLC[0] - bLC[0];
return a.localeCompare(b, undefined, {
numeric: true,
sensitivity: 'base'
});
}
private updateKeyName = (): void => {
const paths = new Set<string>();
if (this.configurationValue[this.observeAttr][this.attribute]) {
this.configurationValue[this.observeAttr][this.attribute].forEach(path => {
paths.add(path);
});
}
if (this.configurationValue[this.observeAttr][this.telemetry]) {
this.configurationValue[this.observeAttr][this.telemetry].forEach(path => {
paths.add(path);
});
}
private updateKeyName = (paths: Set<string>): void => {
const keyNameNew = {};
paths.forEach(path => {
const pathParameter = this.findIndexesForIds(path);
@ -395,19 +365,19 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
}
private findIndexesForIds = (path: string): number[] => {
const pathParameter = Array.from(path.substring(1).split('/'), Number);
const [objectId, instanceId, resourceId] = Array.from(path.substring(1).split('/'), Number);
// TODO: All paths to map
const pathParameterIndexes: number[] = [];
const objectsOld = deepClone(
this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value.clientLwM2M) as ObjectLwM2M[];
let isIdIndex = (element) => element.id === pathParameter[0];
const objectsOld = this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value.clientLwM2M as ObjectLwM2M[];
let isIdIndex = (element) => element.id === objectId;
const objIndex = objectsOld.findIndex(isIdIndex);
if (objIndex >= 0) {
pathParameterIndexes.push(objIndex);
isIdIndex = (element) => element.id === pathParameter[1];
isIdIndex = (element) => element.id === instanceId;
const instIndex = objectsOld[objIndex].instances.findIndex(isIdIndex);
if (instIndex >= 0) {
pathParameterIndexes.push(instIndex);
isIdIndex = (element) => element.id === pathParameter[2];
isIdIndex = (element) => element.id === resourceId;
const resIndex = objectsOld[objIndex].instances[instIndex].resources.findIndex(isIdIndex);
if (resIndex >= 0) {
pathParameterIndexes.push(resIndex);
@ -456,16 +426,16 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
if (index >= 0) {
objectsOld.splice(index, 1);
}
this.updateObserveAttrTelemetryObjectFormGroup(objectsOld);
this.removeObserveAttrTelemetryFromJson(this.observe, value.id);
this.removeObserveAttrTelemetryFromJson(this.telemetry, value.id);
this.removeObserveAttrTelemetryFromJson(this.attribute, value.id);
this.removeKeyNameFromJson(value.id);
this.updateObserveAttrTelemetryObjectFormGroup(objectsOld);
this.upDateJsonAllConfig();
}
private removeObserveAttrTelemetryFromJson = (observeAttrTel: string, id: number): void => {
const isIdIndex = (element: string) => element.startsWith(`/${id}`);
const isIdIndex = (element) => element.startsWith(`/${id}`);
let index = this.configurationValue[this.observeAttr][observeAttrTel].findIndex(isIdIndex);
while (index >= 0) {
this.configurationValue[this.observeAttr][observeAttrTel].splice(index, 1);

View File

@ -19,18 +19,17 @@ import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Valida
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Observable, of } from 'rxjs';
import { filter, map, mergeMap, tap } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { filter, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators';
import { ObjectLwM2M } from './profile-config.models';
import { TranslateService } from '@ngx-translate/core';
import { DeviceProfileService } from '@core/http/device-profile.service';
import { Direction } from '@shared/models/page/sort-order';
import { isDefined, isDefinedAndNotNull, isEmptyStr } from '@core/utils';
import { isDefined, isDefinedAndNotNull, isEmptyStr, isString } from '@core/utils';
@Component({
selector: 'tb-profile-lwm2m-object-list',
templateUrl: './lwm2m-object-list.component.html',
styleUrls: [],
providers: [
{
provide: NG_VALUE_ACCESSOR,
@ -42,10 +41,10 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
private requiredValue: boolean;
private dirty = false;
private allObjectsList: Observable<Array<ObjectLwM2M>>;
private lw2mModels: Observable<Array<ObjectLwM2M>>;
private modelValue: Array<number> = [];
lwm2mListFormGroup: FormGroup;
modelValue: Array<number> | null;
objectsList: Array<ObjectLwM2M> = [];
filteredObjectsList: Observable<Array<ObjectLwM2M>>;
disabled = false;
@ -57,11 +56,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
@Input()
set required(value: boolean) {
const newVal = coerceBooleanProperty(value);
if (this.requiredValue !== newVal) {
this.requiredValue = newVal;
this.updateValidators();
}
this.requiredValue = coerceBooleanProperty(value);
this.updateValidators();
}
@Output()
@ -73,7 +69,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
@ViewChild('objectInput') objectInput: ElementRef<HTMLInputElement>;
private propagateChange = (v: any) => {
};
}
constructor(private store: Store<AppState>,
public translate: TranslateService,
@ -81,7 +77,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
private fb: FormBuilder) {
this.lwm2mListFormGroup = this.fb.group({
objectsList: [this.objectsList],
objectLwm2m: [null, this.required ? [Validators.required] : []]
objectLwm2m: ['']
});
}
@ -104,11 +100,10 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
if (value && typeof value !== 'string') {
this.add(value);
} else if (value === null) {
this.clear(this.objectInput.nativeElement.value);
this.clear();
}
}),
filter((value) => typeof value === 'string'),
// map(value => value ? value : ''),
filter(searchText => isString(searchText)),
mergeMap(searchText => this.fetchListObjects(searchText))
);
}
@ -127,23 +122,21 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
writeValue(value: any): void {
this.searchText = '';
const objectIds = 'objectIds';
if (value.hasOwnProperty(objectIds) && value[objectIds] != null && value[objectIds].length > 0) {
this.modelValue = [...value[objectIds]];
this.objectsList = value.objectsList;
} else {
this.objectsList = [];
this.modelValue = null;
if (isDefinedAndNotNull(value)) {
if (Array.isArray(value.objectIds)) {
this.modelValue = value.objectIds;
this.objectsList = value.objectsList;
} else {
this.objectsList = [];
this.modelValue = [];
}
this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList, {emitEvents: false});
this.dirty = false;
}
this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList);
this.dirty = true;
}
private add(object: ObjectLwM2M): void {
if (!this.modelValue || this.modelValue.indexOf(object.id) === -1) {
if (!this.modelValue) {
this.modelValue = [];
}
if (this.modelValue.indexOf(object.id) === -1) {
this.modelValue.push(object.id);
this.objectsList.push(object);
this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList);
@ -176,41 +169,42 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
const filters = {names: [], ids: []};
if (isDefinedAndNotNull(searchText) && !isEmptyStr(searchText)) {
const ids = searchText.match(/\d+/g);
filters.ids = isDefinedAndNotNull(ids) ? ids.map(Number) as [] : filters.ids;
const names = searchText.trim().split(" ") as [];
filters.names = names;
filters.ids = ids !== null ? ids.map(Number) : filters.ids;
filters.names = searchText.trim().toUpperCase().split(' ');
}
const predicate = objectLwM2M => filters.names.filter(word => objectLwM2M.name.toUpperCase().includes(word.toUpperCase())).length>0
const predicate = objectLwM2M => filters.names.find(word => objectLwM2M.name.toUpperCase().includes(word))
|| filters.ids.includes(objectLwM2M.id);
return this.getListModels().pipe(
return this.getLwM2mModels().pipe(
map(objectLwM2Ms => searchText ? objectLwM2Ms.filter(predicate) : objectLwM2Ms)
);
}
private getListModels(): Observable<Array<ObjectLwM2M>> {
if (!this.allObjectsList) {
private getLwM2mModels(): Observable<Array<ObjectLwM2M>> {
if (!this.lw2mModels) {
const sortOrder = {
property: 'name',
direction: Direction.ASC
};
this.allObjectsList = this.deviceProfileService.getLwm2mObjects(sortOrder, null, null).pipe(
mergeMap(objectsList => of(objectsList))
this.lw2mModels = this.deviceProfileService.getLwm2mObjects(sortOrder).pipe(
publishReplay(1),
refCount()
);
}
return this.allObjectsList;
return this.lw2mModels;
}
onFocus = (): void => {
if (this.dirty) {
if (!this.dirty) {
this.lwm2mListFormGroup.get('objectLwm2m').updateValueAndValidity({onlySelf: true, emitEvent: true});
this.dirty = false;
this.dirty = true;
}
}
private clear = (value: string = ''): void => {
this.objectInput.nativeElement.value = value;
this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value, {emitEvent: true});
this.searchText = '';
this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value);
setTimeout(() => {
this.objectInput.nativeElement.blur();
this.objectInput.nativeElement.focus();

View File

@ -16,23 +16,24 @@
-->
<section [formGroup]="observeAttrTelemetryFormGroup" class="mat-padding">
<section [formGroup]="observeAttrTelemetryFormGroup">
<mat-accordion multi="true" class="mat-body-1" formArrayName="clientLwM2M">
<mat-expansion-panel
*ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index; trackBy: trackByParams"
*ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index;"
[formGroupName]="i">
<mat-expansion-panel-header>
<mat-panel-title fxLayoutAlign="space-between">
<div class="tb-panel-title"><b><i>{{ objectLwM2M.get('name').value}}</i></b> (object [<b>{{ objectLwM2M.get('id').value}}</b>])</div>
<button type="button"
*ngIf="!disabled"
[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>
</button>
<mat-panel-title fxLayoutAlign="start center">
<b><i>{{ objectLwM2M.get('name').value}}</i></b>&nbsp;&lt;id: {{ objectLwM2M.get('id').value}}>
</mat-panel-title>
<mat-panel-description 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>
</button>
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div fxLayout="column" fxLayoutGap="8px" formArrayName="instances">

View File

@ -176,7 +176,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
this.observeAttrTelemetryFormGroup.get('clientLwM2M').updateValueAndValidity();
}
trackByParams = (index: number): number => {
trackByParams = (index: number, element: any): number => {
return index;
}