Commit c14e2c81b1840a9160496b0baa431fb1e13d66be

Authored by Vladyslav_Prykhodko
1 parent f770f407

UI: Refactoring LwM2M

... ... @@ -82,7 +82,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
82 82 private deviceProfileService: DeviceProfileService,
83 83 @Inject(WINDOW) private window: Window) {
84 84 this.lwm2mDeviceProfileFormGroup = this.fb.group({
85   - objectIds: [[], Validators.required],
  85 + objectIds: [null, Validators.required],
86 86 observeAttrTelemetry: [null, Validators.required],
87 87 shortId: [null, Validators.required],
88 88 lifetime: [null, Validators.required],
... ... @@ -96,15 +96,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
96 96 configurationJson: [null, Validators.required]
97 97 });
98 98 this.lwm2mDeviceProfileFormGroup.valueChanges.subscribe((value) => {
99   - if (!this.disabled) {
100   - this.updateDeviceProfileValue(value);
101   - }
  99 + this.updateDeviceProfileValue(value);
102 100 });
103 101 this.lwm2mDeviceConfigFormGroup.valueChanges.subscribe(() => {
104   - console.warn('config form');
105   - if (!this.disabled) {
106   - this.updateModel();
107   - }
  102 + this.updateModel();
108 103 });
109 104 this.sortFunction = this.sortObjectKeyPathJson;
110 105 }
... ... @@ -188,7 +183,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
188 183
189 184 private updateDeviceProfileValue(config): void {
190 185 if (this.lwm2mDeviceProfileFormGroup.valid) {
191   - this.upDateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M);
  186 + this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M);
192 187 this.configurationValue.bootstrap.bootstrapServer = config.bootstrapServer;
193 188 this.configurationValue.bootstrap.lwm2mServer = config.lwm2mServer;
194 189 const bootstrapServers = this.configurationValue.bootstrap.servers;
... ... @@ -225,9 +220,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
225 220 this.updateKeyNameObjects(keyNameJson, clientObserveAttrTelemetry);
226 221 }
227 222 }
228   - clientObserveAttrTelemetry.forEach(obj => {
229   - obj.instances.sort((a, b) => a.id - b.id);
230   - });
231 223 return {clientLwM2M: clientObserveAttrTelemetry};
232 224 }
233 225
... ... @@ -239,8 +231,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
239 231 private addInstances = (attribute: string[], telemetry: string[], clientObserveAttrTelemetry: ObjectLwM2M[]): void => {
240 232 const instancesPath = attribute.concat(telemetry)
241 233 .filter(instance => !instance.includes('/0/'))
242   - .map(instance => this.convertPathToInstance(instance))
243   - .sort();
  234 + .map(instance => instance.slice(1, instance.lastIndexOf('/')))
  235 + .sort(this.sortPath);
244 236
245 237 new Set(instancesPath).forEach(path => {
246 238 const pathParameter = Array.from(path.split('/'), Number);
... ... @@ -253,24 +245,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
253 245 });
254 246 }
255 247
256   - private convertPathToInstance = (path: string): string => {
257   - const [objectId, instanceId] = path.substring(1).split('/');
258   - return `${objectId}/${instanceId}`;
259   - }
260   -
261 248 private updateObserveAttrTelemetryObjects = (parameters: string[], clientObserveAttrTelemetry: ObjectLwM2M[],
262 249 nameParameter: string): void => {
263 250 parameters.forEach(parameter => {
264 251 const [objectId, instanceId, resourceId] = Array.from(parameter.substring(1).split('/'), Number);
265   - clientObserveAttrTelemetry
266   - .forEach(key => {
267   - if (key.id === objectId) {
268   - const instance = key.instances.find(itrInstance => itrInstance.id === instanceId);
269   - if (isDefinedAndNotNull(instance)) {
270   - instance.resources.find(resource => resource.id === resourceId)[nameParameter] = true;
271   - }
272   - }
273   - });
  252 + clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.id === objectId)
  253 + .instances.find(itrInstance => itrInstance.id === instanceId)
  254 + .resources.find(resource => resource.id === resourceId)
  255 + [nameParameter] = true;
274 256 });
275 257 }
276 258
... ... @@ -278,62 +260,60 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
278 260 const keyName = JSON.parse(JSON.stringify(nameJson));
279 261 Object.keys(keyName).forEach(key => {
280 262 const [objectId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), Number);
281   - clientObserveAttrTelemetry
282   - .forEach(object => {
283   - if (object.id === objectId) {
284   - object.instances
285   - .find(instance => instance.id === instanceId).resources
286   - .find(resource => resource.id === resourceId).keyName = keyName[key];
287   - }
288   - });
  263 + clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.id === objectId)
  264 + .instances.find(instance => instance.id === instanceId)
  265 + .resources.find(resource => resource.id === resourceId)
  266 + .keyName = keyName[key];
