committed by
GitHub
20 changed files with 1195 additions and 88 deletions
@ -1,23 +0,0 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
<form [formGroup]="snmpDeviceProfileTransportConfigurationFormGroup" style="padding-bottom: 16px;"> |
|||
<tb-json-object-edit |
|||
required |
|||
formControlName="configuration"> |
|||
</tb-json-object-edit> |
|||
</form> |
|||
@ -0,0 +1,72 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
<div fxLayout="column"> |
|||
<div *ngFor="let deviceProfileCommunication of communicationConfigFormArray().controls; let $index = index; |
|||
last as isLast;" fxLayout="row" fxLayoutAlign="start center" |
|||
fxLayoutGap="8px" class="scope-row" [formGroup]="deviceProfileCommunication"> |
|||
<div class="communication-config" fxFlex fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start"> |
|||
<mat-form-field class="spec mat-block" floatLabel="always" hideRequiredMarker> |
|||
<mat-label translate>device-profile.snmp.scope</mat-label> |
|||
<mat-select formControlName="spec" required> |
|||
<mat-option *ngFor="let snmpSpecType of snmpSpecTypes" [value]="snmpSpecType" |
|||
[disabled]="isDisabledSeverity(snmpSpecType, $index)"> |
|||
{{ snmpSpecTypeTranslationMap.get(snmpSpecType) }} |
|||
</mat-option> |
|||
</mat-select> |
|||
<mat-error *ngIf="deviceProfileCommunication.get('spec').hasError('required')"> |
|||
{{ 'device-profile.snmp.scope-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
<mat-divider vertical></mat-divider> |
|||
<section fxFlex fxLayout="column"> |
|||
<mat-form-field *ngIf="isShowFrequency(deviceProfileCommunication.get('spec').value)"> |
|||
<mat-label translate>device-profile.snmp.querying-frequency</mat-label> |
|||
<input matInput formControlName="queryingFrequencyMs" type="number" min="0" required/> |
|||
<mat-error *ngIf="deviceProfileCommunication.get('queryingFrequencyMs').hasError('required')"> |
|||
{{ 'device-profile.snmp.querying-frequency-required' | translate }} |
|||
</mat-error> |
|||
<mat-error *ngIf="deviceProfileCommunication.get('queryingFrequencyMs').hasError('pattern') || |
|||
deviceProfileCommunication.get('queryingFrequencyMs').hasError('min')"> |
|||
{{ 'device-profile.snmp.querying-frequency-invalid-format' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
<tb-snmp-device-profile-mapping formControlName="mappings"> |
|||
</tb-snmp-device-profile-mapping> |
|||
</section> |
|||
</div> |
|||
<button *ngIf="!disabled" |
|||
mat-icon-button color="primary" style="min-width: 40px;" |
|||
type="button" |
|||
(click)="removeCommunicationConfig($index)" |
|||
matTooltip="{{ 'action.remove' | translate }}" |
|||
matTooltipPosition="above"> |
|||
<mat-icon>remove_circle_outline</mat-icon> |
|||
</button> |
|||
</div> |
|||
<div *ngIf="!communicationConfigFormArray().controls.length && !disabled"> |
|||
<span fxLayoutAlign="center center" class="tb-prompt required required-text" translate>device-profile.snmp.please-add-communication-config</span> |
|||
</div> |
|||
<div *ngIf="!disabled && isAddEnabled"> |
|||
<button mat-stroked-button color="primary" |
|||
type="button" |
|||
(click)="addCommunicationConfig()"> |
|||
<mat-icon class="button-icon">add_circle_outline</mat-icon> |
|||
{{ 'device-profile.snmp.add-communication-config' | translate }} |
|||
</button> |
|||
</div> |
|||
</div> |
|||
@ -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; |
|||
} |
|||
} |
|||
@ -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<AbstractControl> = []; |
|||
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: 5000, |
|||
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(5000, [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); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
<!-- |
|||
|
|||
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. |
|||
|
|||
--> |
|||
<div fxFlex fxLayout="column" class="mapping-config"> |
|||
<div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" fxFlex="100"> |
|||
<div fxFlex fxLayout="row" fxLayoutGap="8px"> |
|||
<label fxFlex="26" class="tb-title no-padding" translate>device-profile.snmp.data-type</label> |
|||
<label fxFlex="37" class="tb-title no-padding" translate>device-profile.snmp.data-key</label> |
|||
<label fxFlex="37" class="tb-title no-padding" translate>device-profile.snmp.oid</label> |
|||
<span style="min-width: 40px" [fxShow]="!disabled"></span> |
|||
</div> |
|||
</div> |
|||
<mat-divider></mat-divider> |
|||
<div *ngFor="let mappingConfig of mappingsConfigFormArray().controls; let $index = index; |
|||
last as isLast;" fxLayout="row" fxLayoutAlign="start center" |
|||
fxLayoutGap="8px" [formGroup]="mappingConfig" class="mapping-list"> |
|||
<div fxFlex fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start"> |
|||
<mat-form-field fxFlex="26" floatLabel="always" hideRequiredMarker> |
|||
<mat-label></mat-label> |
|||
<mat-select formControlName="dataType" required> |
|||
<mat-option *ngFor="let dataType of dataTypes" [value]="dataType"> |
|||
{{ dataTypesTranslationMap.get(dataType) | translate }} |
|||
</mat-option> |
|||
</mat-select> |
|||
<mat-error *ngIf="mappingConfig.get('dataType').hasError('required')"> |
|||
{{ 'device-profile.snmp.data-type-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex="37"> |
|||
<mat-label></mat-label> |
|||
<input matInput formControlName="key" required/> |
|||
<mat-error *ngIf="mappingConfig.get('key').hasError('required')"> |
|||
{{ 'device-profile.snmp.data-key-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
<mat-form-field floatLabel="always" hideRequiredMarker fxFlex="37"> |
|||
<mat-label></mat-label> |
|||
<input matInput formControlName="oid" required/> |
|||
<mat-error *ngIf="mappingConfig.get('oid').hasError('required')"> |
|||
{{ 'device-profile.snmp.oid-required' | translate }} |
|||
</mat-error> |
|||
<mat-error *ngIf="mappingConfig.get('oid').hasError('pattern')"> |
|||
{{ 'device-profile.snmp.oid-pattern' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
<button *ngIf="!disabled" |
|||
mat-icon-button color="primary" |
|||
type="button" |
|||
(click)="removeMappingConfig($index)" |
|||
matTooltip="{{ 'action.remove' | translate }}" |
|||
matTooltipPosition="above"> |
|||
<mat-icon>close</mat-icon> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
<div *ngIf="!mappingsConfigFormArray().controls.length && !disabled"> |
|||
<span fxLayoutAlign="center center" class="tb-prompt required required-text" translate>device-profile.snmp.please-add-mapping-config</span> |
|||
</div> |
|||
<div *ngIf="!disabled"> |
|||
<button mat-stroked-button color="primary" |
|||
type="button" |
|||
(click)="addMappingConfig()"> |
|||
<mat-icon class="button-icon">add_circle_outline</mat-icon> |
|||
{{ 'device-profile.snmp.add-mapping' | translate }} |
|||
</button> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,49 @@ |
|||
/** |
|||
* 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; |
|||
} |
|||
.mat-form-field-subscript-wrapper{ |
|||
margin-top: 1.8em; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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<AbstractControl> = []; |
|||
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); |
|||
} |
|||
|
|||
} |
|||
@ -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. |
|||
|
|||
--> |
|||
<form [formGroup]="snmpDeviceProfileTransportConfigurationFormGroup" style="padding: 8px 0 16px;"> |
|||
<section fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"> |
|||
<mat-form-field fxFlex> |
|||
<mat-label translate>device-profile.snmp.timeout-ms</mat-label> |
|||
<input matInput formControlName="timeoutMs" type="number" min="0" required/> |
|||
<mat-error *ngIf="snmpDeviceProfileTransportConfigurationFormGroup.get('timeoutMs').hasError('required')"> |
|||
{{ 'device-profile.snmp.timeout-ms-required' | translate }} |
|||
</mat-error> |
|||
<mat-error *ngIf="snmpDeviceProfileTransportConfigurationFormGroup.get('timeoutMs').hasError('pattern') || |
|||
snmpDeviceProfileTransportConfigurationFormGroup.get('timeoutMs').hasError('min')"> |
|||
{{ 'device-profile.snmp.timeout-ms-invalid-format' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
<mat-form-field fxFlex> |
|||
<mat-label translate>device-profile.snmp.retries</mat-label> |
|||
<input matInput formControlName="retries" type="number" min="0" required/> |
|||
<mat-error *ngIf="snmpDeviceProfileTransportConfigurationFormGroup.get('retries').hasError('required')"> |
|||
{{ 'device-profile.snmp.retries-required' | translate }} |
|||
</mat-error> |
|||
<mat-error *ngIf="snmpDeviceProfileTransportConfigurationFormGroup.get('retries').hasError('pattern') || |
|||
snmpDeviceProfileTransportConfigurationFormGroup.get('retries').hasError('min')"> |
|||
{{ 'device-profile.snmp.retries-invalid-format' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
</section> |
|||
<div class="tb-small" style="padding-bottom: 8px" translate>device-profile.snmp.communication-configs</div> |
|||
<tb-snmp-device-profile-communication-config formControlName="communicationConfigs"> |
|||
</tb-snmp-device-profile-communication-config> |
|||
</form> |
|||
@ -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 { } |
|||
Loading…
Reference in new issue