major refactoring
This commit is contained in:
parent
e9d58525bc
commit
7b9d8e0a55
@ -68,12 +68,6 @@ import { ModbusMasterTableComponent } from '../modbus-master-table/modbus-master
|
||||
overflow: hidden !important;
|
||||
}
|
||||
}
|
||||
|
||||
:host ::ng-deep {
|
||||
.mat-mdc-tab-group, .mat-mdc-tab-body-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
`]
|
||||
})
|
||||
|
||||
@ -83,10 +77,9 @@ export class ModbusBasicConfigComponent implements ControlValueAccessor, Validat
|
||||
|
||||
basicFormGroup: FormGroup;
|
||||
|
||||
onChange: (value: string) => void;
|
||||
onChange: (value: ModbusBasicConfig) => void;
|
||||
onTouched: () => void;
|
||||
|
||||
protected readonly connectorType = ConnectorType;
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
@ -108,7 +101,7 @@ export class ModbusBasicConfigComponent implements ControlValueAccessor, Validat
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: string) => void): void {
|
||||
registerOnChange(fn: (value: ModbusBasicConfig) => void): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
@ -118,8 +111,8 @@ export class ModbusBasicConfigComponent implements ControlValueAccessor, Validat
|
||||
|
||||
writeValue(basicConfig: ModbusBasicConfig): void {
|
||||
const editedBase = {
|
||||
slave: basicConfig.slave || {},
|
||||
master: basicConfig.master || {},
|
||||
slave: basicConfig.slave ?? {},
|
||||
master: basicConfig.master ?? {},
|
||||
};
|
||||
|
||||
this.basicFormGroup.setValue(editedBase, {emitEvent: false});
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
<div class="tb-form-panel-title">{{ panelTitle | translate }}{{' (' + keysListFormArray.controls.length + ')'}}</div>
|
||||
<div class="tb-form-panel no-border no-padding key-panel" *ngIf="keysListFormArray.controls.length; else noKeys">
|
||||
<div class="tb-form-panel no-border no-padding tb-flex no-flex row center fill-width"
|
||||
*ngFor="let keyControl of keysListFormArray.controls; trackBy: trackByKey; let $index = index; let last = last;">
|
||||
*ngFor="let keyControl of keysListFormArray.controls; trackBy: trackByControlId; let $index = index; let last = last;">
|
||||
<div class="tb-form-panel stroked tb-flex">
|
||||
<ng-container [formGroup]="keyControl">
|
||||
<mat-expansion-panel class="tb-settings" [expanded]="last">
|
||||
@ -99,10 +99,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||
<div class="fixed-title-width" translate>gateway.address</div>
|
||||
<div class="fixed-title-width tb-required" translate>gateway.address</div>
|
||||
<div class="tb-flex no-gap">
|
||||
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
|
||||
<input matInput type="number" min="0" max="50000" name="value" formControlName="address" placeholder="{{ 'gateway.set' | translate }}"/>
|
||||
<mat-icon matSuffix
|
||||
matTooltipPosition="above"
|
||||
matTooltipClass="tb-error-tooltip"
|
||||
[matTooltip]="('gateway.address-required') | translate"
|
||||
*ngIf="keyControl.get('address').hasError('required') &&
|
||||
keyControl.get('address').touched"
|
||||
class="tb-error">
|
||||
warning
|
||||
</mat-icon>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -14,14 +14,21 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { AbstractControl, FormGroup, UntypedFormArray, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
||||
import {
|
||||
AbstractControl,
|
||||
FormArray,
|
||||
FormGroup,
|
||||
UntypedFormArray,
|
||||
UntypedFormBuilder,
|
||||
UntypedFormGroup,
|
||||
Validators
|
||||
} from '@angular/forms';
|
||||
import { TbPopoverComponent } from '@shared/components/popover.component';
|
||||
import {
|
||||
ModbusDataType,
|
||||
ModbusFunctionCodeTranslationsMap,
|
||||
ModbusObjectCountByDataType,
|
||||
ModbusRegisterType,
|
||||
ModbusValue,
|
||||
ModbusValueKey,
|
||||
noLeadTrailSpacesRegex,
|
||||
@ -31,6 +38,8 @@ import { SharedModule } from '@shared/shared.module';
|
||||
import { GatewayHelpLinkPipe } from '@home/pipes/gateway-help-link.pipe';
|
||||
import { generateSecret } from '@core/utils';
|
||||
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-modbus-data-keys-panel',
|
||||
@ -43,7 +52,7 @@ import { coerceBoolean } from '@shared/decorators/coercion';
|
||||
GatewayHelpLinkPipe,
|
||||
]
|
||||
})
|
||||
export class ModbusDataKeysPanelComponent implements OnInit {
|
||||
export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy {
|
||||
|
||||
@coerceBoolean()
|
||||
@Input() isMaster = false;
|
||||
@ -51,15 +60,13 @@ export class ModbusDataKeysPanelComponent implements OnInit {
|
||||
@Input() addKeyTitle: string;
|
||||
@Input() deleteKeyTitle: string;
|
||||
@Input() noKeysText: string;
|
||||
@Input() register: ModbusRegisterType;
|
||||
@Input() keysType: ModbusValueKey;
|
||||
@Input() values: ModbusValue[];
|
||||
@Input() popover: TbPopoverComponent<ModbusDataKeysPanelComponent>;
|
||||
|
||||
@Output() keysDataApplied = new EventEmitter<Array<ModbusValue>>();
|
||||
|
||||
keysListFormArray: UntypedFormArray;
|
||||
errorText = '';
|
||||
keysListFormArray: FormArray<UntypedFormGroup>;
|
||||
modbusDataTypes = Object.values(ModbusDataType);
|
||||
withFunctionCode = true;
|
||||
functionCodesMap = new Map();
|
||||
@ -67,9 +74,12 @@ export class ModbusDataKeysPanelComponent implements OnInit {
|
||||
|
||||
readonly editableDataTypes = [ModbusDataType.BYTES, ModbusDataType.BITS, ModbusDataType.STRING];
|
||||
readonly ModbusFunctionCodeTranslationsMap = ModbusFunctionCodeTranslationsMap;
|
||||
readonly defaultReadFunctionCodes = [3, 4];
|
||||
readonly defaultWriteFunctionCodes = [5, 6, 15, 16];
|
||||
readonly stringAttrUpdatesWriteFunctionCodes = [6, 16];
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
private readonly defaultReadFunctionCodes = [3, 4];
|
||||
private readonly defaultWriteFunctionCodes = [5, 6, 15, 16];
|
||||
private readonly stringAttrUpdatesWriteFunctionCodes = [6, 16];
|
||||
|
||||
constructor(private fb: UntypedFormBuilder) {}
|
||||
|
||||
@ -79,8 +89,13 @@ export class ModbusDataKeysPanelComponent implements OnInit {
|
||||
this.defaultFunctionCodes = this.getDefaultFunctionCodes();
|
||||
}
|
||||
|
||||
trackByKey(_: number, keyControl: AbstractControl): AbstractControl {
|
||||
return keyControl;
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
trackByControlId(_: number, keyControl: AbstractControl): string {
|
||||
return keyControl.value.id;
|
||||
}
|
||||
|
||||
addKey(): void {
|
||||
@ -88,7 +103,7 @@ export class ModbusDataKeysPanelComponent implements OnInit {
|
||||
tag: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
value: [{value: '', disabled: !this.isMaster}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
type: [ModbusDataType.BYTES, [Validators.required]],
|
||||
address: [0, [Validators.required]],
|
||||
address: [null, [Validators.required]],
|
||||
objectsCount: [1, [Validators.required]],
|
||||
functionCode: [this.getDefaultFunctionCodes()[0]],
|
||||
id: [{value: generateSecret(5), disabled: true}],
|
||||
@ -107,7 +122,7 @@ export class ModbusDataKeysPanelComponent implements OnInit {
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this.popover?.hide();
|
||||
this.popover.hide();
|
||||
}
|
||||
|
||||
applyKeysData(): void {
|
||||
@ -116,32 +131,38 @@ export class ModbusDataKeysPanelComponent implements OnInit {
|
||||
|
||||
private prepareKeysFormArray(values: ModbusValue[]): UntypedFormArray {
|
||||
const keysControlGroups: Array<AbstractControl> = [];
|
||||
|
||||
if (values) {
|
||||
values.forEach(keyData => {
|
||||
const { tag, value, type, address, objectsCount, functionCode } = keyData;
|
||||
const dataKeyFormGroup = this.fb.group({
|
||||
tag: [tag, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
value: [{value, disabled: !this.isMaster}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
type: [type, [Validators.required]],
|
||||
address: [address, [Validators.required]],
|
||||
objectsCount: [objectsCount, [Validators.required]],
|
||||
functionCode: [functionCode, []],
|
||||
id: [{value: generateSecret(5), disabled: true}],
|
||||
});
|
||||
values.forEach(value => {
|
||||
const dataKeyFormGroup = this.createDataKeyFormGroup(value);
|
||||
this.observeKeyDataType(dataKeyFormGroup);
|
||||
this.functionCodesMap.set(dataKeyFormGroup.get('id').value, this.getFunctionCodes(type));
|
||||
this.functionCodesMap.set(dataKeyFormGroup.get('id').value, this.getFunctionCodes(value.type));
|
||||
|
||||
keysControlGroups.push(dataKeyFormGroup);
|
||||
});
|
||||
}
|
||||
|
||||
return this.fb.array(keysControlGroups);
|
||||
}
|
||||
|
||||
private createDataKeyFormGroup(modbusValue: ModbusValue): FormGroup {
|
||||
const { tag, value, type, address, objectsCount, functionCode } = modbusValue;
|
||||
|
||||
return this.fb.group({
|
||||
tag: [tag, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
value: [{ value, disabled: !this.isMaster }, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
type: [type, [Validators.required]],
|
||||
address: [address, [Validators.required]],
|
||||
objectsCount: [objectsCount, [Validators.required]],
|
||||
functionCode: [functionCode, [Validators.required]],
|
||||
id: [{ value: generateSecret(5), disabled: true }],
|
||||
});
|
||||
}
|
||||
|
||||
private observeKeyDataType(keyFormGroup: FormGroup): void {
|
||||
keyFormGroup.get('type').valueChanges.subscribe(dataType => {
|
||||
const objectsCountControl = keyFormGroup.get('objectsCount');
|
||||
keyFormGroup.get('type').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(dataType => {
|
||||
if (!this.editableDataTypes.includes(dataType)) {
|
||||
objectsCountControl.patchValue(ModbusObjectCountByDataType[dataType]);
|
||||
keyFormGroup.get('objectsCount').patchValue(ModbusObjectCountByDataType[dataType], {emitEvent: false});
|
||||
}
|
||||
this.functionCodesMap.set(keyFormGroup.get('id').value, this.getFunctionCodes(dataType));
|
||||
});
|
||||
@ -149,20 +170,21 @@ export class ModbusDataKeysPanelComponent implements OnInit {
|
||||
|
||||
private getFunctionCodes(dataType: ModbusDataType): number[] {
|
||||
if (this.keysType === ModbusValueKey.ATTRIBUTES_UPDATES) {
|
||||
if (dataType === ModbusDataType.STRING) {
|
||||
return this.stringAttrUpdatesWriteFunctionCodes;
|
||||
}
|
||||
return this.defaultWriteFunctionCodes;
|
||||
return dataType === ModbusDataType.STRING
|
||||
? this.stringAttrUpdatesWriteFunctionCodes
|
||||
: this.defaultWriteFunctionCodes;
|
||||
}
|
||||
|
||||
const functionCodes = [...this.defaultReadFunctionCodes];
|
||||
if (dataType === ModbusDataType.BITS) {
|
||||
const bitsFunctionCodes = [1, 2];
|
||||
bitsFunctionCodes.forEach(code => functionCodes.push(code));
|
||||
functionCodes.push(...bitsFunctionCodes);
|
||||
functionCodes.sort();
|
||||
}
|
||||
if (this.keysType === ModbusValueKey.RPC_REQUESTS) {
|
||||
this.defaultWriteFunctionCodes.forEach(code => functionCodes.push(code));
|
||||
functionCodes.push(...this.defaultWriteFunctionCodes);
|
||||
}
|
||||
|
||||
return functionCodes;
|
||||
}
|
||||
|
||||
|
||||
@ -60,18 +60,18 @@
|
||||
<div class="table-container">
|
||||
<table mat-table [dataSource]="dataSource">
|
||||
<ng-container [matColumnDef]="'name'">
|
||||
<mat-header-cell *matHeaderCellDef class="table-value-column request-column">
|
||||
<mat-header-cell *matHeaderCellDef class="table-value-column">
|
||||
{{ 'gateway.name' | translate }}
|
||||
</mat-header-cell>
|
||||
<mat-cell *matCellDef="let mapping" class="table-value-column request-column">
|
||||
<mat-cell *matCellDef="let mapping" class="table-value-column">
|
||||
{{ mapping['name'] }}
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
<ng-container [matColumnDef]="'type'">
|
||||
<mat-header-cell *matHeaderCellDef class="table-value-column request-column">
|
||||
<mat-header-cell *matHeaderCellDef class="table-value-column">
|
||||
{{ 'gateway.client-communication-type' | translate }}
|
||||
</mat-header-cell>
|
||||
<mat-cell *matCellDef="let mapping" class="table-value-column request-column">
|
||||
<mat-cell *matCellDef="let mapping" class="table-value-column">
|
||||
{{ ModbusProtocolLabelsMap.get(mapping['type']) }}
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
@ -111,7 +111,7 @@
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
<mat-header-row [ngClass]="{'mat-row-select': true}" *matHeaderRowDef="['name', 'type', 'actions']; sticky: true"></mat-header-row>
|
||||
<mat-row *matRowDef="let mapping; columns: ['name', 'type', 'actions'];"></mat-row>
|
||||
<mat-row *matRowDef="let mapping; columns: ['name', 'type', 'actions']"></mat-row>
|
||||
</table>
|
||||
<section [fxShow]="!textSearchMode && (dataSource.isEmpty() | async)" fxLayoutAlign="center center"
|
||||
class="mat-headline-5 tb-absolute-fill tb-add-new">
|
||||
|
||||
@ -19,19 +19,15 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
|
||||
.tb-master-table {
|
||||
|
||||
.tb-master-table-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
|
||||
&.tb-outlined-border {
|
||||
box-shadow: 0 0 0 0 rgb(0 0 0 / 20%), 0 0 0 0 rgb(0 0 0 / 14%), 0 0 0 0 rgb(0 0 0 / 12%);
|
||||
border: solid 1px #e0e0e0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mat-toolbar-tools{
|
||||
min-height: auto;
|
||||
}
|
||||
@ -49,25 +45,17 @@
|
||||
|
||||
.table-container {
|
||||
overflow: auto;
|
||||
|
||||
.mat-mdc-table {
|
||||
table-layout: fixed;
|
||||
min-width: 450px;
|
||||
|
||||
.table-value-column {
|
||||
padding: 0 12px;
|
||||
width: 23%;
|
||||
|
||||
&.request-column {
|
||||
width: 38%;
|
||||
}
|
||||
width: 38%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,6 +79,7 @@
|
||||
:host ::ng-deep {
|
||||
mat-cell.tb-value-cell {
|
||||
cursor: pointer;
|
||||
|
||||
.mat-icon {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
forwardRef,
|
||||
@ -76,15 +77,12 @@ export class ModbusMasterTableComponent implements ControlValueAccessor, Validat
|
||||
|
||||
textSearchMode = false;
|
||||
dataSource: SlavesDatasource;
|
||||
hidePageSize = false;
|
||||
activeValue = false;
|
||||
dirtyValue = false;
|
||||
masterFormGroup: UntypedFormGroup;
|
||||
textSearch = this.fb.control('', {nonNullable: true});
|
||||
|
||||
readonly ModbusProtocolLabelsMap = ModbusProtocolLabelsMap;
|
||||
|
||||
private onChange: (value: string) => void = () => {};
|
||||
private onChange: (value: ModbusMasterConfig) => void = () => {};
|
||||
private onTouched: () => void = () => {};
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
@ -93,10 +91,10 @@ export class ModbusMasterTableComponent implements ControlValueAccessor, Validat
|
||||
public translate: TranslateService,
|
||||
public dialog: MatDialog,
|
||||
private dialogService: DialogService,
|
||||
private fb: FormBuilder
|
||||
private fb: FormBuilder,
|
||||
private cdr: ChangeDetectorRef,
|
||||
) {
|
||||
this.masterFormGroup = this.fb.group({ slaves: this.fb.array([])});
|
||||
this.dirtyValue = !this.activeValue;
|
||||
this.masterFormGroup = this.fb.group({ slaves: this.fb.array([]) });
|
||||
this.dataSource = new SlavesDatasource();
|
||||
}
|
||||
|
||||
@ -124,13 +122,10 @@ export class ModbusMasterTableComponent implements ControlValueAccessor, Validat
|
||||
debounceTime(150),
|
||||
distinctUntilChanged((prev, current) => (prev ?? '') === current.trim()),
|
||||
takeUntil(this.destroy$)
|
||||
).subscribe((text) => {
|
||||
const searchText = text.trim();
|
||||
this.updateTableData(this.slaves.value, searchText.trim());
|
||||
});
|
||||
).subscribe(text => this.updateTableData(this.slaves.value, text.trim()));
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: string) => void): void {
|
||||
registerOnChange(fn: (value: ModbusMasterConfig) => void): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
@ -151,10 +146,10 @@ export class ModbusMasterTableComponent implements ControlValueAccessor, Validat
|
||||
|
||||
enterFilterMode(): void {
|
||||
this.textSearchMode = true;
|
||||
setTimeout(() => {
|
||||
this.searchInputField.nativeElement.focus();
|
||||
this.searchInputField.nativeElement.setSelectionRange(0, 0);
|
||||
}, 10);
|
||||
this.cdr.detectChanges();
|
||||
const searchInput = this.searchInputField.nativeElement;
|
||||
searchInput.focus();
|
||||
searchInput.setSelectionRange(0, 0);
|
||||
}
|
||||
|
||||
exitFilterMode(): void {
|
||||
@ -167,19 +162,20 @@ export class ModbusMasterTableComponent implements ControlValueAccessor, Validat
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
const value = isDefinedAndNotNull(index) ? this.slaves.at(index).value : {};
|
||||
const withIndex = isDefinedAndNotNull(index);
|
||||
const value = withIndex ? this.slaves.at(index).value : {};
|
||||
this.dialog.open<ModbusSlaveDialogComponent, any, any>(ModbusSlaveDialogComponent, {
|
||||
disableClose: true,
|
||||
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
||||
data: {
|
||||
value,
|
||||
buttonTitle: isUndefinedOrNull(index) ? 'action.add' : 'action.apply'
|
||||
buttonTitle: withIndex ? 'action.add' : 'action.apply'
|
||||
}
|
||||
}).afterClosed()
|
||||
.pipe(take(1), takeUntil(this.destroy$))
|
||||
.subscribe(res => {
|
||||
if (res) {
|
||||
if (isDefinedAndNotNull(index)) {
|
||||
if (withIndex) {
|
||||
this.slaves.at(index).patchValue(res);
|
||||
} else {
|
||||
this.slaves.push(this.fb.control(res));
|
||||
@ -199,7 +195,7 @@ export class ModbusMasterTableComponent implements ControlValueAccessor, Validat
|
||||
this.translate.instant('action.no'),
|
||||
this.translate.instant('action.yes'),
|
||||
true
|
||||
).subscribe((result) => {
|
||||
).pipe(take(1), takeUntil(this.destroy$)).subscribe((result) => {
|
||||
if (result) {
|
||||
this.slaves.removeAt(index);
|
||||
this.masterFormGroup.markAsDirty();
|
||||
@ -208,15 +204,14 @@ export class ModbusMasterTableComponent implements ControlValueAccessor, Validat
|
||||
}
|
||||
|
||||
private updateTableData(data: SlaveConfig[], textSearch?: string): void {
|
||||
let tableValue = data;
|
||||
if (textSearch) {
|
||||
tableValue = tableValue.filter(value =>
|
||||
Object.values(value).some(val =>
|
||||
val.toString().toLowerCase().includes(textSearch.toLowerCase())
|
||||
data = data.filter(item =>
|
||||
Object.values(item).some(value =>
|
||||
value.toString().toLowerCase().includes(textSearch.toLowerCase())
|
||||
)
|
||||
);
|
||||
}
|
||||
this.dataSource.loadData(tableValue);
|
||||
this.dataSource.loadData(data);
|
||||
}
|
||||
|
||||
private pushDataAsFormArrays(slaves: SlaveConfig[]): void {
|
||||
|
||||
@ -85,7 +85,7 @@ export class ModbusSecurityConfigComponent implements ControlValueAccessor, Vali
|
||||
keyfile: ['', [Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
password: ['', [Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
server_hostname: ['', [Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
reqclicert: [{value: false, disabled: true}, []],
|
||||
reqclicert: [{value: false, disabled: true}],
|
||||
});
|
||||
|
||||
this.observeValueChanges();
|
||||
|
||||
@ -86,11 +86,8 @@
|
||||
<mat-icon matSuffix
|
||||
matTooltipPosition="above"
|
||||
matTooltipClass="tb-error-tooltip"
|
||||
[matTooltip]="slaveConfigFormGroup.get('serialPort') | getGatewayPortTooltip"
|
||||
*ngIf="(slaveConfigFormGroup.get('serialPort').hasError('required') ||
|
||||
slaveConfigFormGroup.get('serialPort').hasError('min') ||
|
||||
slaveConfigFormGroup.get('serialPort').hasError('max')) &&
|
||||
slaveConfigFormGroup.get('serialPort').touched"
|
||||
[matTooltip]="'gateway.port-required' | translate"
|
||||
*ngIf="slaveConfigFormGroup.get('port').hasError('required') && slaveConfigFormGroup.get('port').touched"
|
||||
class="tb-error">
|
||||
warning
|
||||
</mat-icon>
|
||||
@ -188,7 +185,7 @@
|
||||
<div class="tb-form-panel-title" translate>gateway.advanced-connection-settings</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="tb-form-panel no-border no-padding padding-top" [formGroup]="slaveConfigFormGroup">
|
||||
<div class="tb-form-panel no-border no-padding padding-top">
|
||||
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||
<div class="fixed-title-width" translate>gateway.byte-order</div>
|
||||
<div class="tb-flex no-gap">
|
||||
|
||||
@ -16,11 +16,6 @@
|
||||
$server-config-header-height: 132px;
|
||||
|
||||
:host {
|
||||
.nested-expansion-header {
|
||||
::ng-deep .mat-content {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.slave-content {
|
||||
height: calc(100% - #{$server-config-header-height});
|
||||
|
||||
@ -18,9 +18,9 @@ import { ChangeDetectionStrategy, Component, forwardRef, OnDestroy } from '@angu
|
||||
import {
|
||||
ControlValueAccessor,
|
||||
FormBuilder,
|
||||
FormControl,
|
||||
NG_VALIDATORS,
|
||||
NG_VALUE_ACCESSOR,
|
||||
UntypedFormControl,
|
||||
UntypedFormGroup,
|
||||
ValidationErrors,
|
||||
Validator,
|
||||
@ -78,7 +78,7 @@ import { isEqual } from '@core/utils';
|
||||
export class ModbusSlaveConfigComponent implements ControlValueAccessor, Validator, OnDestroy {
|
||||
|
||||
slaveConfigFormGroup: UntypedFormGroup;
|
||||
showSecurityControl: UntypedFormControl;
|
||||
showSecurityControl: FormControl<boolean>;
|
||||
ModbusProtocolLabelsMap = ModbusProtocolLabelsMap;
|
||||
ModbusMethodLabelsMap = ModbusMethodLabelsMap;
|
||||
portLimits = PortLimits;
|
||||
@ -89,8 +89,9 @@ export class ModbusSlaveConfigComponent implements ControlValueAccessor, Validat
|
||||
readonly modbusOrderType = Object.values(ModbusOrderType);
|
||||
readonly ModbusProtocolType = ModbusProtocolType;
|
||||
readonly modbusBaudrates = ModbusBaudrates;
|
||||
readonly serialSpecificControlKeys = ['serialPort', 'baudrate'];
|
||||
readonly tcpUdpSpecificControlKeys = ['port', 'security', 'host'];
|
||||
|
||||
private readonly serialSpecificControlKeys = ['serialPort', 'baudrate'];
|
||||
private readonly tcpUdpSpecificControlKeys = ['port', 'security', 'host'];
|
||||
|
||||
private onChange: (value: SlaveConfig) => void;
|
||||
private onTouched: () => void;
|
||||
@ -100,18 +101,18 @@ export class ModbusSlaveConfigComponent implements ControlValueAccessor, Validat
|
||||
constructor(private fb: FormBuilder) {
|
||||
this.showSecurityControl = this.fb.control(false);
|
||||
this.slaveConfigFormGroup = this.fb.group({
|
||||
type: [ModbusProtocolType.TCP, []],
|
||||
type: [ModbusProtocolType.TCP],
|
||||
host: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
port: [null, [Validators.required, Validators.min(PortLimits.MIN), Validators.max(PortLimits.MAX)]],
|
||||
serialPort: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
method: [ModbusMethodType.RTU, []],
|
||||
method: [ModbusMethodType.SOCKET],
|
||||
unitId: [0, [Validators.required]],
|
||||
baudrate: [this.ModbusProtocolType[0], []],
|
||||
baudrate: [this.modbusBaudrates[0]],
|
||||
deviceName: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
deviceType: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
pollPeriod: [5000, []],
|
||||
sendDataToThingsBoard: [false, []],
|
||||
byteOrder:[ModbusOrderType.BIG, []],
|
||||
pollPeriod: [5000],
|
||||
sendDataToThingsBoard: [false],
|
||||
byteOrder:[ModbusOrderType.BIG],
|
||||
security: [],
|
||||
identity: this.fb.group({
|
||||
vendorName: ['', [Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
@ -123,22 +124,16 @@ export class ModbusSlaveConfigComponent implements ControlValueAccessor, Validat
|
||||
values: [],
|
||||
});
|
||||
|
||||
this.slaveConfigFormGroup.valueChanges.pipe(
|
||||
takeUntil(this.destroy$)
|
||||
).subscribe((value: SlaveConfig) => {
|
||||
if (value.type === ModbusProtocolType.Serial) {
|
||||
value.port = value.serialPort;
|
||||
delete value.serialPort;
|
||||
}
|
||||
this.onChange(value);
|
||||
this.onTouched();
|
||||
});
|
||||
|
||||
this.observeValueChanges();
|
||||
this.observeTypeChange();
|
||||
this.observeFormEnable();
|
||||
this.observeShowSecurity();
|
||||
}
|
||||
|
||||
get isSlaveEnabled(): boolean {
|
||||
return this.slaveConfigFormGroup.get('sendDataToThingsBoard').value;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
@ -154,7 +149,7 @@ export class ModbusSlaveConfigComponent implements ControlValueAccessor, Validat
|
||||
|
||||
validate(): ValidationErrors | null {
|
||||
return this.slaveConfigFormGroup.valid ? null : {
|
||||
serverConfigFormGroup: { valid: false }
|
||||
slaveConfigFormGroup: { valid: false }
|
||||
};
|
||||
}
|
||||
|
||||
@ -164,18 +159,29 @@ export class ModbusSlaveConfigComponent implements ControlValueAccessor, Validat
|
||||
this.updateFormEnableState(slaveConfig.sendDataToThingsBoard);
|
||||
}
|
||||
|
||||
private observeTypeChange(): void {
|
||||
this.slaveConfigFormGroup.get('type').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(type => {
|
||||
this.updateFormEnableState(this.slaveConfigFormGroup.get('sendDataToThingsBoard').value);
|
||||
private observeValueChanges(): void {
|
||||
this.slaveConfigFormGroup.valueChanges.pipe(
|
||||
takeUntil(this.destroy$)
|
||||
).subscribe((value: SlaveConfig) => {
|
||||
if (value.type === ModbusProtocolType.Serial) {
|
||||
value.port = value.serialPort;
|
||||
delete value.serialPort;
|
||||
}
|
||||
this.onChange(value);
|
||||
this.onTouched();
|
||||
});
|
||||
}
|
||||
|
||||
private observeTypeChange(): void {
|
||||
this.slaveConfigFormGroup.get('type').valueChanges
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(() => this.updateFormEnableState(this.isSlaveEnabled));
|
||||
}
|
||||
|
||||
private observeFormEnable(): void {
|
||||
this.slaveConfigFormGroup.get('sendDataToThingsBoard').valueChanges
|
||||
.pipe(startWith(this.slaveConfigFormGroup.get('sendDataToThingsBoard').value), takeUntil(this.destroy$))
|
||||
.subscribe(value => {
|
||||
this.updateFormEnableState(value);
|
||||
});
|
||||
.pipe(startWith(this.isSlaveEnabled), takeUntil(this.destroy$))
|
||||
.subscribe(value => this.updateFormEnableState(value));
|
||||
}
|
||||
|
||||
private updateFormEnableState(enabled: boolean): void {
|
||||
@ -192,11 +198,13 @@ export class ModbusSlaveConfigComponent implements ControlValueAccessor, Validat
|
||||
}
|
||||
|
||||
private observeShowSecurity(): void {
|
||||
this.showSecurityControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => this.updateSecurityEnable(value));
|
||||
this.showSecurityControl.valueChanges
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(value => this.updateSecurityEnable(value));
|
||||
}
|
||||
|
||||
private updateSecurityEnable(isEnabled: boolean): void {
|
||||
if (isEnabled && this.slaveConfigFormGroup.get('sendDataToThingsBoard').value) {
|
||||
private updateSecurityEnable(securityEnabled: boolean): void {
|
||||
if (securityEnabled && this.isSlaveEnabled) {
|
||||
this.slaveConfigFormGroup.get('security').enable({emitEvent: false});
|
||||
} else {
|
||||
this.slaveConfigFormGroup.get('security').disable({emitEvent: false});
|
||||
@ -204,63 +212,58 @@ export class ModbusSlaveConfigComponent implements ControlValueAccessor, Validat
|
||||
}
|
||||
|
||||
private updateEnablingByProtocol(type: ModbusProtocolType): void {
|
||||
if (type === ModbusProtocolType.Serial) {
|
||||
if (this.slaveConfigFormGroup.get('sendDataToThingsBoard').value) {
|
||||
this.serialSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({emitEvent: false}));
|
||||
}
|
||||
this.tcpUdpSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({emitEvent: false}));
|
||||
} else {
|
||||
this.serialSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({emitEvent: false}));
|
||||
if (this.slaveConfigFormGroup.get('sendDataToThingsBoard').value) {
|
||||
this.tcpUdpSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({emitEvent: false}));
|
||||
}
|
||||
const enableKeys = type === ModbusProtocolType.Serial ? this.serialSpecificControlKeys : this.tcpUdpSpecificControlKeys;
|
||||
const disableKeys = type === ModbusProtocolType.Serial ? this.tcpUdpSpecificControlKeys : this.serialSpecificControlKeys;
|
||||
|
||||
if (this.isSlaveEnabled) {
|
||||
enableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({ emitEvent: false }));
|
||||
}
|
||||
};
|
||||
|
||||
disableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({ emitEvent: false }));
|
||||
}
|
||||
|
||||
private updateSlaveConfig(slaveConfig: ModbusSlave): void {
|
||||
const {
|
||||
type,
|
||||
method,
|
||||
unitId,
|
||||
deviceName,
|
||||
deviceType,
|
||||
pollPeriod,
|
||||
sendDataToThingsBoard,
|
||||
byteOrder,
|
||||
security,
|
||||
identity,
|
||||
values,
|
||||
baudrate,
|
||||
host,
|
||||
port,
|
||||
} = slaveConfig;
|
||||
let slaveState: ModbusSlave = {
|
||||
host: host ?? '',
|
||||
type: type ?? ModbusProtocolType.TCP,
|
||||
method: method ?? ModbusMethodType.RTU,
|
||||
unitId: unitId ?? 0,
|
||||
deviceName: deviceName ?? '',
|
||||
deviceType: deviceType ?? '',
|
||||
pollPeriod: pollPeriod ?? 5000,
|
||||
sendDataToThingsBoard: !!sendDataToThingsBoard,
|
||||
byteOrder: byteOrder ?? ModbusOrderType.BIG,
|
||||
security: security ?? {},
|
||||
identity: identity ?? {
|
||||
type = ModbusProtocolType.TCP,
|
||||
method = ModbusMethodType.RTU,
|
||||
unitId = 0,
|
||||
deviceName = '',
|
||||
deviceType = '',
|
||||
pollPeriod = 5000,
|
||||
sendDataToThingsBoard = false,
|
||||
byteOrder = ModbusOrderType.BIG,
|
||||
security = {},
|
||||
identity = {
|
||||
vendorName: '',
|
||||
productCode: '',
|
||||
vendorUrl: '',
|
||||
productName: '',
|
||||
modelName: '',
|
||||
},
|
||||
values: values ?? {} as ModbusRegisterValues,
|
||||
port: port ?? null,
|
||||
baudrate: baudrate ?? this.modbusBaudrates[0],
|
||||
values = {} as ModbusRegisterValues,
|
||||
baudrate = this.modbusBaudrates[0],
|
||||
host = '',
|
||||
port = null,
|
||||
} = slaveConfig;
|
||||
|
||||
const slaveState: ModbusSlave = {
|
||||
type,
|
||||
method,
|
||||
unitId,
|
||||
deviceName,
|
||||
deviceType,
|
||||
pollPeriod,
|
||||
sendDataToThingsBoard: !!sendDataToThingsBoard,
|
||||
byteOrder,
|
||||
security,
|
||||
identity,
|
||||
values,
|
||||
baudrate,
|
||||
host: type === ModbusProtocolType.Serial ? '' : host,
|
||||
port: type === ModbusProtocolType.Serial ? null : port,
|
||||
serialPort: (type === ModbusProtocolType.Serial ? port : '') as string,
|
||||
};
|
||||
if (slaveConfig.type === ModbusProtocolType.Serial) {
|
||||
slaveState = { ...slaveState, serialPort: port, host: '', port: null } as ModbusSlave;
|
||||
} else {
|
||||
slaveState = { ...slaveState, serialPort: '' } as ModbusSlave;
|
||||
}
|
||||
this.slaveConfigFormGroup.setValue(slaveState, {emitEvent: false});
|
||||
|
||||
this.slaveConfigFormGroup.setValue(slaveState, { emitEvent: false });
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,10 +105,8 @@
|
||||
<mat-icon matSuffix
|
||||
matTooltipPosition="above"
|
||||
matTooltipClass="tb-error-tooltip"
|
||||
[matTooltip]="slaveConfigFormGroup.get('serialPort') | getGatewayPortTooltip"
|
||||
*ngIf="(slaveConfigFormGroup.get('serialPort').hasError('required') ||
|
||||
slaveConfigFormGroup.get('serialPort').hasError('min') ||
|
||||
slaveConfigFormGroup.get('serialPort').hasError('max')) &&
|
||||
[matTooltip]="'gateway.port-required' | translate"
|
||||
*ngIf="slaveConfigFormGroup.get('serialPort').hasError('required') &&
|
||||
slaveConfigFormGroup.get('serialPort').touched"
|
||||
class="tb-error">
|
||||
warning
|
||||
|
||||
@ -17,14 +17,13 @@
|
||||
import { ChangeDetectionStrategy, Component, forwardRef, Inject, OnDestroy } from '@angular/core';
|
||||
import {
|
||||
FormBuilder,
|
||||
FormControl,
|
||||
NG_VALIDATORS,
|
||||
NG_VALUE_ACCESSOR,
|
||||
UntypedFormControl,
|
||||
UntypedFormGroup,
|
||||
Validators,
|
||||
} from '@angular/forms';
|
||||
import {
|
||||
MappingInfo,
|
||||
ModbusBaudrates,
|
||||
ModbusByteSizes,
|
||||
ModbusMethodLabelsMap,
|
||||
@ -35,6 +34,7 @@ import {
|
||||
ModbusProtocolLabelsMap,
|
||||
ModbusProtocolType,
|
||||
ModbusSerialMethodType,
|
||||
ModbusSlaveInfo,
|
||||
noLeadTrailSpacesRegex,
|
||||
PortLimits,
|
||||
SlaveConfig,
|
||||
@ -80,15 +80,15 @@ import { isEqual } from '@core/utils';
|
||||
styles: [`
|
||||
:host {
|
||||
.slaves-config-container {
|
||||
width: 900px;
|
||||
}
|
||||
width: 80vw;
|
||||
max-width: 900px; }
|
||||
}
|
||||
`],
|
||||
})
|
||||
export class ModbusSlaveDialogComponent extends DialogComponent<ModbusSlaveDialogComponent, SlaveConfig> implements OnDestroy {
|
||||
|
||||
slaveConfigFormGroup: UntypedFormGroup;
|
||||
showSecurityControl: UntypedFormControl;
|
||||
showSecurityControl: FormControl<boolean>;
|
||||
portLimits = PortLimits;
|
||||
|
||||
readonly modbusProtocolTypes = Object.values(ModbusProtocolType);
|
||||
@ -104,8 +104,9 @@ export class ModbusSlaveDialogComponent extends DialogComponent<ModbusSlaveDialo
|
||||
readonly ModbusMethodLabelsMap = ModbusMethodLabelsMap;
|
||||
readonly modbusHelpLink =
|
||||
'https://thingsboard.io/docs/iot-gateway/config/modbus/#section-master-description-and-configuration-parameters';
|
||||
readonly serialSpecificControlKeys = ['serialPort', 'baudrate', 'stopbits', 'bytesize', 'parity', 'strict'];
|
||||
readonly tcpUdpSpecificControlKeys = ['port', 'security', 'host', 'wordOrder'];
|
||||
|
||||
private readonly serialSpecificControlKeys = ['serialPort', 'baudrate', 'stopbits', 'bytesize', 'parity', 'strict'];
|
||||
private readonly tcpUdpSpecificControlKeys = ['port', 'security', 'host', 'wordOrder'];
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
@ -113,7 +114,7 @@ export class ModbusSlaveDialogComponent extends DialogComponent<ModbusSlaveDialo
|
||||
private fb: FormBuilder,
|
||||
protected store: Store<AppState>,
|
||||
protected router: Router,
|
||||
@Inject(MAT_DIALOG_DATA) public data: MappingInfo,
|
||||
@Inject(MAT_DIALOG_DATA) public data: ModbusSlaveInfo,
|
||||
public dialogRef: MatDialogRef<ModbusSlaveDialogComponent, SlaveConfig>,
|
||||
) {
|
||||
super(store, router, dialogRef);
|
||||
@ -121,42 +122,38 @@ export class ModbusSlaveDialogComponent extends DialogComponent<ModbusSlaveDialo
|
||||
this.showSecurityControl = this.fb.control(false);
|
||||
this.slaveConfigFormGroup = this.fb.group({
|
||||
name: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
type: [ModbusProtocolType.TCP, [Validators.required]],
|
||||
type: [ModbusProtocolType.TCP],
|
||||
host: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
port: [null, [Validators.required, Validators.min(PortLimits.MIN), Validators.max(PortLimits.MAX)]],
|
||||
serialPort: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
method: [ModbusMethodType.RTU, []],
|
||||
baudrate: [this.modbusBaudrates[0], []],
|
||||
stopbits: [null, []],
|
||||
bytesize: [ModbusByteSizes[0], []],
|
||||
parity: [ModbusParity.None, []],
|
||||
strict: [false, []],
|
||||
method: [ModbusMethodType.SOCKET, [Validators.required]],
|
||||
baudrate: [this.modbusBaudrates[0]],
|
||||
stopbits: [1],
|
||||
bytesize: [ModbusByteSizes[0]],
|
||||
parity: [ModbusParity.None],
|
||||
strict: [true],
|
||||
unitId: [0, [Validators.required]],
|
||||
deviceName: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
deviceType: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
sendDataOnlyOnChange: [false, []],
|
||||
sendDataOnlyOnChange: [false],
|
||||
timeout: [35],
|
||||
byteOrder: [ModbusOrderType.BIG, []],
|
||||
wordOrder: [ModbusOrderType.BIG, []],
|
||||
retries: [true, []],
|
||||
retryOnEmpty: [true, []],
|
||||
retryOnInvalid: [true, []],
|
||||
pollPeriod: [5000, []],
|
||||
connectAttemptTimeMs: [5000, []],
|
||||
connectAttemptCount: [5, []],
|
||||
waitAfterFailedAttemptsMs: [300000, []],
|
||||
values: [{}, []],
|
||||
byteOrder: [ModbusOrderType.BIG],
|
||||
wordOrder: [ModbusOrderType.BIG],
|
||||
retries: [true],
|
||||
retryOnEmpty: [true],
|
||||
retryOnInvalid: [true],
|
||||
pollPeriod: [5000],
|
||||
connectAttemptTimeMs: [5000],
|
||||
connectAttemptCount: [5],
|
||||
waitAfterFailedAttemptsMs: [300000],
|
||||
values: [{}],
|
||||
security: [{}],
|
||||
});
|
||||
|
||||
this.slaveConfigFormGroup.patchValue({
|
||||
...this.data.value,
|
||||
port: this.data.value.type === ModbusProtocolType.Serial
|
||||
? null
|
||||
: this.data.value.port,
|
||||
serialPort: this.data.value.type === ModbusProtocolType.Serial
|
||||
? this.data.value.port
|
||||
: '',
|
||||
port: this.data.value.type === ModbusProtocolType.Serial ? null : this.data.value.port,
|
||||
serialPort: this.data.value.type === ModbusProtocolType.Serial ? this.data.value.port : '',
|
||||
values: {
|
||||
attributes: this.data.value.attributes ?? [],
|
||||
timeseries: this.data.value.timeseries ?? [],
|
||||
@ -180,40 +177,49 @@ export class ModbusSlaveDialogComponent extends DialogComponent<ModbusSlaveDialo
|
||||
}
|
||||
|
||||
add(): void {
|
||||
if (this.slaveConfigFormGroup.valid) {
|
||||
const slaveResult = {...this.slaveConfigFormGroup.value, ...this.slaveConfigFormGroup.value.values};
|
||||
delete slaveResult.values;
|
||||
if (slaveResult.type === ModbusProtocolType.Serial) {
|
||||
slaveResult.port = slaveResult.serialPort;
|
||||
}
|
||||
delete slaveResult.serialPort;
|
||||
this.dialogRef.close(slaveResult);
|
||||
if (!this.slaveConfigFormGroup.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { values, type, serialPort, ...rest } = this.slaveConfigFormGroup.value;
|
||||
const slaveResult = { ...rest, ...values };
|
||||
|
||||
if (type === ModbusProtocolType.Serial) {
|
||||
slaveResult.port = serialPort;
|
||||
}
|
||||
|
||||
this.dialogRef.close(slaveResult);
|
||||
}
|
||||
|
||||
|
||||
private observeTypeChange(): void {
|
||||
this.slaveConfigFormGroup.get('type').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(type => {
|
||||
this.updateControlsEnabling(type);
|
||||
});
|
||||
this.slaveConfigFormGroup.get('type').valueChanges
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(type => this.updateControlsEnabling(type));
|
||||
}
|
||||
|
||||
private updateControlsEnabling(type: ModbusProtocolType): void {
|
||||
if (type === ModbusProtocolType.Serial) {
|
||||
this.serialSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({emitEvent: false}));
|
||||
this.tcpUdpSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({emitEvent: false}));
|
||||
} else {
|
||||
this.serialSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({emitEvent: false}));
|
||||
this.tcpUdpSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({emitEvent: false}));
|
||||
}
|
||||
};
|
||||
const [enableKeys, disableKeys] = type === ModbusProtocolType.Serial
|
||||
? [this.serialSpecificControlKeys, this.tcpUdpSpecificControlKeys]
|
||||
: [this.tcpUdpSpecificControlKeys, this.serialSpecificControlKeys];
|
||||
|
||||
enableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({ emitEvent: false }));
|
||||
disableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({ emitEvent: false }));
|
||||
|
||||
this.updateSecurityEnabling(this.showSecurityControl.value);
|
||||
}
|
||||
|
||||
private observeShowSecurity(): void {
|
||||
this.showSecurityControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => {
|
||||
if (value) {
|
||||
this.slaveConfigFormGroup.get('security').enable({emitEvent: false});
|
||||
} else {
|
||||
this.slaveConfigFormGroup.get('security').disable({emitEvent: false});
|
||||
}
|
||||
});
|
||||
this.showSecurityControl.valueChanges
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(value => this.updateSecurityEnabling(value));
|
||||
}
|
||||
|
||||
private updateSecurityEnabling(isEnabled: boolean): void {
|
||||
if (isEnabled) {
|
||||
this.slaveConfigFormGroup.get('security').enable({emitEvent: false});
|
||||
} else {
|
||||
this.slaveConfigFormGroup.get('security').disable({emitEvent: false});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ import {
|
||||
ModbusRegisterTranslationsMap,
|
||||
ModbusRegisterType,
|
||||
ModbusRegisterValues,
|
||||
ModbusValue,
|
||||
ModbusValueKey,
|
||||
ModbusValues,
|
||||
ModbusValuesState,
|
||||
@ -103,7 +104,7 @@ export class ModbusValuesComponent implements ControlValueAccessor, Validator, O
|
||||
ModbusValueKey = ModbusValueKey;
|
||||
valuesFormGroup: FormGroup;
|
||||
|
||||
private onChange: (value: string) => void;
|
||||
private onChange: (value: ModbusValuesState) => void;
|
||||
private onTouched: () => void;
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
@ -125,7 +126,7 @@ export class ModbusValuesComponent implements ControlValueAccessor, Validator, O
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: string) => void): void {
|
||||
registerOnChange(fn: (value: ModbusValuesState) => void): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
@ -135,15 +136,15 @@ export class ModbusValuesComponent implements ControlValueAccessor, Validator, O
|
||||
|
||||
writeValue(values: ModbusValuesState): void {
|
||||
if (this.singleMode) {
|
||||
this.valuesFormGroup.setValue(this.getSingleRegisterState(values as ModbusValues), {emitEvent: false});
|
||||
this.valuesFormGroup.setValue(this.getSingleRegisterState(values as ModbusValues), { emitEvent: false });
|
||||
} else {
|
||||
const registers = values as ModbusRegisterValues;
|
||||
const { holding_registers, coils_initializer, input_registers, discrete_inputs } = values as ModbusRegisterValues;
|
||||
this.valuesFormGroup.setValue({
|
||||
holding_registers: this.getSingleRegisterState(registers.holding_registers),
|
||||
coils_initializer: this.getSingleRegisterState(registers.coils_initializer),
|
||||
input_registers: this.getSingleRegisterState(registers.input_registers),
|
||||
discrete_inputs: this.getSingleRegisterState(registers.discrete_inputs),
|
||||
}, {emitEvent: false});
|
||||
holding_registers: this.getSingleRegisterState(holding_registers),
|
||||
coils_initializer: this.getSingleRegisterState(coils_initializer),
|
||||
input_registers: this.getSingleRegisterState(input_registers),
|
||||
discrete_inputs: this.getSingleRegisterState(discrete_inputs),
|
||||
}, { emitEvent: false });
|
||||
}
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
@ -159,69 +160,63 @@ export class ModbusValuesComponent implements ControlValueAccessor, Validator, O
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
getValueGroup(valueKey: ModbusValueKey, register?: ModbusRegisterType) {
|
||||
getValueGroup(valueKey: ModbusValueKey, register?: ModbusRegisterType): FormGroup {
|
||||
return register ? this.valuesFormGroup.get(register).get(valueKey).value : this.valuesFormGroup.get(valueKey).value;
|
||||
}
|
||||
|
||||
manageKeys($event: Event, matButton: MatButton, keysType: ModbusValueKey, register?: ModbusRegisterType): void {
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
$event.stopPropagation();
|
||||
const trigger = matButton._elementRef.nativeElement;
|
||||
if (this.popoverService.hasPopover(trigger)) {
|
||||
this.popoverService.hidePopover(trigger);
|
||||
} else {
|
||||
const group = this.valuesFormGroup;
|
||||
|
||||
const keysControl = register ? group.get(register).get(keysType) : group.get(keysType);
|
||||
const ctx = {
|
||||
values: keysControl.value,
|
||||
isMaster: !this.singleMode,
|
||||
keysType,
|
||||
panelTitle: ModbusKeysPanelTitleTranslationsMap.get(keysType),
|
||||
addKeyTitle: ModbusKeysAddKeyTranslationsMap.get(keysType),
|
||||
deleteKeyTitle: ModbusKeysDeleteKeyTranslationsMap.get(keysType),
|
||||
noKeysText: ModbusKeysNoKeysTextTranslationsMap.get(keysType)
|
||||
};
|
||||
const dataKeysPanelPopover = this.popoverService.displayPopover(
|
||||
trigger,
|
||||
this.renderer,
|
||||
this.viewContainerRef,
|
||||
ModbusDataKeysPanelComponent,
|
||||
'leftBottom',
|
||||
false,
|
||||
null,
|
||||
ctx,
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
true
|
||||
);
|
||||
dataKeysPanelPopover.tbComponentRef.instance.popover = dataKeysPanelPopover;
|
||||
dataKeysPanelPopover.tbComponentRef.instance.keysDataApplied.pipe(takeUntil(this.destroy$)).subscribe((keysData) => {
|
||||
dataKeysPanelPopover.hide();
|
||||
keysControl.patchValue(keysData);
|
||||
keysControl.markAsDirty();
|
||||
this.cdr.markForCheck();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const keysControl = this.getValueGroup(keysType, register);
|
||||
const ctx = {
|
||||
values: keysControl.value,
|
||||
isMaster: !this.singleMode,
|
||||
keysType,
|
||||
panelTitle: ModbusKeysPanelTitleTranslationsMap.get(keysType),
|
||||
addKeyTitle: ModbusKeysAddKeyTranslationsMap.get(keysType),
|
||||
deleteKeyTitle: ModbusKeysDeleteKeyTranslationsMap.get(keysType),
|
||||
noKeysText: ModbusKeysNoKeysTextTranslationsMap.get(keysType)
|
||||
};
|
||||
const dataKeysPanelPopover = this.popoverService.displayPopover(
|
||||
trigger,
|
||||
this.renderer,
|
||||
this.viewContainerRef,
|
||||
ModbusDataKeysPanelComponent,
|
||||
'leftBottom',
|
||||
false,
|
||||
null,
|
||||
ctx,
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
true
|
||||
);
|
||||
dataKeysPanelPopover.tbComponentRef.instance.popover = dataKeysPanelPopover;
|
||||
dataKeysPanelPopover.tbComponentRef.instance.keysDataApplied.pipe(takeUntil(this.destroy$)).subscribe((keysData: ModbusValue[]) => {
|
||||
dataKeysPanelPopover.hide();
|
||||
keysControl.patchValue(keysData);
|
||||
keysControl.markAsDirty();
|
||||
this.cdr.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
private initializeValuesFormGroup(): void {
|
||||
const getValuesFormGroup = () => this.fb.group(this.modbusValueKeys.reduce((acc, key) => {
|
||||
acc[key] = this.fb.control([[], []]);
|
||||
return acc;
|
||||
}, {}));
|
||||
|
||||
if (this.singleMode) {
|
||||
this.valuesFormGroup = this.fb.group(this.modbusValueKeys.reduce((acc, key) => {
|
||||
acc[key] = this.fb.control([[], []]);
|
||||
return acc;
|
||||
}, {}));
|
||||
this.valuesFormGroup = getValuesFormGroup();
|
||||
} else {
|
||||
this.valuesFormGroup = this.fb.group(
|
||||
this.modbusRegisterTypes.reduce((registersAcc, register) => {
|
||||
|
||||
registersAcc[register] = this.fb.group(this.modbusValueKeys.reduce((acc, key) => {
|
||||
acc[key] = this.fb.control([[], []]);
|
||||
return acc;
|
||||
}, {}));
|
||||
|
||||
registersAcc[register] = getValuesFormGroup();
|
||||
return registersAcc;
|
||||
}, {})
|
||||
);
|
||||
|
||||
@ -485,6 +485,11 @@ export interface MappingInfo {
|
||||
buttonTitle: string;
|
||||
}
|
||||
|
||||
export interface ModbusSlaveInfo {
|
||||
value: SlaveConfig;
|
||||
buttonTitle: string;
|
||||
}
|
||||
|
||||
export enum ConnectorConfigurationModes {
|
||||
BASIC = 'basic',
|
||||
ADVANCED = 'advanced'
|
||||
@ -852,24 +857,6 @@ export enum ModbusObjectCountByDataType {
|
||||
'64float' = 4,
|
||||
}
|
||||
|
||||
export enum ModbusValueField {
|
||||
Tag = 'tag',
|
||||
Type = 'type',
|
||||
ObjectsCount = 'objectsCount',
|
||||
Address = 'address',
|
||||
Value = 'value',
|
||||
}
|
||||
|
||||
export const ModbusFieldsTranslationsMap = new Map<ModbusValueField, string>(
|
||||
[
|
||||
[ModbusValueField.Tag, 'gateway.tag'],
|
||||
[ModbusValueField.Type, 'gateway.type'],
|
||||
[ModbusValueField.ObjectsCount, 'gateway.objects_count'],
|
||||
[ModbusValueField.Address, 'gateway.address'],
|
||||
[ModbusValueField.Value, 'gateway.value']
|
||||
]
|
||||
);
|
||||
|
||||
export enum ModbusValueKey {
|
||||
ATTRIBUTES = 'attributes',
|
||||
TIMESERIES = 'timeseries',
|
||||
@ -946,7 +933,7 @@ export interface SlaveConfig {
|
||||
pollPeriod: number;
|
||||
unitId: number;
|
||||
deviceName: string;
|
||||
deviceType?: string;
|
||||
deviceType: string;
|
||||
sendDataOnlyOnChange: boolean;
|
||||
connectAttemptTimeMs: number;
|
||||
connectAttemptCount: number;
|
||||
@ -959,7 +946,7 @@ export interface SlaveConfig {
|
||||
baudrate?: number;
|
||||
stopbits?: number;
|
||||
bytesize?: number;
|
||||
parity?: string;
|
||||
parity?: ModbusParity;
|
||||
strict?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@ -208,25 +208,26 @@ import {
|
||||
LabelCardWidgetComponent,
|
||||
LabelValueCardWidgetComponent,
|
||||
UnreadNotificationWidgetComponent,
|
||||
NotificationTypeFilterPanelComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
RpcWidgetsModule,
|
||||
HomePageWidgetsModule,
|
||||
SharedHomeComponentsModule,
|
||||
RestConnectorSecurityComponent,
|
||||
GatewayHelpLinkPipe,
|
||||
BrokerConfigControlComponent,
|
||||
WorkersConfigControlComponent,
|
||||
OpcServerConfigComponent,
|
||||
MqttBasicConfigComponent,
|
||||
MappingTableComponent,
|
||||
OpcUaBasicConfigComponent,
|
||||
KeyValueIsNotEmptyPipe,
|
||||
ModbusBasicConfigComponent,
|
||||
EllipsisChipListDirective,
|
||||
],
|
||||
NotificationTypeFilterPanelComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
RpcWidgetsModule,
|
||||
HomePageWidgetsModule,
|
||||
SharedHomeComponentsModule,
|
||||
RestConnectorSecurityComponent,
|
||||
GatewayHelpLinkPipe,
|
||||
BrokerConfigControlComponent,
|
||||
WorkersConfigControlComponent,
|
||||
OpcServerConfigComponent,
|
||||
MqttBasicConfigComponent,
|
||||
MappingTableComponent,
|
||||
OpcUaBasicConfigComponent,
|
||||
KeyValueIsNotEmptyPipe,
|
||||
ModbusBasicConfigComponent,
|
||||
EllipsisChipListDirective,
|
||||
],
|
||||
exports: [
|
||||
EntitiesTableWidgetComponent,
|
||||
AlarmsTableWidgetComponent,
|
||||
|
||||
@ -30,12 +30,12 @@ export class GatewayPortTooltipPipe implements PipeTransform {
|
||||
transform(portControl: AbstractControl): string {
|
||||
if (portControl.hasError('required')) {
|
||||
return this.translate.instant('gateway.port-required');
|
||||
} else if (
|
||||
portControl.hasError('min') ||
|
||||
portControl.hasError('max')
|
||||
) {
|
||||
return this.translate.instant('gateway.port-limits-error',
|
||||
{min: PortLimits.MIN, max: PortLimits.MAX});
|
||||
}
|
||||
if (portControl.hasError('min') || portControl.hasError('max')) {
|
||||
return this.translate.instant('gateway.port-limits-error', {
|
||||
min: PortLimits.MIN,
|
||||
max: PortLimits.MAX,
|
||||
});
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -61,8 +61,11 @@ export class EllipsisChipListDirective implements OnDestroy {
|
||||
).subscribe(() => {
|
||||
this.adjustChips();
|
||||
});
|
||||
this.observeIntersection();
|
||||
}
|
||||
|
||||
this.intersectionObserver = new IntersectionObserver((entries) => {
|
||||
private observeIntersection(): void {
|
||||
this.intersectionObserver = new IntersectionObserver(entries => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
this.adjustChips();
|
||||
|
||||
@ -2756,6 +2756,7 @@
|
||||
},
|
||||
"gateway": {
|
||||
"address": "Address",
|
||||
"address-required": "Address required",
|
||||
"add-entry": "Add configuration",
|
||||
"add-attribute": "Add attribute",
|
||||
"add-attribute-update": "Add attribute update",
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
"pollPeriod": 5000,
|
||||
"unitId": 1,
|
||||
"deviceName": "Temp Sensor",
|
||||
"deviceType": "default",
|
||||
"sendDataOnlyOnChange": true,
|
||||
"connectAttemptTimeMs": 5000,
|
||||
"connectAttemptCount": 5,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user