289 267 });
290 268 }
291 269
292   - private upDateObserveAttrTelemetryFromGroupToJson = (val: ObjectLwM2M[]): void => {
  270 + private updateObserveAttrTelemetryFromGroupToJson = (val: ObjectLwM2M[]): void => {
293 271 const observeArray: Array<string> = [];
294 272 const attributeArray: Array<string> = [];
295 273 const telemetryArray: Array<string> = [];
296 274 const observeJson: ObjectLwM2M[] = JSON.parse(JSON.stringify(val));
  275 + const paths = new Set<string>();
297 276 let pathObj;
298 277 let pathInst;
299 278 let pathRes;
300 279 observeJson.forEach(obj => {
301   - Object.entries(obj).forEach(([key, value]) => {
  280 + for (const [key, value] of Object.entries(obj)) {
302 281 if (key === 'id') {
303 282 pathObj = value;
304 283 }
305 284 if (key === 'instances') {
306   - const instancesJson = JSON.parse(JSON.stringify(value)) as Instance[];
  285 + const instancesJson = value as Instance[];
307 286 if (instancesJson.length > 0) {
308 287 instancesJson.forEach(instance => {
309   - Object.entries(instance).forEach(([instanceKey, instanceValue]) => {
  288 + for (const [instanceKey, instanceValue] of Object.entries(instance)) {
310 289 if (instanceKey === 'id') {
311 290 pathInst = instanceValue;
312 291 }
313 292 if (instanceKey === 'resources') {
314   - const resourcesJson = JSON.parse(JSON.stringify(instanceValue)) as ResourceLwM2M[];
  293 + const resourcesJson = instanceValue as ResourceLwM2M[];
315 294 if (resourcesJson.length > 0) {
316 295 resourcesJson.forEach(res => {
317   - Object.entries(res).forEach(([resourceKey, resourceValue]) => {
  296 + for (const [resourceKey, idResource] of Object.entries(res)) {
318 297 if (resourceKey === 'id') {
319   - // pathRes = resourceValue
320   - pathRes = '/' + pathObj + '/' + pathInst + '/' + resourceValue;
321   - } else if (resourceKey === 'observe' && resourceValue) {
  298 + pathRes = `/${pathObj}/${pathInst}/${idResource}`;
  299 + } else if (resourceKey === 'observe' && idResource) {
322 300 observeArray.push(pathRes);
323   - } else if (resourceKey === 'attribute' && resourceValue) {
  301 + } else if (resourceKey === 'attribute' && idResource) {
324 302 attributeArray.push(pathRes);
325   - } else if (resourceKey === 'telemetry' && resourceValue) {
  303 + paths.add(pathRes);
  304 + } else if (resourceKey === 'telemetry' && idResource) {
326 305 telemetryArray.push(pathRes);
  306 + paths.add(pathRes);
327 307 }
328   - });
  308 + }
329 309 });
330 310 }
331 311 }
332   - });
  312 + }
333 313 });
334 314 }
335 315 }
336   - });
  316 + }
337 317 });
338 318 if (isUndefined(this.configurationValue[this.observeAttr])) {
339 319 this.configurationValue[this.observeAttr] = {
... ... @@ -346,7 +326,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
346 326 this.configurationValue[this.observeAttr][this.attribute] = attributeArray;
347 327 this.configurationValue[this.observeAttr][this.telemetry] = telemetryArray;
348 328 }
349   - this.updateKeyName();
  329 + this.updateKeyName(paths);
350 330 }
351 331
352 332 sortObjectKeyPathJson = (key: string, value: object): object => {
... ... @@ -366,23 +346,13 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
366 346 }
367 347
368 348 private sortPath = (a, b): number => {
369   - const aLC = Array.from(a.substring(1).split('/'), Number);
370   - const bLC = Array.from(b.substring(1).split('/'), Number);
371   - return aLC[0] === bLC[0] ? aLC[1] - bLC[1] : aLC[0] - bLC[0];
  349 + return a.localeCompare(b, undefined, {
  350 + numeric: true,
  351 + sensitivity: 'base'
  352 + });
372 353 }
373 354
374   - private updateKeyName = (): void => {
375   - const paths = new Set<string>();
376   - if (this.configurationValue[this.observeAttr][this.attribute]) {
377   - this.configurationValue[this.observeAttr][this.attribute].forEach(path => {
378   - paths.add(path);
379   - });
380   - }
381   - if (this.configurationValue[this.observeAttr][this.telemetry]) {
382   - this.configurationValue[this.observeAttr][this.telemetry].forEach(path => {
383   - paths.add(path);
384   - });
385   - }
  355 + private updateKeyName = (paths: Set<string>): void => {
386 356 const keyNameNew = {};
387 357 paths.forEach(path => {
388 358 const pathParameter = this.findIndexesForIds(path);
... ... @@ -395,19 +365,19 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
395 365 }
396 366
397 367 private findIndexesForIds = (path: string): number[] => {
398   - const pathParameter = Array.from(path.substring(1).split('/'), Number);
  368 + const [objectId, instanceId, resourceId] = Array.from(path.substring(1).split('/'), Number);
  369 + // TODO: All paths to map
399 370 const pathParameterIndexes: number[] = [];
400   - const objectsOld = deepClone(
401   - this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value.clientLwM2M) as ObjectLwM2M[];
402   - let isIdIndex = (element) => element.id === pathParameter[0];
  371 + const objectsOld = this.lwm2mDeviceProfileFormGroup.get('observeAttrTelemetry').value.clientLwM2M as ObjectLwM2M[];
  372 + let isIdIndex = (element) => element.id === objectId;
403 373 const objIndex = objectsOld.findIndex(isIdIndex);
404 374 if (objIndex >= 0) {
405 375 pathParameterIndexes.push(objIndex);
406   - isIdIndex = (element) => element.id === pathParameter[1];
  376 + isIdIndex = (element) => element.id === instanceId;
407 377 const instIndex = objectsOld[objIndex].instances.findIndex(isIdIndex);
408 378 if (instIndex >= 0) {
409 379 pathParameterIndexes.push(instIndex);
410   - isIdIndex = (element) => element.id === pathParameter[2];
  380 + isIdIndex = (element) => element.id === resourceId;
411 381 const resIndex = objectsOld[objIndex].instances[instIndex].resources.findIndex(isIdIndex);
412 382 if (resIndex >= 0) {
413 383 pathParameterIndexes.push(resIndex);
... ... @@ -456,16 +426,16 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
456 426 if (index >= 0) {
457 427 objectsOld.splice(index, 1);
458 428 }
459   - this.updateObserveAttrTelemetryObjectFormGroup(objectsOld);
460 429 this.removeObserveAttrTelemetryFromJson(this.observe, value.id);
461 430 this.removeObserveAttrTelemetryFromJson(this.telemetry, value.id);
462 431 this.removeObserveAttrTelemetryFromJson(this.attribute, value.id);
463 432 this.removeKeyNameFromJson(value.id);
  433 + this.updateObserveAttrTelemetryObjectFormGroup(objectsOld);
464 434 this.upDateJsonAllConfig();
465 435 }
466 436
467 437 private removeObserveAttrTelemetryFromJson = (observeAttrTel: string, id: number): void => {
468   - const isIdIndex = (element: string) => element.startsWith(`/${id}`);
  438 + const isIdIndex = (element) => element.startsWith(`/${id}`);
469 439 let index = this.configurationValue[this.observeAttr][observeAttrTel].findIndex(isIdIndex);
470 440 while (index >= 0) {
471 441 this.configurationValue[this.observeAttr][observeAttrTel].splice(index, 1);
... ...
... ... @@ -19,18 +19,17 @@ import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Valida
19 19 import { coerceBooleanProperty } from '@angular/cdk/coercion';
20 20 import { Store } from '@ngrx/store';
21 21 import { AppState } from '@core/core.state';
22   -import { Observable, of } from 'rxjs';
23   -import { filter, map, mergeMap, tap } from 'rxjs/operators';
  22 +import { Observable } from 'rxjs';
  23 +import { filter, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators';
24 24 import { ObjectLwM2M } from './profile-config.models';
25 25 import { TranslateService } from '@ngx-translate/core';
26 26 import { DeviceProfileService } from '@core/http/device-profile.service';
27 27 import { Direction } from '@shared/models/page/sort-order';
28   -import { isDefined, isDefinedAndNotNull, isEmptyStr } from '@core/utils';
  28 +import { isDefined, isDefinedAndNotNull, isEmptyStr, isString } from '@core/utils';
29 29
30 30 @Component({
31 31 selector: 'tb-profile-lwm2m-object-list',
32 32 templateUrl: './lwm2m-object-list.component.html',
33   - styleUrls: [],
34 33 providers: [
35 34 {
36 35 provide: NG_VALUE_ACCESSOR,
... ... @@ -42,10 +41,10 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
42 41
43 42 private requiredValue: boolean;
44 43 private dirty = false;
45   - private allObjectsList: Observable<Array<ObjectLwM2M>>;
  44 + private lw2mModels: Observable<Array<ObjectLwM2M>>;
  45 + private modelValue: Array<number> = [];
46 46
47 47 lwm2mListFormGroup: FormGroup;
48   - modelValue: Array<number> | null;
49 48 objectsList: Array<ObjectLwM2M> = [];
50 49 filteredObjectsList: Observable<Array<ObjectLwM2M>>;
51 50 disabled = false;
... ... @@ -57,11 +56,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
57 56
58 57 @Input()
59 58 set required(value: boolean) {
60   - const newVal = coerceBooleanProperty(value);
61   - if (this.requiredValue !== newVal) {
62   - this.requiredValue = newVal;
63   - this.updateValidators();
64   - }
  59 + this.requiredValue = coerceBooleanProperty(value);
  60 + this.updateValidators();
65 61 }
66 62
67 63 @Output()
... ... @@ -73,7 +69,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
73 69 @ViewChild('objectInput') objectInput: ElementRef<HTMLInputElement>;
74 70
75 71 private propagateChange = (v: any) => {
76   - };
  72 + }
77 73
78 74 constructor(private store: Store<AppState>,
79 75 public translate: TranslateService,
... ... @@ -81,7 +77,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
81 77 private fb: FormBuilder) {
82 78 this.lwm2mListFormGroup = this.fb.group({
83 79 objectsList: [this.objectsList],
84   - objectLwm2m: [null, this.required ? [Validators.required] : []]
  80 + objectLwm2m: ['']
85 81 });
86 82 }
87 83
... ... @@ -104,11 +100,10 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
104 100 if (value && typeof value !== 'string') {
105 101 this.add(value);
106 102 } else if (value === null) {
107   - this.clear(this.objectInput.nativeElement.value);
  103 + this.clear();
108 104 }
109 105 }),
110   - filter((value) => typeof value === 'string'),
111   - // map(value => value ? value : ''),
  106 + filter(searchText => isString(searchText)),
112 107 mergeMap(searchText => this.fetchListObjects(searchText))
113 108 );
114 109 }
... ... @@ -127,23 +122,21 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
127 122
128 123 writeValue(value: any): void {
129 124 this.searchText = '';
130   - const objectIds = 'objectIds';
131   - if (value.hasOwnProperty(objectIds) && value[objectIds] != null && value[objectIds].length > 0) {
132   - this.modelValue = [...value[objectIds]];
133   - this.objectsList = value.objectsList;
134   - } else {
135   - this.objectsList = [];
136   - this.modelValue = null;
  125 + if (isDefinedAndNotNull(value)) {
  126 + if (Array.isArray(value.objectIds)) {
  127 + this.modelValue = value.objectIds;
  128 + this.objectsList = value.objectsList;
  129 + } else {
  130 + this.objectsList = [];
  131 + this.modelValue = [];
  132 + }
  133 + this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList, {emitEvents: false});
  134 + this.dirty = false;
137 135 }
138   - this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList);
139   - this.dirty = true;
140 136 }
141 137
142 138 private add(object: ObjectLwM2M): void {
143   - if (!this.modelValue || this.modelValue.indexOf(object.id) === -1) {
144   - if (!this.modelValue) {
145   - this.modelValue = [];
146   - }
  139 + if (this.modelValue.indexOf(object.id) === -1) {
147 140 this.modelValue.push(object.id);
148 141 this.objectsList.push(object);
149 142 this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList);
... ... @@ -176,41 +169,42 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
176 169 const filters = {names: [], ids: []};
177 170 if (isDefinedAndNotNull(searchText) && !isEmptyStr(searchText)) {
178 171 const ids = searchText.match(/\d+/g);
179   - filters.ids = isDefinedAndNotNull(ids) ? ids.map(Number) as [] : filters.ids;
180   - const names = searchText.trim().split(" ") as [];
181   - filters.names = names;
  172 + filters.ids = ids !== null ? ids.map(Number) : filters.ids;
  173 + filters.names = searchText.trim().toUpperCase().split(' ');
182 174 }
183   - const predicate = objectLwM2M => filters.names.filter(word => objectLwM2M.name.toUpperCase().includes(word.toUpperCase())).length>0
  175 + const predicate = objectLwM2M => filters.names.find(word => objectLwM2M.name.toUpperCase().includes(word))
184 176 || filters.ids.includes(objectLwM2M.id);
185   - return this.getListModels().pipe(
  177 + return this.getLwM2mModels().pipe(
186 178 map(objectLwM2Ms => searchText ? objectLwM2Ms.filter(predicate) : objectLwM2Ms)
187 179 );
188 180 }
189 181
190   - private getListModels(): Observable<Array<ObjectLwM2M>> {
191   - if (!this.allObjectsList) {
  182 + private getLwM2mModels(): Observable<Array<ObjectLwM2M>> {
  183 + if (!this.lw2mModels) {
192 184 const sortOrder = {
193 185 property: 'name',
194 186 direction: Direction.ASC
195 187 };
196   - this.allObjectsList = this.deviceProfileService.getLwm2mObjects(sortOrder, null, null).pipe(
197   - mergeMap(objectsList => of(objectsList))
  188 + this.lw2mModels = this.deviceProfileService.getLwm2mObjects(sortOrder).pipe(
  189 + publishReplay(1),
  190 + refCount()
198 191 );
199 192 }
200   - return this.allObjectsList;
  193 + return this.lw2mModels;
201 194 }
202 195
203 196
204 197 onFocus = (): void => {
205   - if (this.dirty) {
  198 + if (!this.dirty) {
206 199 this.lwm2mListFormGroup.get('objectLwm2m').updateValueAndValidity({onlySelf: true, emitEvent: true});
207   - this.dirty = false;
  200 + this.dirty = true;
208 201 }
209 202 }
210 203
211 204 private clear = (value: string = ''): void => {
212 205 this.objectInput.nativeElement.value = value;
213   - this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value, {emitEvent: true});
  206 + this.searchText = '';
  207 + this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value);
214 208 setTimeout(() => {
215 209 this.objectInput.nativeElement.blur();
216 210 this.objectInput.nativeElement.focus();
... ...
... ... @@ -16,23 +16,24 @@
16 16
17 17 -->
18 18
19   -<section [formGroup]="observeAttrTelemetryFormGroup" class="mat-padding">
  19 +<section [formGroup]="observeAttrTelemetryFormGroup">
20 20 <mat-accordion multi="true" class="mat-body-1" formArrayName="clientLwM2M">
21 21 <mat-expansion-panel
22   - *ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index; trackBy: trackByParams"
  22 + *ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index;"
23 23 [formGroupName]="i">
24 24 <mat-expansion-panel-header>
25   - <mat-panel-title fxLayoutAlign="space-between">
26   - <div class="tb-panel-title"><b><i>{{ objectLwM2M.get('name').value}}</i></b> (object [<b>{{ objectLwM2M.get('id').value}}</b>])</div>
27   - <button type="button"
28   - *ngIf="!disabled"
29   - [fxShow]="objectLwM2M.get('multiple').value"
30   - mat-button mat-icon-button (click)="addInstances($event, objectLwM2M.value)"
31   - matTooltip="{{'device-profile.lwm2m.add-instances-tip' | translate}}"
32   - matTooltipPosition="above">
33   - <mat-icon class="material-icons">{{'add'}}</mat-icon>
34   - </button>
  25 + <mat-panel-title fxLayoutAlign="start center">
  26 + <b><i>{{ objectLwM2M.get('name').value}}</i></b>&nbsp;&lt;id: {{ objectLwM2M.get('id').value}}>
35 27 </mat-panel-title>
  28 + <mat-panel-description fxLayoutAlign="end center" *ngIf="!disabled">
  29 + <button type="button"
  30 + [fxShow]="objectLwM2M.get('multiple').value"
  31 + mat-button mat-icon-button (click)="addInstances($event, objectLwM2M.value)"
  32 + matTooltip="{{'device-profile.lwm2m.add-instances-tip' | translate}}"
  33 + matTooltipPosition="above">
  34 + <mat-icon class="material-icons">{{'add'}}</mat-icon>
  35 + </button>
  36 + </mat-panel-description>
36 37 </mat-expansion-panel-header>
37 38 <ng-template matExpansionPanelContent>
38 39 <div fxLayout="column" fxLayoutGap="8px" formArrayName="instances">
... ...
... ... @@ -176,7 +176,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
176 176 this.observeAttrTelemetryFormGroup.get('clientLwM2M').updateValueAndValidity();
177 177 }
178 178
179   - trackByParams = (index: number): number => {
  179 + trackByParams = (index: number, element: any): number => {
180 180 return index;
181 181 }
182 182
... ...