diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index f7d01db377..e9b6b17edc 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -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 = []; const attributeArray: Array = []; const telemetryArray: Array = []; const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val)); + const paths = new Set(); 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(); - 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): 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); diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts index 625f9c77d9..3062c52ed3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts @@ -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>; + private lw2mModels: Observable>; + private modelValue: Array = []; lwm2mListFormGroup: FormGroup; - modelValue: Array | null; objectsList: Array = []; filteredObjectsList: Observable>; 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; private propagateChange = (v: any) => { - }; + } constructor(private store: Store, 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> { - if (!this.allObjectsList) { + private getLwM2mModels(): Observable> { + 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(); diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html index eb0e4bc88d..7b33264ba6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html @@ -16,23 +16,24 @@ --> -
+
- -
{{ objectLwM2M.get('name').value}} (object [{{ objectLwM2M.get('id').value}}])
- + + {{ objectLwM2M.get('name').value}} <id: {{ objectLwM2M.get('id').value}}> + + +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts index 4eeeb4b3e5..b208ed7e14 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts @@ -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; }