Added new functionality for enabling server tab

This commit is contained in:
mpetrov 2024-07-22 16:12:37 +03:00
parent 62e2c8a6ad
commit 0ae9af06c9
10 changed files with 103 additions and 25 deletions

View File

@ -19,10 +19,10 @@
<mat-tab label="{{ 'gateway.general' | translate }}"> <mat-tab label="{{ 'gateway.general' | translate }}">
<ng-container [ngTemplateOutlet]="generalTabContent"></ng-container> <ng-container [ngTemplateOutlet]="generalTabContent"></ng-container>
</mat-tab> </mat-tab>
<mat-tab label="{{ 'gateway.master-connections' | translate }}"> <mat-tab label="{{ 'gateway.master-connections' | translate }}*">
<tb-modbus-master-table formControlName="master"></tb-modbus-master-table> <tb-modbus-master-table formControlName="master"></tb-modbus-master-table>
</mat-tab> </mat-tab>
<mat-tab label="{{ 'gateway.server-config' | translate }}"> <mat-tab label="{{ 'gateway.server-config' | translate }}">
<tb-modbus-server-config formControlName="slave"></tb-modbus-server-config> <tb-modbus-slave-config formControlName="slave"></tb-modbus-slave-config>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>

View File

@ -67,6 +67,7 @@ export class ModbusDataKeysPanelComponent implements OnInit {
readonly ModbusFunctionCodeTranslationsMap = ModbusFunctionCodeTranslationsMap; readonly ModbusFunctionCodeTranslationsMap = ModbusFunctionCodeTranslationsMap;
readonly defaultReadFunctionCodes = [3, 4]; readonly defaultReadFunctionCodes = [3, 4];
readonly defaultWriteFunctionCodes = [5, 6, 15, 16]; readonly defaultWriteFunctionCodes = [5, 6, 15, 16];
readonly stringAttrUpdatesWriteFunctionCodes = [6, 16];
constructor(private fb: UntypedFormBuilder) {} constructor(private fb: UntypedFormBuilder) {}
@ -84,7 +85,7 @@ export class ModbusDataKeysPanelComponent implements OnInit {
const dataKeyFormGroup = this.fb.group({ const dataKeyFormGroup = this.fb.group({
tag: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], tag: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
value: [{value: '', disabled: !this.isMaster}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], value: [{value: '', disabled: !this.isMaster}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
type: [ModbusDataType.STRING, [Validators.required]], type: [ModbusDataType.BYTES, [Validators.required]],
address: [0, [Validators.required]], address: [0, [Validators.required]],
objectsCount: [1, [Validators.required]], objectsCount: [1, [Validators.required]],
functionCode: [this.getDefaultFunctionCodes()[0]], functionCode: [this.getDefaultFunctionCodes()[0]],
@ -146,6 +147,9 @@ export class ModbusDataKeysPanelComponent implements OnInit {
private getFunctionCodes(dataType: ModbusDataType): number[] { private getFunctionCodes(dataType: ModbusDataType): number[] {
if (this.keysType === ModbusValueKey.ATTRIBUTES_UPDATES) { if (this.keysType === ModbusValueKey.ATTRIBUTES_UPDATES) {
if (dataType === ModbusDataType.STRING) {
return this.stringAttrUpdatesWriteFunctionCodes;
}
return this.defaultWriteFunctionCodes; return this.defaultWriteFunctionCodes;
} }
const functionCodes = [...this.defaultReadFunctionCodes]; const functionCodes = [...this.defaultReadFunctionCodes];

View File

@ -61,6 +61,7 @@ import { takeUntil } from 'rxjs/operators';
export class ModbusSecurityConfigComponent implements ControlValueAccessor, Validator, OnChanges, OnDestroy { export class ModbusSecurityConfigComponent implements ControlValueAccessor, Validator, OnChanges, OnDestroy {
@Input() isMaster = false; @Input() isMaster = false;
@Input() disabled = false;
securityConfigFormGroup: UntypedFormGroup; securityConfigFormGroup: UntypedFormGroup;
@ -90,6 +91,11 @@ export class ModbusSecurityConfigComponent implements ControlValueAccessor, Vali
}); });
this.observeValueChanges(); this.observeValueChanges();
} }
if (this.disabled) {
this.securityConfigFormGroup.disable({emitEvent:false});
} else {
this.securityConfigFormGroup.enable({emitEvent:false});
}
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -106,8 +112,8 @@ export class ModbusSecurityConfigComponent implements ControlValueAccessor, Vali
} }
validate(): ValidationErrors | null { validate(): ValidationErrors | null {
return this.securityConfigFormGroup.valid ? null : { return this.securityConfigFormGroup.valid || this.disabled ? null : {
serverConfigFormGroup: { valid: false } securityConfigFormGroup: { valid: false }
}; };
} }

View File

@ -17,6 +17,13 @@
--> -->
<div class="tb-form-panel no-border no-padding padding-top" [formGroup]="slaveConfigFormGroup"> <div class="tb-form-panel no-border no-padding padding-top" [formGroup]="slaveConfigFormGroup">
<div class="tb-form-hint tb-primary-fill tb-flex center">{{ 'gateway.hints.modbus-server' | translate }}</div> <div class="tb-form-hint tb-primary-fill tb-flex center">{{ 'gateway.hints.modbus-server' | translate }}</div>
<div class="tb-form-row" fxLayoutAlign="space-between center">
<mat-slide-toggle class="mat-slide" formControlName="sendDataToThingsBoard">
<mat-label>
{{ 'gateway.enable' | translate }}
</mat-label>
</mat-slide-toggle>
</div>
<div class="tb-flex row space-between align-center no-gap fill-width"> <div class="tb-flex row space-between align-center no-gap fill-width">
<div class="fixed-title-width" translate>gateway.server-slave-config</div> <div class="fixed-title-width" translate>gateway.server-slave-config</div>
<tb-toggle-select formControlName="type" appearance="fill"> <tb-toggle-select formControlName="type" appearance="fill">
@ -168,13 +175,6 @@
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
<div class="tb-form-row" fxLayoutAlign="space-between center">
<mat-slide-toggle class="mat-slide" formControlName="sendDataToThingsBoard">
<mat-label>
{{ 'gateway.send-data-TB' | translate }}
</mat-label>
</mat-slide-toggle>
</div>
<mat-expansion-panel> <mat-expansion-panel>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> <mat-panel-title>
@ -203,7 +203,7 @@
</mat-slide-toggle> </mat-slide-toggle>
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<tb-modbus-security-config formControlName="security" [isMaster]="true"></tb-modbus-security-config> <tb-modbus-security-config formControlName="security" [disabled]="!slaveConfigFormGroup.get('sendDataToThingsBoard').value || !showSecurityControl.value" [isMaster]="true"></tb-modbus-security-config>
</mat-expansion-panel> </mat-expansion-panel>
<ng-container [formGroup]="slaveConfigFormGroup.get('identity')"> <ng-container [formGroup]="slaveConfigFormGroup.get('identity')">
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center"> <div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
@ -251,6 +251,6 @@
</mat-expansion-panel> </mat-expansion-panel>
<div class="tb-form-panel stroked"> <div class="tb-form-panel stroked">
<div class="tb-form-panel-title" translate>gateway.values</div> <div class="tb-form-panel-title" translate>gateway.values</div>
<tb-modbus-values formControlName="values"></tb-modbus-values> <tb-modbus-values formControlName="values" [disabled]="!slaveConfigFormGroup.get('sendDataToThingsBoard').value"></tb-modbus-values>
</div> </div>
</div> </div>

View File

@ -41,13 +41,13 @@ import {
import { SharedModule } from '@shared/shared.module'; import { SharedModule } from '@shared/shared.module';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { startWith, takeUntil } from 'rxjs/operators';
import { GatewayPortTooltipPipe } from '@home/pipes/public-api'; import { GatewayPortTooltipPipe } from '@home/pipes/public-api';
import { ModbusSecurityConfigComponent } from '../modbus-security-config/modbus-security-config.component'; import { ModbusSecurityConfigComponent } from '../modbus-security-config/modbus-security-config.component';
import { ModbusValuesComponent, } from '../modbus-values/modbus-values.component'; import { ModbusValuesComponent, } from '../modbus-values/modbus-values.component';
@Component({ @Component({
selector: 'tb-modbus-server-config', selector: 'tb-modbus-slave-config',
templateUrl: './modbus-slave-config.component.html', templateUrl: './modbus-slave-config.component.html',
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
providers: [ providers: [
@ -138,6 +138,7 @@ export class ModbusSlaveConfigComponent implements ControlValueAccessor, Validat
}); });
this.observeTypeChange(); this.observeTypeChange();
this.observeFormEnable();
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -162,23 +163,47 @@ export class ModbusSlaveConfigComponent implements ControlValueAccessor, Validat
writeValue(slaveConfig: ModbusSlave): void { writeValue(slaveConfig: ModbusSlave): void {
this.showSecurityControl.patchValue(!!slaveConfig.security); this.showSecurityControl.patchValue(!!slaveConfig.security);
this.updateSlaveConfig(slaveConfig); this.updateSlaveConfig(slaveConfig);
this.updateControlsEnabling(slaveConfig.type); this.updateFormEnableState(slaveConfig.sendDataToThingsBoard);
} }
private observeTypeChange(): void { private observeTypeChange(): void {
this.slaveConfigFormGroup.get('type').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(type => { this.slaveConfigFormGroup.get('type').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(type => {
this.updateControlsEnabling(type); this.updateFormEnableState(this.slaveConfigFormGroup.get('sendDataToThingsBoard').value);
}); });
} }
private updateControlsEnabling(type: ModbusProtocolType): void { private observeFormEnable(): void {
this.slaveConfigFormGroup.get('sendDataToThingsBoard').valueChanges
.pipe(startWith(this.slaveConfigFormGroup.get('sendDataToThingsBoard').value), takeUntil(this.destroy$))
.subscribe(value => {
this.updateFormEnableState(value);
});
}
private updateFormEnableState(enabled: boolean): void {
if (enabled) {
this.slaveConfigFormGroup.enable({emitEvent: false});
this.showSecurityControl.enable({emitEvent: false});
} else {
this.slaveConfigFormGroup.disable({emitEvent: false});
this.showSecurityControl.disable({emitEvent: false});
this.slaveConfigFormGroup.get('sendDataToThingsBoard').enable({emitEvent: false});
}
this.updateEnablingByProtocol(this.slaveConfigFormGroup.get('type').value);
}
private updateEnablingByProtocol(type: ModbusProtocolType): void {
if (type === ModbusProtocolType.Serial) { if (type === ModbusProtocolType.Serial) {
if (this.slaveConfigFormGroup.get('sendDataToThingsBoard').value) {
this.serialSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({emitEvent: false})); this.serialSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({emitEvent: false}));
}
this.tcpUdpSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({emitEvent: false})); this.tcpUdpSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({emitEvent: false}));
} else { } else {
this.serialSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({emitEvent: false})); 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})); this.tcpUdpSpecificControlKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({emitEvent: false}));
} }
}
}; };
private updateSlaveConfig(slaveConfig: ModbusSlave): void { private updateSlaveConfig(slaveConfig: ModbusSlave): void {

View File

@ -145,6 +145,12 @@ export class ModbusSlaveDialogComponent extends DialogComponent<ModbusSlaveDialo
this.slaveConfigFormGroup.patchValue({ this.slaveConfigFormGroup.patchValue({
...this.data.value, ...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
: '',
values: { values: {
attributes: this.data.value.attributes ?? [], attributes: this.data.value.attributes ?? [],
timeseries: this.data.value.timeseries ?? [], timeseries: this.data.value.timeseries ?? [],
@ -172,8 +178,8 @@ export class ModbusSlaveDialogComponent extends DialogComponent<ModbusSlaveDialo
delete slaveResult.values; delete slaveResult.values;
if (slaveResult.type === ModbusProtocolType.Serial) { if (slaveResult.type === ModbusProtocolType.Serial) {
slaveResult.port = slaveResult.serialPort; slaveResult.port = slaveResult.serialPort;
delete slaveResult.serialPort;
} }
delete slaveResult.serialPort;
this.dialogRef.close(slaveResult); this.dialogRef.close(slaveResult);
} }
} }

View File

@ -47,6 +47,7 @@
<button type="button" <button type="button"
mat-icon-button mat-icon-button
color="primary" color="primary"
[disabled]="disabled"
#attributesButton #attributesButton
(click)="manageKeys($event, attributesButton, ModbusValueKey.ATTRIBUTES, register)"> (click)="manageKeys($event, attributesButton, ModbusValueKey.ATTRIBUTES, register)">
<tb-icon matButtonIcon>edit</tb-icon> <tb-icon matButtonIcon>edit</tb-icon>
@ -67,6 +68,7 @@
<button type="button" <button type="button"
mat-icon-button mat-icon-button
color="primary" color="primary"
[disabled]="disabled"
#telemetryButton #telemetryButton
(click)="manageKeys($event, telemetryButton, ModbusValueKey.TIMESERIES, register)"> (click)="manageKeys($event, telemetryButton, ModbusValueKey.TIMESERIES, register)">
<tb-icon matButtonIcon>edit</tb-icon> <tb-icon matButtonIcon>edit</tb-icon>
@ -86,6 +88,7 @@
</mat-chip-listbox> </mat-chip-listbox>
<button type="button" <button type="button"
mat-icon-button mat-icon-button
[disabled]="disabled"
color="primary" color="primary"
#attributesUpdatesButton #attributesUpdatesButton
(click)="manageKeys($event, attributesUpdatesButton, ModbusValueKey.ATTRIBUTES_UPDATES, register)"> (click)="manageKeys($event, attributesUpdatesButton, ModbusValueKey.ATTRIBUTES_UPDATES, register)">
@ -107,6 +110,7 @@
<button type="button" <button type="button"
mat-icon-button mat-icon-button
color="primary" color="primary"
[disabled]="disabled"
#rpcRequestsButton #rpcRequestsButton
(click)="manageKeys($event, rpcRequestsButton, ModbusValueKey.RPC_REQUESTS, register)"> (click)="manageKeys($event, rpcRequestsButton, ModbusValueKey.RPC_REQUESTS, register)">
<tb-icon matButtonIcon>edit</tb-icon> <tb-icon matButtonIcon>edit</tb-icon>

View File

@ -43,6 +43,8 @@ import {
ModbusRegisterType, ModbusRegisterType,
ModbusRegisterValues, ModbusRegisterValues,
ModbusValueKey, ModbusValueKey,
ModbusValues,
ModbusValuesState,
} from '@home/components/widget/lib/gateway/gateway-widget.models'; } from '@home/components/widget/lib/gateway/gateway-widget.models';
import { SharedModule } from '@shared/shared.module'; import { SharedModule } from '@shared/shared.module';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
@ -87,6 +89,7 @@ import { ModbusDataKeysPanelComponent } from '../modbus-data-keys-panel/modbus-d
export class ModbusValuesComponent implements ControlValueAccessor, Validator, OnChanges, OnDestroy { export class ModbusValuesComponent implements ControlValueAccessor, Validator, OnChanges, OnDestroy {
@Input() singleMode = false; @Input() singleMode = false;
@Input() disabled = false;
modbusRegisterTypes: ModbusRegisterType[] = Object.values(ModbusRegisterType); modbusRegisterTypes: ModbusRegisterType[] = Object.values(ModbusRegisterType);
modbusValueKeys = Object.values(ModbusValueKey); modbusValueKeys = Object.values(ModbusValueKey);
@ -135,8 +138,19 @@ export class ModbusValuesComponent implements ControlValueAccessor, Validator, O
this.onTouched = fn; this.onTouched = fn;
} }
writeValue(values: ModbusRegisterValues): void { writeValue(values: ModbusValuesState): void {
this.valuesFormGroup.patchValue(values, {emitEvent: false}); if (this.singleMode) {
this.valuesFormGroup.setValue(this.getSingleRegisterState(values as ModbusValues), {emitEvent: false});
} else {
const registers = 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});
}
this.cdr.markForCheck();
} }
validate(): ValidationErrors | null { validate(): ValidationErrors | null {
@ -201,4 +215,13 @@ export class ModbusValuesComponent implements ControlValueAccessor, Validator, O
this.onTouched(); this.onTouched();
}); });
} }
private getSingleRegisterState(values: ModbusValues): ModbusValues {
return {
attributes: values?.attributes ?? [],
timeseries: values?.timeseries ?? [],
attributeUpdates: values?.attributeUpdates ?? [],
rpc: values?.rpc ?? [],
};
}
} }

