From 6c631a9f146516fd7dbbc839928eb8ea8a74bce7 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 21 Jul 2021 14:53:50 +0300 Subject: [PATCH] UI: Add power mode settings in LwM2M device profile and refactoring lwm2m device profile transport form --- .../home/components/home-components.module.ts | 11 +- .../common/device-profile-common.module.ts | 34 ++++ ...ile-transport-configuration.component.html | 25 +-- ...ofile-transport-configuration.component.ts | 150 ++++++++---------- ...object-add-instances-dialog.component.html | 3 +- ...m-object-add-instances-list.component.html | 44 +++-- ...m2m-object-add-instances-list.component.ts | 104 +++++++++--- .../lwm2m/lwm2m-object-list.component.html | 5 +- .../lwm2m/lwm2m-object-list.component.ts | 16 +- .../lwm2m/lwm2m-profile-components.module.ts | 4 +- .../lwm2m/lwm2m-profile-config.models.ts | 4 +- ...ice-transport-configuration.component.html | 22 +-- ...evice-transport-configuration.component.ts | 38 ++--- .../home/pages/device/device.module.ts | 2 + ui-ngx/src/app/shared/models/device.models.ts | 26 ++- .../assets/locale/locale.constant-en_US.json | 8 +- 16 files changed, 266 insertions(+), 230 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/profile/device/common/device-profile-common.module.ts diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 735e11c2c8..71b983b4d4 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -141,7 +141,7 @@ import { DashboardImageDialogComponent } from '@home/components/dashboard-page/d import { WidgetContainerComponent } from '@home/components/widget/widget-container.component'; import { SnmpDeviceProfileTransportModule } from '@home/components/profile/device/snpm/snmp-device-profile-transport.module'; import { DeviceCredentialsModule } from '@home/components/device/device-credentials.module'; -import { PowerModeSettingComponent } from '@home/components/profile/device/common/power-mode-setting.component'; +import { DeviceProfileCommonModule } from '@home/components/profile/device/common/device-profile-common.module'; @NgModule({ declarations: @@ -260,8 +260,7 @@ import { PowerModeSettingComponent } from '@home/components/profile/device/commo DashboardStateDialogComponent, DashboardImageDialogComponent, EmbedDashboardDialogComponent, - DisplayWidgetTypesPanelComponent, - PowerModeSettingComponent + DisplayWidgetTypesPanelComponent ], imports: [ CommonModule, @@ -270,7 +269,8 @@ import { PowerModeSettingComponent } from '@home/components/profile/device/commo Lwm2mProfileComponentsModule, SnmpDeviceProfileTransportModule, StatesControllerModule, - DeviceCredentialsModule + DeviceCredentialsModule, + DeviceProfileCommonModule ], exports: [ EntitiesTableComponent, @@ -371,8 +371,7 @@ import { PowerModeSettingComponent } from '@home/components/profile/device/commo DashboardStateDialogComponent, DashboardImageDialogComponent, EmbedDashboardDialogComponent, - DisplayWidgetTypesPanelComponent, - PowerModeSettingComponent + DisplayWidgetTypesPanelComponent ], providers: [ WidgetComponentService, diff --git a/ui-ngx/src/app/modules/home/components/profile/device/common/device-profile-common.module.ts b/ui-ngx/src/app/modules/home/components/profile/device/common/device-profile-common.module.ts new file mode 100644 index 0000000000..81bdcc49af --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/common/device-profile-common.module.ts @@ -0,0 +1,34 @@ +/// +/// Copyright © 2016-2021 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { NgModule } from '@angular/core'; +import { PowerModeSettingComponent } from '@home/components/profile/device/common/power-mode-setting.component'; +import { SharedModule } from '@shared/shared.module'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + declarations: [ + PowerModeSettingComponent + ], + imports: [ + CommonModule, + SharedModule + ], + exports: [ + PowerModeSettingComponent + ] +}) +export class DeviceProfileCommonModule { } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index 6e4f4f3929..834071dfb4 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -132,7 +132,7 @@ {{ 'device-profile.lwm2m.fw-update-strategy-data' | translate }} - + {{ 'device-profile.lwm2m.fw-update-resource' | translate }} @@ -149,7 +149,7 @@ {{ 'device-profile.lwm2m.sw-update-strategy-package-uri' | translate }} - + {{ 'device-profile.lwm2m.sw-update-resource' | translate }} @@ -159,25 +159,8 @@
device-profile.power-saving-mode - - - - - {{ powerModeTranslationMap.get(powerMod) | translate}} - - - - - {{ 'device-profile.edrx-cycle' | translate }} - - - {{ 'device-profile.edrx-cycle-required' | translate }} - - - {{ 'device-profile.edrx-cycle-pattern' | translate }} - - + +
{{ 'device-profile.lwm2m.composite-operations-support' | translate }} 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 1fd6835cb7..0934a9b6e2 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 @@ -16,7 +16,15 @@ import { DeviceProfileTransportConfiguration } from '@shared/models/device.models'; import { Component, forwardRef, Input, OnDestroy } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ATTRIBUTE, @@ -30,10 +38,7 @@ import { DEFAULT_NOTIF_IF_DESIBLED, DEFAULT_SW_UPDATE_RESOURCE, getDefaultBootstrapServerSecurityConfig, - getDefaultBootstrapServersSecurityConfig, getDefaultLwM2MServerSecurityConfig, - getDefaultProfileClientLwM2mSettingsConfig, - getDefaultProfileObserveAttrConfig, Instance, INSTANCES, KEY_NAME, @@ -41,7 +46,6 @@ import { ObjectLwM2M, OBSERVE, PowerMode, - PowerModeTranslationMap, RESOURCES, ServerSecurityConfig, TELEMETRY @@ -58,13 +62,19 @@ import { takeUntil } from 'rxjs/operators'; selector: 'tb-profile-lwm2m-device-transport-configuration', templateUrl: './lwm2m-device-profile-transport-configuration.component.html', styleUrls: ['./lwm2m-device-profile-transport-configuration.component.scss'], - providers: [{ - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => Lwm2mDeviceProfileTransportConfigurationComponent), - multi: true - }] + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => Lwm2mDeviceProfileTransportConfigurationComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => Lwm2mDeviceProfileTransportConfigurationComponent), + multi: true + }] }) -export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validators, OnDestroy { +export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validator, OnDestroy { private configurationValue: Lwm2mProfileConfigModels; private requiredValue: boolean; @@ -76,10 +86,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro lwm2mDeviceProfileFormGroup: FormGroup; lwm2mDeviceConfigFormGroup: FormGroup; sortFunction: (key: string, value: object) => object; - isFwUpdateStrategy: boolean; - isSwUpdateStrategy: boolean; - powerMods = Object.values(PowerMode); - powerModeTranslationMap = PowerModeTranslationMap; get required(): boolean { return this.requiredValue; @@ -116,7 +122,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro fwUpdateResource: [{value: '', disabled: true}, []], swUpdateResource: [{value: '', disabled: true}, []], powerMode: [PowerMode.DRX, Validators.required], - edrxCycle: [0], + edrxCycle: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], + psmActivityTimer: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], + pagingTransmissionWindow: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], compositeOperationsSupport: [false] }) }); @@ -128,42 +136,22 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro ).subscribe((fwStrategy) => { if (fwStrategy === 2) { this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource').enable({emitEvent: false}); - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource') - .patchValue(DEFAULT_FW_UPDATE_RESOURCE, {emitEvent: false}); - this.isFwUpdateStrategy = true; } else { this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource').disable({emitEvent: false}); - this.isFwUpdateStrategy = false; + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource') + .reset(DEFAULT_FW_UPDATE_RESOURCE, {emitEvent: false}); } - this.otaUpdateFwStrategyValidate(true); }); this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateStrategy').valueChanges.pipe( takeUntil(this.destroy$) ).subscribe((swStrategy) => { if (swStrategy === 2) { this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource').enable({emitEvent: false}); - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource') - .patchValue(DEFAULT_SW_UPDATE_RESOURCE, {emitEvent: false}); - this.isSwUpdateStrategy = true; } else { - this.isSwUpdateStrategy = false; this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource').disable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource') + .reset(DEFAULT_SW_UPDATE_RESOURCE, {emitEvent: false}); } - this.otaUpdateSwStrategyValidate(true); - }); - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.powerMode').valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((powerMode: PowerMode) => { - if (powerMode === PowerMode.E_DRX) { - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.edrxCycle').enable({emitEvent: false}); - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.edrxCycle').patchValue(0, {emitEvent: false}); - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.edrxCycle') - .setValidators([Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]); - } else { - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.edrxCycle').disable({emitEvent: false}); - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.edrxCycle').clearValidators(); - } - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.edrxCycle').updateValueAndValidity({emitEvent: false}); }); this.lwm2mDeviceProfileFormGroup.valueChanges.pipe( takeUntil(this.destroy$) @@ -198,24 +186,33 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } else { this.lwm2mDeviceProfileFormGroup.enable({emitEvent: false}); this.lwm2mDeviceConfigFormGroup.enable({emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.powerMode').updateValueAndValidity({onlySelf: true}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateStrategy').updateValueAndValidity({onlySelf: true}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateStrategy').updateValueAndValidity({onlySelf: true}); } } async writeValue(value: Lwm2mProfileConfigModels | null) { - if (isDefinedAndNotNull(value)) { - if (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap) { - this.configurationValue = value; - } else { - this.configurationValue = await this.defaultProfileConfig(); - } + if (isDefinedAndNotNull(value) && (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap)) { + this.configurationValue = value; + const defaultFormSettings = !(value.observeAttr.attribute.length && value.observeAttr.telemetry.length); this.lwm2mDeviceConfigFormGroup.patchValue({ configurationJson: this.configurationValue - }, {emitEvent: false}); + }, {emitEvent: defaultFormSettings}); + if (defaultFormSettings) { + await this.defaultProfileConfig(); + } this.initWriteValue(); } } - private async defaultProfileConfig(): Promise { + validate(): ValidationErrors | null { + return this.lwm2mDeviceProfileFormGroup.valid ? null : { + lwm2mDeviceProfile: false + }; + } + + private async defaultProfileConfig(): Promise { let bootstrap: ServerSecurityConfig; let lwm2m: ServerSecurityConfig; try { @@ -227,15 +224,15 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrap = getDefaultBootstrapServerSecurityConfig(); lwm2m = getDefaultLwM2MServerSecurityConfig(); } - return { - observeAttr: getDefaultProfileObserveAttrConfig(), - bootstrap: { - servers: getDefaultBootstrapServersSecurityConfig(), - bootstrapServer: bootstrap, - lwm2mServer: lwm2m - }, - clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig() - }; + + this.configurationValue.bootstrap.bootstrapServer = bootstrap; + this.configurationValue.bootstrap.lwm2mServer = lwm2m; + this.lwm2mDeviceConfigFormGroup.patchValue({ + configurationJson: this.configurationValue + }, {emitEvent: false}); + this.lwm2mDeviceProfileFormGroup.patchValue({ + bootstrap: this.configurationValue.bootstrap + }, {emitEvent: false}); } private initWriteValue = (): void => { @@ -256,10 +253,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private updateWriteValue = (value: ObjectLwM2M[]): void => { - const fwResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateResource) ? - this.configurationValue.clientLwM2mSettings.fwUpdateResource : ''; - const swResource = isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.swUpdateResource) ? - this.configurationValue.clientLwM2mSettings.swUpdateResource : ''; this.lwm2mDeviceProfileFormGroup.patchValue({ objectIds: value, observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value), @@ -268,22 +261,19 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy || 1, swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy || 1, - fwUpdateResource: fwResource, - swUpdateResource: swResource, + fwUpdateResource: this.configurationValue.clientLwM2mSettings.fwUpdateResource || '', + swUpdateResource: this.configurationValue.clientLwM2mSettings.swUpdateResource || '', powerMode: this.configurationValue.clientLwM2mSettings.powerMode || PowerMode.DRX, edrxCycle: this.configurationValue.clientLwM2mSettings.edrxCycle || 0, compositeOperationsSupport: this.configurationValue.clientLwM2mSettings.compositeOperationsSupport || false } }, {emitEvent: false}); - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.powerMode') - .patchValue(this.configurationValue.clientLwM2mSettings.powerMode || PowerMode.DRX, {emitEvent: false, onlySelf: true}); - this.configurationValue.clientLwM2mSettings.fwUpdateResource = fwResource; - this.configurationValue.clientLwM2mSettings.swUpdateResource = swResource; - this.isFwUpdateStrategy = this.configurationValue.clientLwM2mSettings.fwUpdateStrategy === 2; - this.isSwUpdateStrategy = this.configurationValue.clientLwM2mSettings.swUpdateStrategy === 2; - this.otaUpdateSwStrategyValidate(); - this.otaUpdateFwStrategyValidate(); + if (!this.disabled) { + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.powerMode').updateValueAndValidity({onlySelf: true}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateStrategy').updateValueAndValidity({onlySelf: true}); + this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateStrategy').updateValueAndValidity({onlySelf: true}); + } } private updateModel = (): void => { @@ -576,22 +566,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro }); } - private otaUpdateFwStrategyValidate(updated = false): void { - if (this.isFwUpdateStrategy) { - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource').setValidators([Validators.required]); - } else { - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource').clearValidators(); - } - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource').updateValueAndValidity({emitEvent: updated}); - } - - private otaUpdateSwStrategyValidate(updated = false): void { - if (this.isSwUpdateStrategy) { - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource').setValidators([Validators.required]); - } else { - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource').clearValidators(); - } - this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource').updateValueAndValidity({emitEvent: updated}); + get clientSettingsFormGroup(): FormGroup { + return this.lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings') as FormGroup; } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.html index 29822c3e1c..a1231e94ae 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.html @@ -17,7 +17,7 @@ -->
- {{data.objectName}} (object <{{data.objectId}}>) + {{data.objectName}} #{{data.objectId}} - - {{ 'device-profile.lwm2m.valid-id-instance' | translate: { - count: 2, instance: instanceId, min: instanceIdValueMin - } }} + + {{ 'device-profile.lwm2m.instances-list-required' | translate }} - - {{ 'device-profile.lwm2m.valid-id-instance' | translate: { - count: 1, instance: instanceId, max: instanceIdValueMax - } }} + + {{ 'device-profile.lwm2m.instance-id-pattern' | translate }} - - {{ 'device-profile.lwm2m.valid-id-instance' | translate: { - count: 0, instance: instanceId, max: instanceIdValueMax - } }} + + {{ 'device-profile.lwm2m.instance-id-max' | translate: {max: instanceIdValueMax} }} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts index 29f5bb33cf..7fbb1ba1f4 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts @@ -14,37 +14,76 @@ /// limitations under the License. /// -import { Component, forwardRef } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; -import { INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER } from './lwm2m-profile-config.models'; +import { Component, ElementRef, forwardRef, Input, ViewChild } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, ValidationErrors, Validator, + Validators +} from '@angular/forms'; +import { INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN } from './lwm2m-profile-config.models'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; +import { MatChipInputEvent } from '@angular/material/chips'; @Component({ selector: 'tb-profile-lwm2m-object-add-instances-list', templateUrl: './lwm2m-object-add-instances-list.component.html', - providers: [{ - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => Lwm2mObjectAddInstancesListComponent), - multi: true - }] + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => Lwm2mObjectAddInstancesListComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => Lwm2mObjectAddInstancesListComponent), + multi: true + }] }) -export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccessor { +export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccessor, Validator { - private disabled = false; - private dirty = false; + private requiredValue: boolean; + + @Input() + disabled: boolean; + + get required(): boolean { + return this.requiredValue; + } + + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + this.updateValidators(); + } + + @ViewChild('instanceId') instanceId: ElementRef; instancesListFormGroup: FormGroup; instancesId = new Set(); - instanceIdValueMin = INSTANCES_ID_VALUE_MIN; + separatorKeysCodes = [ENTER, COMMA, SEMICOLON]; instanceIdValueMax = INSTANCES_ID_VALUE_MAX; private propagateChange = (v: any) => { }; constructor(private fb: FormBuilder) { this.instancesListFormGroup = this.fb.group({ - instanceIdInput: [null, [ - Validators.min(this.instanceIdValueMin), - Validators.max(this.instanceIdValueMax), - Validators.pattern(KEY_REGEXP_NUMBER)]] + instanceList: [null], + instanceId: [null, [ + Validators.min(INSTANCES_ID_VALUE_MIN), + Validators.max(INSTANCES_ID_VALUE_MAX), + Validators.pattern('[0-9]*')]] + }); + this.instancesListFormGroup.get('instanceId').statusChanges.subscribe((value) => { + if (value === 'INVALID') { + const errors = this.instancesListFormGroup.get('instanceId').errors; + this.instancesListFormGroup.get('instanceList').setErrors(errors); + } else { + this.instancesListFormGroup.get('instanceList').updateValueAndValidity({onlySelf: true}); + } }); } @@ -67,26 +106,41 @@ export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccesso writeValue(value: Set): void { if (value && value.size) { this.instancesId = value; + this.instancesListFormGroup.patchValue({instanceList: Array.from(this.instancesId)}, {emitEvent: false}); } - this.dirty = false; } - add = (): void => { - if (this.instancesListFormGroup.get('instanceIdInput').valid && Number.isFinite(Number(this.instanceId))) { - this.instancesId.add(Number(this.instanceId)); - this.instancesListFormGroup.get('instanceIdInput').setValue(null); + validate(): ValidationErrors | null { + return this.instancesListFormGroup.valid ? null : { + instancesListForm: false + }; + } + + add = (event: MatChipInputEvent): void => { + const value = event.value; + if (this.instancesListFormGroup.get('instanceId').valid && value !== '' && Number.isFinite(Number(value))) { + this.instancesId.add(Number(value)); + this.instancesListFormGroup.patchValue({instanceList: Array.from(this.instancesId)}, {emitEvent: false}); + this.instancesListFormGroup.get('instanceId').setValue(null, {emitEvent: false}); this.propagateChange(this.instancesId); - this.dirty = true; } } remove = (object: number): void => { this.instancesId.delete(object); + this.instancesListFormGroup.patchValue({instanceList: Array.from(this.instancesId)}, {emitEvent: false}); this.propagateChange(this.instancesId); - this.dirty = true; } - get instanceId(): number { - return this.instancesListFormGroup.get('instanceIdInput').value; + onFocus() { + setTimeout(() => { + this.instanceId.nativeElement.blur(); + this.instanceId.nativeElement.focus(); + }, 0); + } + + private updateValidators() { + this.instancesListFormGroup.get('instanceList').setValidators(this.required ? [Validators.required] : []); + this.instancesListFormGroup.get('instanceList').updateValueAndValidity({emitEvent: false}); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html index c78f30f1ed..6ca38b5fb5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html @@ -16,7 +16,8 @@ --> - + device-profile.lwm2m.object-list + close - { - this.objectInput.nativeElement.blur(); - this.objectInput.nativeElement.focus(); - }, 0); + this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value, {emitEvent}); + if (emitEvent) { + setTimeout(() => { + this.objectInput.nativeElement.blur(); + this.objectInput.nativeElement.focus(); + }, 0); + } } } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts index 6be30b3fc8..aa801f84dc 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts @@ -28,6 +28,7 @@ import { Lwm2mObjectAddInstancesListComponent } from './lwm2m-object-add-instanc import { CommonModule } from '@angular/common'; import { SharedModule } from '@app/shared/shared.module'; import { Lwm2mObserveAttrTelemetryInstancesComponent } from './lwm2m-observe-attr-telemetry-instances.component'; +import { DeviceProfileCommonModule } from '@home/components/profile/device/common/device-profile-common.module'; @NgModule({ declarations: @@ -46,7 +47,8 @@ import { Lwm2mObserveAttrTelemetryInstancesComponent } from './lwm2m-observe-att ], imports: [ CommonModule, - SharedModule + SharedModule, + DeviceProfileCommonModule ], exports: [ Lwm2mDeviceProfileTransportConfigurationComponent, diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index 1a14cb354d..ab4fea8b40 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -18,7 +18,6 @@ import { ValidatorFn, Validators } from '@angular/forms'; export const PAGE_SIZE_LIMIT = 50; export const INSTANCES = 'instances'; -export const INSTANCE = 'instance'; export const RESOURCES = 'resources'; export const OBSERVE = 'observe'; export const ATTRIBUTE = 'attribute'; @@ -38,7 +37,6 @@ export const DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT = 0; export const LEN_MAX_PUBLIC_KEY_RPK = 182; export const LEN_MAX_PUBLIC_KEY_X509 = 3000; export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/; -export const KEY_REGEXP_NUMBER = /^(-?|\+?)\d*$/; export const INSTANCES_ID_VALUE_MIN = 0; export const INSTANCES_ID_VALUE_MAX = 65535; export const DEFAULT_OTA_UPDATE_PROTOCOL = 'coap://'; @@ -170,6 +168,8 @@ export interface ClientLwM2mSettings { swUpdateResource: string; powerMode: PowerMode; edrxCycle?: number; + pagingTransmissionWindow?: number; + psmActivityTimer?: number; compositeOperationsSupport: boolean; } diff --git a/ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.html b/ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.html index 5a59caf862..fe6024d9f5 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.html @@ -16,24 +16,6 @@ --> - - device-profile.power-saving-mode - - {{ "device-profile.power-saving-mode-type.default" | translate }} - - {{ powerModeTranslationMap.get(powerMod) | translate }} - - - - - {{ 'device-profile.edrx-cycle' | translate }} - - - {{ 'device-profile.edrx-cycle-required' | translate }} - - - {{ 'device-profile.edrx-cycle-pattern' | translate }} - - + + diff --git a/ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.ts index 1d98f5580b..8046dde486 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.ts @@ -19,8 +19,11 @@ import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Valida import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { DeviceTransportConfiguration, Lwm2mDeviceTransportConfiguration } from '@shared/models/device.models'; -import { PowerMode, PowerModeTranslationMap } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; +import { + DeviceTransportConfiguration, + DeviceTransportType, + Lwm2mDeviceTransportConfiguration +} from '@shared/models/device.models'; import { takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; import { isDefinedAndNotNull } from '@core/utils'; @@ -38,8 +41,6 @@ import { isDefinedAndNotNull } from '@core/utils'; export class Lwm2mDeviceTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy { lwm2mDeviceTransportConfigurationFormGroup: FormGroup; - powerMods = Object.values(PowerMode); - powerModeTranslationMap = PowerModeTranslationMap; private requiredValue: boolean; get required(): boolean { @@ -70,21 +71,9 @@ export class Lwm2mDeviceTransportConfigurationComponent implements ControlValueA ngOnInit() { this.lwm2mDeviceTransportConfigurationFormGroup = this.fb.group({ powerMode: [null], - edrxCycle: [0] - }); - this.lwm2mDeviceTransportConfigurationFormGroup.get('powerMode').valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((powerMode: PowerMode) => { - if (powerMode === PowerMode.E_DRX) { - this.lwm2mDeviceTransportConfigurationFormGroup.get('edrxCycle').enable({emitEvent: false}); - this.lwm2mDeviceTransportConfigurationFormGroup.get('edrxCycle').patchValue(0, {emitEvent: false}); - this.lwm2mDeviceTransportConfigurationFormGroup.get('edrxCycle') - .setValidators([Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]); - } else { - this.lwm2mDeviceTransportConfigurationFormGroup.get('edrxCycle').disable({emitEvent: false}); - this.lwm2mDeviceTransportConfigurationFormGroup.get('edrxCycle').clearValidators(); - } - this.lwm2mDeviceTransportConfigurationFormGroup.get('edrxCycle').updateValueAndValidity({emitEvent: false}); + edrxCycle: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], + psmActivityTimer: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]], + pagingTransmissionWindow: [{disabled: true, value: 0}, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]] }); this.lwm2mDeviceTransportConfigurationFormGroup.valueChanges.pipe( takeUntil(this.destroy$) @@ -104,15 +93,18 @@ export class Lwm2mDeviceTransportConfigurationComponent implements ControlValueA this.lwm2mDeviceTransportConfigurationFormGroup.disable({emitEvent: false}); } else { this.lwm2mDeviceTransportConfigurationFormGroup.enable({emitEvent: false}); + this.lwm2mDeviceTransportConfigurationFormGroup.get('powerMode').updateValueAndValidity({onlySelf: true}); } } writeValue(value: Lwm2mDeviceTransportConfiguration | null): void { if (isDefinedAndNotNull(value)) { - this.lwm2mDeviceTransportConfigurationFormGroup.get('powerMode').patchValue(value.powerMode, {emitEvent: false, onlySelf: true}); - this.lwm2mDeviceTransportConfigurationFormGroup.get('edrxCycle').patchValue(value.edrxCycle || 0, {emitEvent: false}); + this.lwm2mDeviceTransportConfigurationFormGroup.patchValue(value, {emitEvent: false}); } else { - this.lwm2mDeviceTransportConfigurationFormGroup.patchValue({powerMode: null, edrxCycle: 0}, {emitEvent: false}); + this.lwm2mDeviceTransportConfigurationFormGroup.get('powerMode').patchValue(null, {emitEvent: false}); + } + if (!this.disabled) { + this.lwm2mDeviceTransportConfigurationFormGroup.get('powerMode').updateValueAndValidity({onlySelf: true}); } } @@ -120,7 +112,7 @@ export class Lwm2mDeviceTransportConfigurationComponent implements ControlValueA let configuration: DeviceTransportConfiguration = null; if (this.lwm2mDeviceTransportConfigurationFormGroup.valid) { configuration = this.lwm2mDeviceTransportConfigurationFormGroup.value; - // configuration.type = DeviceTransportType.LWM2M; + configuration.type = DeviceTransportType.LWM2M; } this.propagateChange(configuration); } diff --git a/ui-ngx/src/app/modules/home/pages/device/device.module.ts b/ui-ngx/src/app/modules/home/pages/device/device.module.ts index b9afd98d95..d9452e570a 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.module.ts +++ b/ui-ngx/src/app/modules/home/pages/device/device.module.ts @@ -34,6 +34,7 @@ import { CoapDeviceTransportConfigurationComponent } from './data/coap-device-tr import { Lwm2mDeviceTransportConfigurationComponent } from './data/lwm2m-device-transport-configuration.component'; import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-transport-configuration.component'; import { DeviceCredentialsModule } from '@home/components/device/device-credentials.module'; +import { DeviceProfileCommonModule } from '@home/components/profile/device/common/device-profile-common.module'; @NgModule({ declarations: [ @@ -57,6 +58,7 @@ import { DeviceCredentialsModule } from '@home/components/device/device-credenti HomeComponentsModule, HomeDialogsModule, DeviceCredentialsModule, + DeviceProfileCommonModule, DeviceRoutingModule ] }) diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index fb1426b15b..bbf5bde5ca 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -30,7 +30,14 @@ import { AbstractControl, ValidationErrors } from '@angular/forms'; import { OtaPackageId } from '@shared/models/id/ota-package-id'; import { DashboardId } from '@shared/models/id/dashboard-id'; import { DataType } from '@shared/models/constants'; -import { PowerMode } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; +import { + getDefaultBootstrapServerSecurityConfig, + getDefaultBootstrapServersSecurityConfig, + getDefaultLwM2MServerSecurityConfig, + getDefaultProfileClientLwM2mSettingsConfig, + getDefaultProfileObserveAttrConfig, + PowerMode +} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; export enum DeviceProfileType { DEFAULT = 'DEFAULT', @@ -369,7 +376,15 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT transportConfiguration = {...coapTransportConfiguration, type: DeviceTransportType.COAP}; break; case DeviceTransportType.LWM2M: - const lwm2mTransportConfiguration: Lwm2mDeviceProfileTransportConfiguration = {}; + const lwm2mTransportConfiguration: Lwm2mDeviceProfileTransportConfiguration = { + observeAttr: getDefaultProfileObserveAttrConfig(), + bootstrap: { + servers: getDefaultBootstrapServersSecurityConfig(), + bootstrapServer: getDefaultBootstrapServerSecurityConfig(), + lwm2mServer: getDefaultLwM2MServerSecurityConfig() + }, + clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig() + }; transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M}; break; case DeviceTransportType.SNMP: @@ -404,7 +419,9 @@ export function createDeviceTransportConfiguration(type: DeviceTransportType): D transportConfiguration = {...coapTransportConfiguration, type: DeviceTransportType.COAP}; break; case DeviceTransportType.LWM2M: - const lwm2mTransportConfiguration: Lwm2mDeviceTransportConfiguration = {}; + const lwm2mTransportConfiguration: Lwm2mDeviceTransportConfiguration = { + powerMode: null + }; transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M}; break; case DeviceTransportType.SNMP: @@ -592,7 +609,8 @@ export interface CoapDeviceTransportConfiguration { export interface Lwm2mDeviceTransportConfiguration { powerMode?: PowerMode | null; edrxCycle?: number; - [key: string]: any; + pagingTransmissionWindow?: number; + psmActivityTimer?: number; } export enum SnmpDeviceProtocolVersion { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index e1b8a95a8b..1c5bc2ed8e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1242,14 +1242,12 @@ "object-list": "Object list", "object-list-empty": "No objects selected.", "no-objects-matching": "No objects matching '{{object}}' were found.", - "valid-id-instance-no-min": "Instance number '{{instance}}' no validated. Min value='{{min}}'", - "valid-id-instance-no-max": "Instance number '{{instance}}' no validated. Max value='{{max}}'", - "valid-id-instance": "Instance number '{{instance}}' no validated. { count, plural, 1 {Max value='{{max}}'} 2 {Min value='{{min}}'} other {Must be only number} }", "model-tab": "LWM2M Model", "add-new-instances": "Add new instances", "instances-list": "Instances list", - "instances-input": "Input Instance Id value", - "instances-input-holder": "Input Instance number...", + "instances-list-required": "Instances list is required.", + "instance-id-pattern": "Instance id must be a positive integer.", + "instance-id-max": "Maximum instance id value {{max}}.", "instance": "Instance", "resource-label": "#ID Resource name", "observe-label": "Observe",