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 3958830088..d0ad42b62e 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
@@ -99,7 +99,6 @@ import { DeviceProfileDialogComponent } from '@home/components/profile/device-pr
import { DeviceProfileAutocompleteComponent } from '@home/components/profile/device-profile-autocomplete.component';
import { MqttDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/mqtt-device-profile-transport-configuration.component';
import { CoapDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/coap-device-profile-transport-configuration.component';
-import { SnmpDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/snmp-device-profile-transport-configuration.component';
import { DeviceProfileAlarmsComponent } from '@home/components/profile/alarm/device-profile-alarms.component';
import { DeviceProfileAlarmComponent } from '@home/components/profile/alarm/device-profile-alarm.component';
import { CreateAlarmRulesComponent } from '@home/components/profile/alarm/create-alarm-rules.component';
@@ -143,6 +142,7 @@ import { SecurityConfigLwm2mComponent } from '@home/components/device/security-c
import { SecurityConfigLwm2mServerComponent } from '@home/components/device/security-config-lwm2m-server.component';
import { DashboardImageDialogComponent } from '@home/components/dashboard-page/dashboard-image-dialog.component';
import { WidgetContainerComponent } from '@home/components/widget/widget-container.component';
+import { SnmpDeviceProfileTransportModule } from '@home/components/profile/device/snpm/snmp-device-profile-transport.module';
@NgModule({
declarations:
@@ -228,7 +228,6 @@ import { WidgetContainerComponent } from '@home/components/widget/widget-contain
DefaultDeviceProfileTransportConfigurationComponent,
MqttDeviceProfileTransportConfigurationComponent,
CoapDeviceProfileTransportConfigurationComponent,
- SnmpDeviceProfileTransportConfigurationComponent,
DeviceProfileTransportConfigurationComponent,
CreateAlarmRulesComponent,
AlarmRuleComponent,
@@ -272,6 +271,7 @@ import { WidgetContainerComponent } from '@home/components/widget/widget-contain
SharedModule,
SharedHomeComponentsModule,
Lwm2mProfileComponentsModule,
+ SnmpDeviceProfileTransportModule,
StatesControllerModule
],
exports: [
@@ -339,7 +339,6 @@ import { WidgetContainerComponent } from '@home/components/widget/widget-contain
DefaultDeviceProfileTransportConfigurationComponent,
MqttDeviceProfileTransportConfigurationComponent,
CoapDeviceProfileTransportConfigurationComponent,
- SnmpDeviceProfileTransportConfigurationComponent,
DeviceProfileTransportConfigurationComponent,
CreateAlarmRulesComponent,
AlarmRuleComponent,
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts
index 305a0a84b6..50b3bc801c 100644
--- a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts
+++ b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.ts
@@ -89,7 +89,9 @@ export class DeviceProfileTransportConfigurationComponent implements ControlValu
if (configuration) {
delete configuration.type;
}
- this.deviceProfileTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false});
+ setTimeout(() => {
+ this.deviceProfileTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false});
+ });
}
private updateModel() {
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.html
deleted file mode 100644
index f57b1ac716..0000000000
--- a/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.html b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.html
new file mode 100644
index 0000000000..7d2f8a154c
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+ device-profile.snmp.scope
+
+
+ {{ snmpSpecTypeTranslationMap.get(snmpSpecType) }}
+
+
+
+ {{ 'device-profile.snmp.scope-required' | translate }}
+
+
+
+
+
+
+
+
+ device-profile.snmp.please-add-communication-config
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.scss
new file mode 100644
index 0000000000..3b6661b862
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.scss
@@ -0,0 +1,44 @@
+/**
+ * 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.
+ */
+:host {
+ .communication-config {
+ border: 2px groove rgba(0, 0, 0, 0.25);
+ border-radius: 4px;
+ padding: 8px;
+ min-width: 0;
+ }
+
+ .scope-row {
+ padding-bottom: 8px;
+ }
+
+ .required-text {
+ margin: 16px 0
+ }
+}
+
+:host ::ng-deep {
+ .mat-form-field.spec {
+ .mat-form-field-infix {
+ width: 160px;
+ }
+ }
+ .button-icon{
+ font-size: 20px;
+ width: 20px;
+ height: 20px;
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.ts
new file mode 100644
index 0000000000..d683d46880
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-communication-config.component.ts
@@ -0,0 +1,218 @@
+///
+/// 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 { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
+import {
+ AbstractControl,
+ ControlValueAccessor,
+ FormArray,
+ FormBuilder,
+ FormGroup,
+ NG_VALIDATORS,
+ NG_VALUE_ACCESSOR,
+ Validator,
+ Validators
+} from '@angular/forms';
+import { SnmpCommunicationConfig, SnmpSpecType, SnmpSpecTypeTranslationMap } from '@shared/models/device.models';
+import { Subject, Subscription } from 'rxjs';
+import { isUndefinedOrNull } from '@core/utils';
+import { takeUntil } from 'rxjs/operators';
+
+@Component({
+ selector: 'tb-snmp-device-profile-communication-config',
+ templateUrl: './snmp-device-profile-communication-config.component.html',
+ styleUrls: ['./snmp-device-profile-communication-config.component.scss'],
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => SnmpDeviceProfileCommunicationConfigComponent),
+ multi: true
+ },
+ {
+ provide: NG_VALIDATORS,
+ useExisting: forwardRef(() => SnmpDeviceProfileCommunicationConfigComponent),
+ multi: true
+ }]
+})
+export class SnmpDeviceProfileCommunicationConfigComponent implements OnInit, OnDestroy, ControlValueAccessor, Validator {
+
+ snmpSpecTypes = Object.values(SnmpSpecType);
+ snmpSpecTypeTranslationMap = SnmpSpecTypeTranslationMap;
+
+ deviceProfileCommunicationConfig: FormGroup;
+
+ @Input()
+ disabled: boolean;
+
+ private usedSpecType: SnmpSpecType[] = [];
+ private valueChange$: Subscription = null;
+ private destroy$ = new Subject();
+ private propagateChange = (v: any) => { };
+
+ constructor(private fb: FormBuilder) { }
+
+ ngOnInit(): void {
+ this.deviceProfileCommunicationConfig = this.fb.group({
+ communicationConfig: this.fb.array([])
+ });
+ }
+
+ ngOnDestroy() {
+ if (this.valueChange$) {
+ this.valueChange$.unsubscribe();
+ }
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+
+ communicationConfigFormArray(): FormArray {
+ return this.deviceProfileCommunicationConfig.get('communicationConfig') as FormArray;
+ }
+
+ registerOnChange(fn: any): void {
+ this.propagateChange = fn;
+ }
+
+ registerOnTouched(fn: any): void {
+ }
+
+ setDisabledState(isDisabled: boolean) {
+ this.disabled = isDisabled;
+ if (this.disabled) {
+ this.deviceProfileCommunicationConfig.disable({emitEvent: false});
+ } else {
+ this.deviceProfileCommunicationConfig.enable({emitEvent: false});
+ }
+ }
+
+ writeValue(communicationConfig: SnmpCommunicationConfig[]) {
+ if (this.valueChange$) {
+ this.valueChange$.unsubscribe();
+ }
+ const communicationConfigControl: Array = [];
+ if (communicationConfig) {
+ communicationConfig.forEach((config) => {
+ communicationConfigControl.push(this.createdFormGroup(config));
+ });
+ }
+ this.deviceProfileCommunicationConfig.setControl('communicationConfig', this.fb.array(communicationConfigControl));
+ if (!communicationConfig || !communicationConfig.length) {
+ this.addCommunicationConfig();
+ }
+ if (this.disabled) {
+ this.deviceProfileCommunicationConfig.disable({emitEvent: false});
+ } else {
+ this.deviceProfileCommunicationConfig.enable({emitEvent: false});
+ }
+ this.valueChange$ = this.deviceProfileCommunicationConfig.valueChanges.subscribe(() => {
+ this.updateModel();
+ });
+ this.updateUsedSpecType();
+ if (!this.disabled && !this.deviceProfileCommunicationConfig.valid) {
+ this.updateModel();
+ }
+ }
+
+ public validate() {
+ return this.deviceProfileCommunicationConfig.valid && this.deviceProfileCommunicationConfig.value.communicationConfig.length ? null : {
+ communicationConfig: false
+ };
+ }
+
+ public removeCommunicationConfig(index: number) {
+ this.communicationConfigFormArray().removeAt(index);
+ }
+
+
+ get isAddEnabled(): boolean {
+ return this.communicationConfigFormArray().length !== Object.keys(SnmpSpecType).length;
+ }
+
+ public addCommunicationConfig() {
+ this.communicationConfigFormArray().push(this.createdFormGroup());
+ this.deviceProfileCommunicationConfig.updateValueAndValidity();
+ if (!this.deviceProfileCommunicationConfig.valid) {
+ this.updateModel();
+ }
+ }
+
+ private getFirstUnusedSeverity(): SnmpSpecType {
+ for (const type of Object.values(SnmpSpecType)) {
+ if (this.usedSpecType.indexOf(type) === -1) {
+ return type;
+ }
+ }
+ return null;
+ }
+
+ public isDisabledSeverity(type: SnmpSpecType, index: number): boolean {
+ const usedIndex = this.usedSpecType.indexOf(type);
+ return usedIndex > -1 && usedIndex !== index;
+ }
+
+ public isShowFrequency(type: SnmpSpecType): boolean {
+ return type === SnmpSpecType.TELEMETRY_QUERYING || type === SnmpSpecType.CLIENT_ATTRIBUTES_QUERYING;
+ }
+
+ private updateUsedSpecType() {
+ this.usedSpecType = [];
+ const value: SnmpCommunicationConfig[] = this.deviceProfileCommunicationConfig.get('communicationConfig').value;
+ value.forEach((rule, index) => {
+ this.usedSpecType[index] = rule.spec;
+ });
+ }
+
+ private createdFormGroup(value?: SnmpCommunicationConfig): FormGroup {
+ if (isUndefinedOrNull(value)) {
+ value = {
+ spec: this.getFirstUnusedSeverity(),
+ queryingFrequencyMs: 0,
+ mappings: null
+ };
+ }
+ const form = this.fb.group({
+ spec: [value.spec, Validators.required],
+ mappings: [value.mappings]
+ });
+ if (this.isShowFrequency(value.spec)) {
+ form.addControl('queryingFrequencyMs',
+ this.fb.control(value.queryingFrequencyMs, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]));
+ }
+ form.get('spec').valueChanges.pipe(
+ takeUntil(this.destroy$)
+ ).subscribe(spec => {
+ if (this.isShowFrequency(spec)) {
+ form.addControl('queryingFrequencyMs',
+ this.fb.control(0, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]));
+ } else {
+ form.removeControl('queryingFrequencyMs');
+ }
+ });
+ return form;
+ }
+
+ private updateModel() {
+ const value: SnmpCommunicationConfig[] = this.deviceProfileCommunicationConfig.get('communicationConfig').value;
+ value.forEach(config => {
+ if (!this.isShowFrequency(config.spec)) {
+ delete config.queryingFrequencyMs;
+ }
+ });
+ this.updateUsedSpecType();
+ this.propagateChange(value);
+ }
+
+}
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.html b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.html
new file mode 100644
index 0000000000..a2a0a97d15
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.html
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ device-profile.snmp.please-add-mapping-config
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.scss
new file mode 100644
index 0000000000..94cf2290ac
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.scss
@@ -0,0 +1,46 @@
+/**
+ * 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.
+ */
+:host {
+ .mapping-config {
+ min-width: 518px;
+ }
+ .mapping-list {
+ padding-bottom: 8px;
+ height: 46px;
+ }
+
+ .required-text {
+ margin: 14px 0;
+ }
+}
+
+:host ::ng-deep {
+ .mapping-list {
+ mat-form-field {
+ .mat-form-field-wrapper {
+ padding-bottom: 0;
+ .mat-form-field-infix {
+ border-top-width: 0.2em;
+ width: auto;
+ min-width: auto;
+ }
+ .mat-form-field-underline {
+ bottom: 0;
+ }
+ }
+ }
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.ts
new file mode 100644
index 0000000000..53dbec422c
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-mapping.component.ts
@@ -0,0 +1,165 @@
+///
+/// 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 { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
+import {
+ AbstractControl,
+ ControlValueAccessor,
+ FormArray,
+ FormBuilder,
+ FormGroup,
+ NG_VALIDATORS,
+ NG_VALUE_ACCESSOR,
+ ValidationErrors,
+ Validator,
+ Validators
+} from '@angular/forms';
+import { SnmpMapping } from '@shared/models/device.models';
+import { Subscription } from 'rxjs';
+import { DataType, DataTypeTranslationMap } from '@shared/models/constants';
+import { isUndefinedOrNull } from '@core/utils';
+
+@Component({
+ selector: 'tb-snmp-device-profile-mapping',
+ templateUrl: './snmp-device-profile-mapping.component.html',
+ styleUrls: ['./snmp-device-profile-mapping.component.scss'],
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => SnmpDeviceProfileMappingComponent),
+ multi: true
+ },
+ {
+ provide: NG_VALIDATORS,
+ useExisting: forwardRef(() => SnmpDeviceProfileMappingComponent),
+ multi: true
+ }]
+})
+export class SnmpDeviceProfileMappingComponent implements OnInit, OnDestroy, ControlValueAccessor, Validator {
+
+ mappingsConfigForm: FormGroup;
+
+ dataTypes = Object.values(DataType);
+ dataTypesTranslationMap = DataTypeTranslationMap;
+
+ @Input()
+ disabled: boolean;
+
+ private readonly oidPattern: RegExp = /^\.?([0-2])((\.0)|(\.[1-9][0-9]*))*$/;
+
+ private valueChange$: Subscription = null;
+ private propagateChange = (v: any) => { };
+
+ constructor(private fb: FormBuilder) { }
+
+ ngOnInit() {
+ this.mappingsConfigForm = this.fb.group({
+ mappings: this.fb.array([])
+ });
+ }
+
+ ngOnDestroy() {
+ if (this.valueChange$) {
+ this.valueChange$.unsubscribe();
+ }
+ }
+
+ registerOnChange(fn: any) {
+ this.propagateChange = fn;
+ }
+
+ registerOnTouched(fn: any) {
+ }
+
+ setDisabledState(isDisabled: boolean) {
+ this.disabled = isDisabled;
+ if (this.disabled) {
+ this.mappingsConfigForm.disable({emitEvent: false});
+ } else {
+ this.mappingsConfigForm.enable({emitEvent: false});
+ }
+ }
+
+ validate(): ValidationErrors | null {
+ return this.mappingsConfigForm.valid && this.mappingsConfigForm.value.mappings.length ? null : {
+ mapping: false
+ };
+ }
+
+ writeValue(mappings: SnmpMapping[]) {
+ if (this.valueChange$) {
+ this.valueChange$.unsubscribe();
+ }
+ const mappingsControl: Array = [];
+ if (mappings) {
+ mappings.forEach((config) => {
+ mappingsControl.push(this.createdFormGroup(config));
+ });
+ }
+ this.mappingsConfigForm.setControl('mappings', this.fb.array(mappingsControl));
+ if (!mappings || !mappings.length) {
+ this.addMappingConfig();
+ }
+ if (this.disabled) {
+ this.mappingsConfigForm.disable({emitEvent: false});
+ } else {
+ this.mappingsConfigForm.enable({emitEvent: false});
+ }
+ this.valueChange$ = this.mappingsConfigForm.valueChanges.subscribe(() => {
+ this.updateModel();
+ });
+ if (!this.disabled && !this.mappingsConfigForm.valid) {
+ this.updateModel();
+ }
+ }
+
+ mappingsConfigFormArray(): FormArray {
+ return this.mappingsConfigForm.get('mappings') as FormArray;
+ }
+
+ public addMappingConfig() {
+ this.mappingsConfigFormArray().push(this.createdFormGroup());
+ this.mappingsConfigForm.updateValueAndValidity();
+ if (!this.mappingsConfigForm.valid) {
+ this.updateModel();
+ }
+ }
+
+ public removeMappingConfig(index: number) {
+ this.mappingsConfigFormArray().removeAt(index);
+ }
+
+ private createdFormGroup(value?: SnmpMapping): FormGroup {
+ if (isUndefinedOrNull(value)) {
+ value = {
+ dataType: DataType.STRING,
+ key: '',
+ oid: ''
+ };
+ }
+ return this.fb.group({
+ dataType: [value.dataType, Validators.required],
+ key: [value.key, Validators.required],
+ oid: [value.oid, [Validators.required, Validators.pattern(this.oidPattern)]]
+ });
+ }
+
+ private updateModel() {
+ const value: SnmpMapping[] = this.mappingsConfigForm.get('mappings').value;
+ this.propagateChange(value);
+ }
+
+}
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.html
new file mode 100644
index 0000000000..7dad337d72
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.html
@@ -0,0 +1,46 @@
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.ts
similarity index 66%
rename from ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts
rename to ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.ts
index e6749a9219..a384acbae3 100644
--- a/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts
+++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component.ts
@@ -15,9 +15,16 @@
///
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
-import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
-import { Store } from '@ngrx/store';
-import { AppState } from '@app/core/core.state';
+import {
+ ControlValueAccessor,
+ FormBuilder,
+ FormGroup,
+ NG_VALIDATORS,
+ NG_VALUE_ACCESSOR,
+ ValidationErrors,
+ Validator,
+ Validators
+} from '@angular/forms';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {
DeviceProfileTransportConfiguration,
@@ -40,19 +47,24 @@ export interface OidMappingConfiguration {
selector: 'tb-snmp-device-profile-transport-configuration',
templateUrl: './snmp-device-profile-transport-configuration.component.html',
styleUrls: [],
- providers: [{
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => SnmpDeviceProfileTransportConfigurationComponent),
- multi: true
- }]
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => SnmpDeviceProfileTransportConfigurationComponent),
+ multi: true
+ },
+ {
+ provide: NG_VALIDATORS,
+ useExisting: forwardRef(() => SnmpDeviceProfileTransportConfigurationComponent),
+ multi: true
+ }]
})
-export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy {
+export class SnmpDeviceProfileTransportConfigurationComponent implements OnInit, OnDestroy, ControlValueAccessor, Validator {
snmpDeviceProfileTransportConfigurationFormGroup: FormGroup;
private destroy$ = new Subject();
private requiredValue: boolean;
- private configuration = [];
get required(): boolean {
return this.requiredValue;
@@ -69,12 +81,14 @@ export class SnmpDeviceProfileTransportConfigurationComponent implements Control
private propagateChange = (v: any) => {
}
- constructor(private store: Store, private fb: FormBuilder) {
+ constructor(private fb: FormBuilder) {
}
ngOnInit(): void {
this.snmpDeviceProfileTransportConfigurationFormGroup = this.fb.group({
- configuration: [null, Validators.required]
+ timeoutMs: [0, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
+ retries: [0, [Validators.required, Validators.min(0), Validators.pattern('[0-9]*')]],
+ communicationConfigs: [null, Validators.required],
});
this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
@@ -95,18 +109,33 @@ export class SnmpDeviceProfileTransportConfigurationComponent implements Control
registerOnTouched(fn: any): void {
}
+ setDisabledState(isDisabled: boolean) {
+ this.disabled = isDisabled;
+ if (this.disabled) {
+ this.snmpDeviceProfileTransportConfigurationFormGroup.disable({emitEvent: false});
+ } else {
+ this.snmpDeviceProfileTransportConfigurationFormGroup.enable({emitEvent: false});
+ }
+ }
+
writeValue(value: SnmpDeviceProfileTransportConfiguration | null): void {
if (isDefinedAndNotNull(value)) {
- this.snmpDeviceProfileTransportConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false});
+ this.snmpDeviceProfileTransportConfigurationFormGroup.patchValue(value, {emitEvent: !value.communicationConfigs});
}
}
private updateModel() {
let configuration: DeviceProfileTransportConfiguration = null;
if (this.snmpDeviceProfileTransportConfigurationFormGroup.valid) {
- configuration = this.snmpDeviceProfileTransportConfigurationFormGroup.getRawValue().configuration;
+ configuration = this.snmpDeviceProfileTransportConfigurationFormGroup.getRawValue();
configuration.type = DeviceTransportType.SNMP;
}
this.propagateChange(configuration);
}
+
+ validate(): ValidationErrors | null {
+ return this.snmpDeviceProfileTransportConfigurationFormGroup.valid ? null : {
+ snmpDeviceProfileTransportConfiguration: false
+ };
+ }
}
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport.module.ts b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport.module.ts
new file mode 100644
index 0000000000..c0797d9b0e
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/profile/device/snpm/snmp-device-profile-transport.module.ts
@@ -0,0 +1,38 @@
+///
+/// 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 { SharedModule } from '@shared/shared.module';
+import { CommonModule } from '@angular/common';
+import { SnmpDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/snpm/snmp-device-profile-transport-configuration.component';
+import { SnmpDeviceProfileCommunicationConfigComponent } from './snmp-device-profile-communication-config.component';
+import { SnmpDeviceProfileMappingComponent } from './snmp-device-profile-mapping.component';
+
+@NgModule({
+ declarations: [
+ SnmpDeviceProfileTransportConfigurationComponent,
+ SnmpDeviceProfileCommunicationConfigComponent,
+ SnmpDeviceProfileMappingComponent
+ ],
+ imports: [
+ CommonModule,
+ SharedModule
+ ],
+ exports: [
+ SnmpDeviceProfileTransportConfigurationComponent
+ ]
+})
+export class SnmpDeviceProfileTransportModule { }
diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts
index b72e2b196d..cd81753c76 100644
--- a/ui-ngx/src/app/shared/models/constants.ts
+++ b/ui-ngx/src/app/shared/models/constants.ts
@@ -147,6 +147,22 @@ export enum ValueType {
JSON = 'JSON'
}
+export enum DataType {
+ STRING = 'STRING',
+ LONG = 'LONG',
+ BOOLEAN = 'BOOLEAN',
+ DOUBLE = 'DOUBLE',
+ JSON = 'JSON'
+}
+
+export const DataTypeTranslationMap = new Map([
+ [DataType.STRING, 'value.string'],
+ [DataType.LONG, 'value.integer'],
+ [DataType.BOOLEAN, 'value.boolean'],
+ [DataType.DOUBLE, 'value.double'],
+ [DataType.JSON, 'value.json']
+]);
+
export const valueTypesMap = new Map(
[
[
diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts
index 7af8e56472..568faeb452 100644
--- a/ui-ngx/src/app/shared/models/device.models.ts
+++ b/ui-ngx/src/app/shared/models/device.models.ts
@@ -29,6 +29,7 @@ import * as _moment from 'moment';
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';
export enum DeviceProfileType {
DEFAULT = 'DEFAULT',
@@ -257,7 +258,35 @@ export interface Lwm2mDeviceProfileTransportConfiguration {
}
export interface SnmpDeviceProfileTransportConfiguration {
- [key: string]: any;
+ timeoutMs?: number;
+ retries?: number;
+ communicationConfigs?: SnmpCommunicationConfig[];
+}
+
+export enum SnmpSpecType {
+ TELEMETRY_QUERYING = 'TELEMETRY_QUERYING',
+ CLIENT_ATTRIBUTES_QUERYING = 'CLIENT_ATTRIBUTES_QUERYING',
+ SHARED_ATTRIBUTES_SETTING = 'SHARED_ATTRIBUTES_SETTING',
+ TO_DEVICE_RPC_REQUEST = 'TO_DEVICE_RPC_REQUEST'
+}
+
+export const SnmpSpecTypeTranslationMap = new Map([
+ [SnmpSpecType.TELEMETRY_QUERYING, ' Telemetry'],
+ [SnmpSpecType.CLIENT_ATTRIBUTES_QUERYING, 'Client attributes'],
+ [SnmpSpecType.SHARED_ATTRIBUTES_SETTING, 'Shared attributes'],
+ [SnmpSpecType.TO_DEVICE_RPC_REQUEST, 'RPC request']
+]);
+
+export interface SnmpCommunicationConfig {
+ spec: SnmpSpecType;
+ mappings: SnmpMapping[];
+ queryingFrequencyMs?: number;
+}
+
+export interface SnmpMapping {
+ oid: string;
+ key: string;
+ dataType: DataType;
}
export type DeviceProfileTransportConfigurations = DefaultDeviceProfileTransportConfiguration &
@@ -332,7 +361,11 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT
transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M};
break;
case DeviceTransportType.SNMP:
- const snmpTransportConfiguration: SnmpDeviceProfileTransportConfiguration = {};
+ const snmpTransportConfiguration: SnmpDeviceProfileTransportConfiguration = {
+ timeoutMs: 0,
+ retries: 0,
+ communicationConfigs: null
+ };
transportConfiguration = {...snmpTransportConfiguration, type: DeviceTransportType.SNMP};
break;
}
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 ec084b8a3a..95dc0c2d72 100644
--- a/ui-ngx/src/assets/locale/locale.constant-en_US.json
+++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json
@@ -1297,6 +1297,31 @@
"sw-update-recourse": "Software update CoAP recourse",
"sw-update-recourse-required": "Software update CoAP recourse is required.",
"config-json-tab": "Json Config Profile Device"
+ },
+ "snmp": {
+ "add-communication-config": "Add communication config",
+ "add-mapping": "Add mapping",
+ "communication-configs": "Communication configs",
+ "data-key": "Data key",
+ "data-key-required": "Data key is required.",
+ "data-type": "Data type",
+ "data-type-required": "Data type is required.",
+ "oid": "OID",
+ "oid-pattern": "Invalid OID format.",
+ "oid-required": "OID is required.",
+ "please-add-communication-config": "Please add communication config",
+ "please-add-mapping-config": "Please add mapping config",
+ "querying-frequency": "Querying frequency, ms",
+ "querying-frequency-invalid-format": "Querying frequency must be a positive integer.",
+ "querying-frequency-required": "Querying frequency is required.",
+ "retries": "Retries",
+ "retries-invalid-format": "Retries must be a positive integer.",
+ "retries-required": "Retries is required.",
+ "scope": "Scope",
+ "scope-required": "Scope is required.",
+ "timeout-ms": "Timeout, ms",
+ "timeout-ms-invalid-format": "Timeout must be a positive integer.",
+ "timeout-ms-required": "Timeout is required."
}
},
"dialog": {