View File

@ -973,12 +973,21 @@ export interface ModbusSlave {
sendDataToThingsBoard: boolean; sendDataToThingsBoard: boolean;
byteOrder: ModbusOrderType; byteOrder: ModbusOrderType;
identity: ModbusIdentity; identity: ModbusIdentity;
values: ModbusRegisterValues; values: ModbusValuesState;
port: string | number; port: string | number;
security: ModbusSecurity; security: ModbusSecurity;
} }
export type ModbusValuesState = ModbusRegisterValues | ModbusValues;
export interface ModbusRegisterValues { export interface ModbusRegisterValues {
holding_registers: ModbusValues;
coils_initializer: ModbusValues;
input_registers: ModbusValues;
discrete_inputs: ModbusValues;
}
export interface ModbusValues {
attributes: ModbusValue[]; attributes: ModbusValue[];
timeseries: ModbusValue[]; timeseries: ModbusValue[];
attributeUpdates: ModbusValue[]; attributeUpdates: ModbusValue[];

View File

@ -2901,6 +2901,7 @@
"device-profile-required": "Device profile required", "device-profile-required": "Device profile required",
"download-tip": "Download configuration file", "download-tip": "Download configuration file",
"drop-file": "Drop file here or", "drop-file": "Drop file here or",
"enable": "Enable",
"enable-subscription": "Enable subscription", "enable-subscription": "Enable subscription",
"extension": "Extension", "extension": "Extension",
"extension-hint": "Put your converter classname in the field. Custom converter with such class should be in extension/mqtt folder.", "extension-hint": "Put your converter classname in the field. Custom converter with such class should be in extension/mqtt folder.",