Browse Source

UI: Add power mode settings in LwM2M device profile and refactoring lwm2m device profile transport form

pull/4958/head
Vladyslav_Prykhodko 5 years ago
committed by Andrew Shvayka
parent
commit
6c631a9f14
  1. 11
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  2. 34
      ui-ngx/src/app/modules/home/components/profile/device/common/device-profile-common.module.ts
  3. 25
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html
  4. 150
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts
  5. 3
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.html
  6. 44
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.html
  7. 104
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts
  8. 5
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html
  9. 16
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts
  10. 4
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-components.module.ts
  11. 4
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts
  12. 22
      ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.html
  13. 38
      ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.ts
  14. 2
      ui-ngx/src/app/modules/home/pages/device/device.module.ts
  15. 26
      ui-ngx/src/app/shared/models/device.models.ts
  16. 8
      ui-ngx/src/assets/locale/locale.constant-en_US.json

11
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,

34
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 { }

25
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html

@ -132,7 +132,7 @@
<mat-option [value]=3>{{ 'device-profile.lwm2m.fw-update-strategy-data' | translate }}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="mat-block" fxFlex *ngIf="isFwUpdateStrategy">
<mat-form-field class="mat-block" fxFlex *ngIf="lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateStrategy').value === 2">
<mat-label>{{ 'device-profile.lwm2m.fw-update-resource' | translate }}</mat-label>
<input matInput formControlName="fwUpdateResource" required>
<mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.fwUpdateResource').hasError('required')">
@ -149,7 +149,7 @@
<mat-option [value]=2>{{ 'device-profile.lwm2m.sw-update-strategy-package-uri' | translate }}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="mat-block" fxFlex *ngIf="isSwUpdateStrategy">
<mat-form-field class="mat-block" fxFlex *ngIf="lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateStrategy').value === 2">
<mat-label>{{ 'device-profile.lwm2m.sw-update-resource' | translate }}</mat-label>
<input matInput formControlName="swUpdateResource" required>
<mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.swUpdateResource').hasError('required')">
@ -159,25 +159,8 @@
</fieldset>
<fieldset class="fields-group">
<legend class="group-title" translate>device-profile.power-saving-mode</legend>
<mat-form-field class="mat-block" fxFlex>
<mat-label> </mat-label>
<mat-select formControlName="powerMode">
<mat-option *ngFor="let powerMod of powerMods" [value]="powerMod">
{{ powerModeTranslationMap.get(powerMod) | translate}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="mat-block" fxFlex *ngIf="lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.powerMode').value === 'E_DRX'">
<mat-label>{{ 'device-profile.edrx-cycle' | translate }}</mat-label>
<input matInput type="number" min="0" formControlName="edrxCycle" required>
<mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.edrxCycle').hasError('required')">
{{ 'device-profile.edrx-cycle-required' | translate }}
</mat-error>
<mat-error *ngIf="lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.edrxCycle').hasError('pattern') ||
lwm2mDeviceProfileFormGroup.get('clientLwM2mSettings.edrxCycle').hasError('min')">
{{ 'device-profile.edrx-cycle-pattern' | translate }}
</mat-error>
</mat-form-field>
<tb-power-mode-settings [parentForm]="clientSettingsFormGroup">
</tb-power-mode-settings>
</fieldset>
<mat-slide-toggle class="mat-slider"
formControlName="compositeOperationsSupport">{{ 'device-profile.lwm2m.composite-operations-support' | translate }}</mat-slide-toggle>

150
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<Lwm2mProfileConfigModels> {
validate(): ValidationErrors | null {
return this.lwm2mDeviceProfileFormGroup.valid ? null : {
lwm2mDeviceProfile: false
};
}
private async defaultProfileConfig(): Promise<void> {
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;
}
}

3
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-dialog.component.html

@ -17,7 +17,7 @@
-->
<form [formGroup]="instancesFormGroup" (ngSubmit)="add()" style="min-width: 400px;">
<mat-toolbar fxLayout="row" color="primary">
<b><i>{{data.objectName}}</i></b> (object <<b>{{data.objectId}}</b>>)
<span style="font-weight: 500">{{data.objectName}}</span>&nbsp;#{{data.objectId}}
<span fxFlex></span>
<button mat-button mat-icon-button
(click)="cancel()"
@ -31,6 +31,7 @@
<div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async">
<tb-profile-lwm2m-object-add-instances-list
required
formControlName="instancesIds">
</tb-profile-lwm2m-object-add-instances-list>
</fieldset>

44
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.html

@ -18,40 +18,32 @@
<section [formGroup]="instancesListFormGroup" class="lwm2m-instances-list">
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.instances-list' | translate }}</mat-label>
<mat-chip-list>
<mat-chip-list #chipList formControlName="instanceList" [required]="required" (focus)="onFocus()">
<mat-chip
*ngFor="let instanceId of instancesId"
[selectable]="true"
[removable]="true"
[selectable]="!disabled"
[removable]="!disabled"
(removed)="remove(instanceId)">
{{instanceId}}
<mat-icon matChipRemove>close</mat-icon>
<mat-icon matChipRemove *ngIf="!disabled">close</mat-icon>
</mat-chip>
<input matInput type="text"
#instanceId
style="max-width: 70px;"
formControlName="instanceId"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
matChipInputAddOnBlur
(matChipInputTokenEnd)="add($event)">
</mat-chip-list>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.instances-input' | translate }}</mat-label>
<input fxFlexFill matInput type='text'
placeholder="{{'device-profile.lwm2m.instances-input-holder' | translate}}"
formControlName="instanceIdInput">
<button mat-icon-button matSuffix
(click)="add()" type="button">
<mat-icon>add</mat-icon>
</button>
<mat-error *ngIf="instancesListFormGroup.get('instanceIdInput').hasError('min')">
{{ 'device-profile.lwm2m.valid-id-instance' | translate: {
count: 2, instance: instanceId, min: instanceIdValueMin
} }}
<mat-error *ngIf="instancesListFormGroup.get('instanceList').hasError('required')">
{{ 'device-profile.lwm2m.instances-list-required' | translate }}
</mat-error>
<mat-error *ngIf="instancesListFormGroup.get('instanceIdInput').hasError('max')">
{{ 'device-profile.lwm2m.valid-id-instance' | translate: {
count: 1, instance: instanceId, max: instanceIdValueMax
} }}
<mat-error *ngIf="instancesListFormGroup.get('instanceId').hasError('pattern')">
{{ 'device-profile.lwm2m.instance-id-pattern' | translate }}
</mat-error>
<mat-error *ngIf="instancesListFormGroup.get('instanceIdInput').hasError('pattern')">
{{ 'device-profile.lwm2m.valid-id-instance' | translate: {
count: 0, instance: instanceId, max: instanceIdValueMax
} }}
<mat-error *ngIf="instancesListFormGroup.get('instanceId').hasError('max')">
{{ 'device-profile.lwm2m.instance-id-max' | translate: {max: instanceIdValueMax} }}
</mat-error>
</mat-form-field>
</section>

104
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<HTMLInputElement>;
instancesListFormGroup: FormGroup;
instancesId = new Set<number>();
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<number>): 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});
}
}

5
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html

@ -16,7 +16,8 @@
-->
<mat-form-field appearance="standard" [formGroup]="lwm2mListFormGroup" class="mat-block">
<mat-chip-list #chipList formControlName="objectsList">
<mat-label translate>device-profile.lwm2m.object-list</mat-label>
<mat-chip-list #chipList formControlName="objectsList" [required]="required">
<mat-chip
*ngFor="let objectLwm2m of objectsList"
[selectable]="!disabled"
@ -25,7 +26,7 @@
{{objectLwm2m.name}} #{{objectLwm2m.keyId}}
<mat-icon matChipRemove *ngIf="!disabled">close</mat-icon>
</mat-chip>
<input matInput type="text" placeholder="{{ !disabled ? ('device-profile.lwm2m.object-list' | translate) : '' }}"
<input matInput type="text"
style="max-width: 200px;"
#objectInput
(focusin)="onFocus()"

16
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts

@ -131,7 +131,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
if (isDisabled) {
this.lwm2mListFormGroup.disable({emitEvent: false});
if (isDefined(this.objectInput)) {
this.clear();
this.clear('', false);
}
} else {
this.lwm2mListFormGroup.enable({emitEvent: false});
@ -196,12 +196,14 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
}
}
private clear(value: string = '') {
private clear(value = '', emitEvent = true) {
this.objectInput.nativeElement.value = value;
this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value);
setTimeout(() => {
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);
}
}
}

4
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,

4
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;
}

22
ui-ngx/src/app/modules/home/pages/device/data/lwm2m-device-transport-configuration.component.html

@ -16,24 +16,6 @@
-->
<form [formGroup]="lwm2mDeviceTransportConfigurationFormGroup" style="padding-bottom: 16px;">
<mat-form-field class="mat-block" fxFlex>
<mat-label translate>device-profile.power-saving-mode</mat-label>
<mat-select formControlName="powerMode">
<mat-option [value]="null">{{ "device-profile.power-saving-mode-type.default" | translate }}</mat-option>
<mat-option *ngFor="let powerMod of powerMods" [value]="powerMod">
{{ powerModeTranslationMap.get(powerMod) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="mat-block" fxFlex *ngIf="lwm2mDeviceTransportConfigurationFormGroup.get('powerMode').value === 'E_DRX'">
<mat-label>{{ 'device-profile.edrx-cycle' | translate }}</mat-label>
<input matInput type="number" min="0" formControlName="edrxCycle" required>
<mat-error *ngIf="lwm2mDeviceTransportConfigurationFormGroup.get('edrxCycle').hasError('required')">
{{ 'device-profile.edrx-cycle-required' | translate }}
</mat-error>
<mat-error *ngIf="lwm2mDeviceTransportConfigurationFormGroup.get('edrxCycle').hasError('pattern') ||
lwm2mDeviceTransportConfigurationFormGroup.get('edrxCycle').hasError('min')">
{{ 'device-profile.edrx-cycle-pattern' | translate }}
</mat-error>
</mat-form-field>
<tb-power-mode-settings [parentForm]="lwm2mDeviceTransportConfigurationFormGroup" [isDeviceSetting]="true">
</tb-power-mode-settings>
</form>

38
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);
}

2
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
]
})

26
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 {

8
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",

Loading…
Cancel
Save