Added Modbus Report Strategy
This commit is contained in:
parent
fb54dbc858
commit
1b36e5770a
@ -16,10 +16,12 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
GatewayConnector,
|
GatewayConnector,
|
||||||
|
LegacySlaveConfig,
|
||||||
ModbusBasicConfig,
|
ModbusBasicConfig,
|
||||||
ModbusBasicConfig_v3_5_2,
|
ModbusBasicConfig_v3_5_2,
|
||||||
ModbusLegacyBasicConfig,
|
ModbusLegacyBasicConfig,
|
||||||
ModbusLegacySlave,
|
ModbusLegacySlave,
|
||||||
|
ModbusMasterConfig,
|
||||||
ModbusSlave,
|
ModbusSlave,
|
||||||
} from '../gateway-widget.models';
|
} from '../gateway-widget.models';
|
||||||
import { GatewayConnectorVersionProcessor } from './gateway-connector-version-processor.abstract';
|
import { GatewayConnectorVersionProcessor } from './gateway-connector-version-processor.abstract';
|
||||||
@ -40,7 +42,7 @@ export class ModbusVersionProcessor extends GatewayConnectorVersionProcessor<any
|
|||||||
...this.connector,
|
...this.connector,
|
||||||
configurationJson: {
|
configurationJson: {
|
||||||
master: configurationJson.master?.slaves
|
master: configurationJson.master?.slaves
|
||||||
? ModbusVersionMappingUtil.mapMasterToUpgradedVersion(configurationJson.master)
|
? ModbusVersionMappingUtil.mapMasterToUpgradedVersion(configurationJson.master as ModbusMasterConfig<LegacySlaveConfig>)
|
||||||
: { slaves: [] },
|
: { slaves: [] },
|
||||||
slave: configurationJson.slave
|
slave: configurationJson.slave
|
||||||
? ModbusVersionMappingUtil.mapSlaveToUpgradedVersion(configurationJson.slave as ModbusLegacySlave)
|
? ModbusVersionMappingUtil.mapSlaveToUpgradedVersion(configurationJson.slave as ModbusLegacySlave)
|
||||||
@ -59,7 +61,9 @@ export class ModbusVersionProcessor extends GatewayConnectorVersionProcessor<any
|
|||||||
slave: configurationJson.slave
|
slave: configurationJson.slave
|
||||||
? ModbusVersionMappingUtil.mapSlaveToDowngradedVersion(configurationJson.slave as ModbusSlave)
|
? ModbusVersionMappingUtil.mapSlaveToDowngradedVersion(configurationJson.slave as ModbusSlave)
|
||||||
: {} as ModbusLegacySlave,
|
: {} as ModbusLegacySlave,
|
||||||
master: configurationJson.master,
|
master: configurationJson.master?.slaves
|
||||||
|
? ModbusVersionMappingUtil.mapMasterToDowngradedVersion(configurationJson.master as ModbusMasterConfig)
|
||||||
|
: { slaves: [] },
|
||||||
},
|
},
|
||||||
configVersion: this.gatewayVersionIn
|
configVersion: this.gatewayVersionIn
|
||||||
} as GatewayConnector<ModbusLegacyBasicConfig>;
|
} as GatewayConnector<ModbusLegacyBasicConfig>;
|
||||||
|
|||||||
@ -25,8 +25,8 @@ import {
|
|||||||
} from '@home/components/widget/lib/gateway/gateway-widget.models';
|
} from '@home/components/widget/lib/gateway/gateway-widget.models';
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
export abstract class ModbusBasicConfigDirective<BasicConfig>
|
export abstract class ModbusBasicConfigDirective<InputBasicConfig, OutputBasicConfig>
|
||||||
extends GatewayConnectorBasicConfigDirective<ModbusBasicConfig_v3_5_2, BasicConfig> {
|
extends GatewayConnectorBasicConfigDirective<InputBasicConfig, OutputBasicConfig> {
|
||||||
|
|
||||||
enableSlaveControl: FormControl<boolean> = new FormControl(false);
|
enableSlaveControl: FormControl<boolean> = new FormControl(false);
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ export abstract class ModbusBasicConfigDirective<BasicConfig>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
override writeValue(basicConfig: BasicConfig & ModbusBasicConfig): void {
|
override writeValue(basicConfig: OutputBasicConfig & ModbusBasicConfig): void {
|
||||||
super.writeValue(basicConfig);
|
super.writeValue(basicConfig);
|
||||||
this.onEnableSlaveControl(basicConfig);
|
this.onEnableSlaveControl(basicConfig);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
<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 [isLegacy]="isLegacy" formControlName="master"></tb-modbus-master-table>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
<mat-tab label="{{ 'gateway.server-config' | translate }}">
|
<mat-tab label="{{ 'gateway.server-config' | translate }}">
|
||||||
<div class="tb-form-panel no-border no-padding padding-top">
|
<div class="tb-form-panel no-border no-padding padding-top">
|
||||||
|
|||||||
@ -56,7 +56,9 @@ import {
|
|||||||
],
|
],
|
||||||
styleUrls: ['./modbus-basic-config.component.scss'],
|
styleUrls: ['./modbus-basic-config.component.scss'],
|
||||||
})
|
})
|
||||||
export class ModbusBasicConfigComponent extends ModbusBasicConfigDirective<ModbusBasicConfig_v3_5_2> {
|
export class ModbusBasicConfigComponent extends ModbusBasicConfigDirective<ModbusBasicConfig_v3_5_2, ModbusBasicConfig_v3_5_2> {
|
||||||
|
|
||||||
|
isLegacy = false;
|
||||||
|
|
||||||
protected override mapConfigToFormValue({ master, slave }: ModbusBasicConfig_v3_5_2): ModbusBasicConfig_v3_5_2 {
|
protected override mapConfigToFormValue({ master, slave }: ModbusBasicConfig_v3_5_2): ModbusBasicConfig_v3_5_2 {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -17,8 +17,10 @@
|
|||||||
import { ChangeDetectionStrategy, Component, forwardRef } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, forwardRef } from '@angular/core';
|
||||||
import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import {
|
import {
|
||||||
ModbusBasicConfig_v3_5_2,
|
LegacySlaveConfig,
|
||||||
ModbusLegacyBasicConfig, ModbusLegacySlave,
|
ModbusBasicConfig,
|
||||||
|
ModbusLegacyBasicConfig,
|
||||||
|
ModbusLegacySlave,
|
||||||
ModbusMasterConfig,
|
ModbusMasterConfig,
|
||||||
ModbusSlave
|
ModbusSlave
|
||||||
} from '@home/components/widget/lib/gateway/gateway-widget.models';
|
} from '@home/components/widget/lib/gateway/gateway-widget.models';
|
||||||
@ -58,21 +60,21 @@ import {
|
|||||||
],
|
],
|
||||||
styleUrls: ['./modbus-basic-config.component.scss'],
|
styleUrls: ['./modbus-basic-config.component.scss'],
|
||||||
})
|
})
|
||||||
export class ModbusLegacyBasicConfigComponent extends ModbusBasicConfigDirective<ModbusLegacyBasicConfig> {
|
export class ModbusLegacyBasicConfigComponent extends ModbusBasicConfigDirective<ModbusBasicConfig, ModbusLegacyBasicConfig> {
|
||||||
|
|
||||||
protected override mapConfigToFormValue(config: ModbusLegacyBasicConfig): ModbusBasicConfig_v3_5_2 {
|
isLegacy = true;
|
||||||
|
|
||||||
|
protected override mapConfigToFormValue(config: ModbusLegacyBasicConfig): ModbusBasicConfig {
|
||||||
return {
|
return {
|
||||||
master: config.master?.slaves
|
master: config.master?.slaves ? config.master : { slaves: [] } as ModbusMasterConfig<LegacySlaveConfig>,
|
||||||
? ModbusVersionMappingUtil.mapMasterToUpgradedVersion(config.master)
|
slave: config.slave ? ModbusVersionMappingUtil.mapSlaveToUpgradedVersion(config.slave as ModbusLegacySlave) : {},
|
||||||
: { slaves: [] } as ModbusMasterConfig,
|
} as ModbusBasicConfig;
|
||||||
slave: config.slave ? ModbusVersionMappingUtil.mapSlaveToUpgradedVersion(config.slave) : {} as ModbusSlave,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override getMappedValue(value: ModbusBasicConfig_v3_5_2): ModbusLegacyBasicConfig {
|
protected override getMappedValue(value: ModbusBasicConfig): ModbusLegacyBasicConfig {
|
||||||
return {
|
return {
|
||||||
master: value.master,
|
master: value.master as ModbusMasterConfig<LegacySlaveConfig>,
|
||||||
slave: value.slave ? ModbusVersionMappingUtil.mapSlaveToDowngradedVersion(value.slave) : {} as ModbusLegacySlave,
|
slave: value.slave ? ModbusVersionMappingUtil.mapSlaveToDowngradedVersion(value.slave as ModbusSlave) : {} as ModbusLegacySlave,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -165,6 +165,11 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<tb-report-strategy
|
||||||
|
*ngIf="withReportStrategy"
|
||||||
|
formControlName="reportStrategy"
|
||||||
|
[isExpansionMode]="true"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
|||||||
@ -41,6 +41,9 @@ import { generateSecret } from '@core/utils';
|
|||||||
import { coerceBoolean } from '@shared/decorators/coercion';
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
import {
|
||||||
|
ReportStrategyComponent
|
||||||
|
} from '@home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-modbus-data-keys-panel',
|
selector: 'tb-modbus-data-keys-panel',
|
||||||
@ -51,12 +54,15 @@ import { Subject } from 'rxjs';
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
GatewayHelpLinkPipe,
|
GatewayHelpLinkPipe,
|
||||||
|
ReportStrategyComponent,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy {
|
export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
@coerceBoolean()
|
@coerceBoolean()
|
||||||
@Input() isMaster = false;
|
@Input() isMaster = false;
|
||||||
|
@coerceBoolean()
|
||||||
|
@Input() hideNewFields = false;
|
||||||
@Input() panelTitle: string;
|
@Input() panelTitle: string;
|
||||||
@Input() addKeyTitle: string;
|
@Input() addKeyTitle: string;
|
||||||
@Input() deleteKeyTitle: string;
|
@Input() deleteKeyTitle: string;
|
||||||
@ -70,6 +76,7 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy {
|
|||||||
keysListFormArray: FormArray<UntypedFormGroup>;
|
keysListFormArray: FormArray<UntypedFormGroup>;
|
||||||
modbusDataTypes = Object.values(ModbusDataType);
|
modbusDataTypes = Object.values(ModbusDataType);
|
||||||
withFunctionCode = true;
|
withFunctionCode = true;
|
||||||
|
withReportStrategy = true;
|
||||||
functionCodesMap = new Map();
|
functionCodesMap = new Map();
|
||||||
defaultFunctionCodes = [];
|
defaultFunctionCodes = [];
|
||||||
|
|
||||||
@ -87,6 +94,9 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.withFunctionCode = !this.isMaster || (this.keysType !== ModbusValueKey.ATTRIBUTES && this.keysType !== ModbusValueKey.TIMESERIES);
|
this.withFunctionCode = !this.isMaster || (this.keysType !== ModbusValueKey.ATTRIBUTES && this.keysType !== ModbusValueKey.TIMESERIES);
|
||||||
|
this.withReportStrategy = !this.isMaster
|
||||||
|
&& (this.keysType === ModbusValueKey.ATTRIBUTES || this.keysType === ModbusValueKey.TIMESERIES)
|
||||||
|
&& !this.hideNewFields;
|
||||||
this.keysListFormArray = this.prepareKeysFormArray(this.values);
|
this.keysListFormArray = this.prepareKeysFormArray(this.values);
|
||||||
this.defaultFunctionCodes = this.getDefaultFunctionCodes();
|
this.defaultFunctionCodes = this.getDefaultFunctionCodes();
|
||||||
}
|
}
|
||||||
@ -108,6 +118,7 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy {
|
|||||||
address: [null, [Validators.required]],
|
address: [null, [Validators.required]],
|
||||||
objectsCount: [1, [Validators.required]],
|
objectsCount: [1, [Validators.required]],
|
||||||
functionCode: [{ value: this.getDefaultFunctionCodes()[0], disabled: !this.withFunctionCode }, [Validators.required]],
|
functionCode: [{ value: this.getDefaultFunctionCodes()[0], disabled: !this.withFunctionCode }, [Validators.required]],
|
||||||
|
reportStrategy: [{ value: null, disabled: !this.withReportStrategy }],
|
||||||
id: [{value: generateSecret(5), disabled: true}],
|
id: [{value: generateSecret(5), disabled: true}],
|
||||||
});
|
});
|
||||||
this.observeKeyDataType(dataKeyFormGroup);
|
this.observeKeyDataType(dataKeyFormGroup);
|
||||||
@ -128,7 +139,20 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyKeysData(): void {
|
applyKeysData(): void {
|
||||||
this.keysDataApplied.emit(this.keysListFormArray.value);
|
this.keysDataApplied.emit(this.getFormValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFormValue(): ModbusValue[] {
|
||||||
|
return this.withReportStrategy
|
||||||
|
? this.cleanUpEmptyStrategies(this.keysListFormArray.value)
|
||||||
|
: this.keysListFormArray.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private cleanUpEmptyStrategies(values: ModbusValue[]): ModbusValue[] {
|
||||||
|
return values.map((key) => {
|
||||||
|
const { reportStrategy, ...updatedKey } = key;
|
||||||
|
return !reportStrategy ? updatedKey : key;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private prepareKeysFormArray(values: ModbusValue[]): UntypedFormArray {
|
private prepareKeysFormArray(values: ModbusValue[]): UntypedFormArray {
|
||||||
@ -148,7 +172,7 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createDataKeyFormGroup(modbusValue: ModbusValue): FormGroup {
|
private createDataKeyFormGroup(modbusValue: ModbusValue): FormGroup {
|
||||||
const { tag, value, type, address, objectsCount, functionCode } = modbusValue;
|
const { tag, value, type, address, objectsCount, functionCode, reportStrategy } = modbusValue;
|
||||||
|
|
||||||
return this.fb.group({
|
return this.fb.group({
|
||||||
tag: [tag, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
tag: [tag, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||||
@ -158,6 +182,7 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy {
|
|||||||
objectsCount: [objectsCount, [Validators.required]],
|
objectsCount: [objectsCount, [Validators.required]],
|
||||||
functionCode: [{ value: functionCode, disabled: !this.withFunctionCode }, [Validators.required]],
|
functionCode: [{ value: functionCode, disabled: !this.withFunctionCode }, [Validators.required]],
|
||||||
id: [{ value: generateSecret(5), disabled: true }],
|
id: [{ value: generateSecret(5), disabled: true }],
|
||||||
|
reportStrategy: [{ value: reportStrategy, disabled: !this.withReportStrategy }],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,12 +21,13 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
|
Input,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { DialogService } from '@core/services/dialog.service';
|
import { DialogService } from '@core/services/dialog.service';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { debounceTime, distinctUntilChanged, take, takeUntil } from 'rxjs/operators';
|
import { debounceTime, distinctUntilChanged, take, takeUntil } from 'rxjs/operators';
|
||||||
@ -38,8 +39,9 @@ import {
|
|||||||
UntypedFormGroup,
|
UntypedFormGroup,
|
||||||
} from '@angular/forms';
|
} from '@angular/forms';
|
||||||
import {
|
import {
|
||||||
|
LegacySlaveConfig,
|
||||||
ModbusMasterConfig,
|
ModbusMasterConfig,
|
||||||
ModbusProtocolLabelsMap,
|
ModbusProtocolLabelsMap, ModbusSlave,
|
||||||
ModbusSlaveInfo,
|
ModbusSlaveInfo,
|
||||||
ModbusValues,
|
ModbusValues,
|
||||||
SlaveConfig
|
SlaveConfig
|
||||||
@ -49,6 +51,10 @@ import { SharedModule } from '@shared/shared.module';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { ModbusSlaveDialogComponent } from '../modbus-slave-dialog/modbus-slave-dialog.component';
|
import { ModbusSlaveDialogComponent } from '../modbus-slave-dialog/modbus-slave-dialog.component';
|
||||||
import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract';
|
import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract';
|
||||||
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||||
|
import {
|
||||||
|
ModbusLegacySlaveDialogComponent
|
||||||
|
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-legacy-slave-dialog.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-modbus-master-table',
|
selector: 'tb-modbus-master-table',
|
||||||
@ -69,6 +75,9 @@ export class ModbusMasterTableComponent implements ControlValueAccessor, AfterVi
|
|||||||
|
|
||||||
@ViewChild('searchInput') searchInputField: ElementRef;
|
@ViewChild('searchInput') searchInputField: ElementRef;
|
||||||
|
|
||||||
|
@coerceBoolean()
|
||||||
|
@Input() isLegacy = false;
|
||||||
|
|
||||||
textSearchMode = false;
|
textSearchMode = false;
|
||||||
dataSource: SlavesDatasource;
|
dataSource: SlavesDatasource;
|
||||||
masterFormGroup: UntypedFormGroup;
|
masterFormGroup: UntypedFormGroup;
|
||||||
@ -152,14 +161,7 @@ export class ModbusMasterTableComponent implements ControlValueAccessor, AfterVi
|
|||||||
}
|
}
|
||||||
const withIndex = isDefinedAndNotNull(index);
|
const withIndex = isDefinedAndNotNull(index);
|
||||||
const value = withIndex ? this.slaves.at(index).value : {};
|
const value = withIndex ? this.slaves.at(index).value : {};
|
||||||
this.dialog.open<ModbusSlaveDialogComponent, ModbusSlaveInfo, ModbusValues>(ModbusSlaveDialogComponent, {
|
this.getSlaveDialog(value, withIndex ? 'action.apply' : 'action.add').afterClosed()
|
||||||
disableClose: true,
|
|
||||||
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
|
||||||
data: {
|
|
||||||
value,
|
|
||||||
buttonTitle: withIndex ? 'action.apply' : 'action.add'
|
|
||||||
}
|
|
||||||
}).afterClosed()
|
|
||||||
.pipe(take(1), takeUntil(this.destroy$))
|
.pipe(take(1), takeUntil(this.destroy$))
|
||||||
.subscribe(res => {
|
.subscribe(res => {
|
||||||
if (res) {
|
if (res) {
|
||||||
@ -173,6 +175,33 @@ export class ModbusMasterTableComponent implements ControlValueAccessor, AfterVi
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getSlaveDialog(
|
||||||
|
value: LegacySlaveConfig | SlaveConfig,
|
||||||
|
buttonTitle: string
|
||||||
|
): MatDialogRef<ModbusLegacySlaveDialogComponent | ModbusSlaveDialogComponent> {
|
||||||
|
if (this.isLegacy) {
|
||||||
|
return this.dialog.open<ModbusLegacySlaveDialogComponent, ModbusSlaveInfo<LegacySlaveConfig>, ModbusValues>
|
||||||
|
(ModbusLegacySlaveDialogComponent, {
|
||||||
|
disableClose: true,
|
||||||
|
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
||||||
|
data: {
|
||||||
|
value: value as LegacySlaveConfig,
|
||||||
|
hideNewFields: true,
|
||||||
|
buttonTitle
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this.dialog.open<ModbusSlaveDialogComponent, ModbusSlaveInfo, ModbusValues>(ModbusSlaveDialogComponent, {
|
||||||
|
disableClose: true,
|
||||||
|
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
||||||
|
data: {
|
||||||
|
value: value as SlaveConfig,
|
||||||
|
buttonTitle,
|
||||||
|
hideNewFields: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
deleteSlave($event: Event, index: number): void {
|
deleteSlave($event: Event, index: number): void {
|
||||||
if ($event) {
|
if ($event) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
|
|||||||
@ -0,0 +1,84 @@
|
|||||||
|
///
|
||||||
|
/// Copyright © 2016-2024 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 { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||||
|
import {
|
||||||
|
FormBuilder,
|
||||||
|
} from '@angular/forms';
|
||||||
|
import {
|
||||||
|
LegacySlaveConfig,
|
||||||
|
ModbusProtocolType,
|
||||||
|
ModbusSlaveInfo,
|
||||||
|
} from '@home/components/widget/lib/gateway/gateway-widget.models';
|
||||||
|
import { SharedModule } from '@shared/shared.module';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ModbusValuesComponent } from '../modbus-values/modbus-values.component';
|
||||||
|
import { ModbusSecurityConfigComponent } from '../modbus-security-config/modbus-security-config.component';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { AppState } from '@core/core.state';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { GatewayPortTooltipPipe } from '@home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe';
|
||||||
|
import {
|
||||||
|
ReportStrategyComponent
|
||||||
|
} from '@home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component';
|
||||||
|
import {
|
||||||
|
ModbusSlaveDialogAbstract
|
||||||
|
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.abstract';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-modbus-legacy-slave-dialog',
|
||||||
|
templateUrl: './modbus-slave-dialog.component.html',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SharedModule,
|
||||||
|
ModbusValuesComponent,
|
||||||
|
ModbusSecurityConfigComponent,
|
||||||
|
GatewayPortTooltipPipe,
|
||||||
|
ReportStrategyComponent,
|
||||||
|
],
|
||||||
|
styleUrls: ['./modbus-slave-dialog.component.scss'],
|
||||||
|
})
|
||||||
|
export class ModbusLegacySlaveDialogComponent extends ModbusSlaveDialogAbstract<ModbusLegacySlaveDialogComponent, LegacySlaveConfig> {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected fb: FormBuilder,
|
||||||
|
protected store: Store<AppState>,
|
||||||
|
protected router: Router,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: ModbusSlaveInfo,
|
||||||
|
public dialogRef: MatDialogRef<ModbusLegacySlaveDialogComponent, LegacySlaveConfig>,
|
||||||
|
) {
|
||||||
|
super(fb, store, router, data, dialogRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override getSlaveResultData(): LegacySlaveConfig {
|
||||||
|
const { values, type, serialPort, ...rest } = this.slaveConfigFormGroup.value;
|
||||||
|
const slaveResult = { ...rest, type, ...values };
|
||||||
|
|
||||||
|
if (type === ModbusProtocolType.Serial) {
|
||||||
|
slaveResult.port = serialPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
return slaveResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override addFieldsToFormGroup(): void {
|
||||||
|
this.slaveConfigFormGroup.addControl('sendDataOnlyOnChange', this.fb.control(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,207 @@
|
|||||||
|
///
|
||||||
|
/// Copyright © 2016-2024 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 { Directive, Inject, OnDestroy } from '@angular/core';
|
||||||
|
import {
|
||||||
|
FormBuilder,
|
||||||
|
FormControl,
|
||||||
|
UntypedFormGroup,
|
||||||
|
Validators,
|
||||||
|
} from '@angular/forms';
|
||||||
|
import {
|
||||||
|
ModbusBaudrates,
|
||||||
|
ModbusByteSizes,
|
||||||
|
ModbusMethodLabelsMap,
|
||||||
|
ModbusMethodType,
|
||||||
|
ModbusOrderType,
|
||||||
|
ModbusParity,
|
||||||
|
ModbusParityLabelsMap,
|
||||||
|
ModbusProtocolLabelsMap,
|
||||||
|
ModbusProtocolType,
|
||||||
|
ModbusSerialMethodType,
|
||||||
|
ModbusSlaveInfo,
|
||||||
|
noLeadTrailSpacesRegex,
|
||||||
|
PortLimits,
|
||||||
|
} from '@home/components/widget/lib/gateway/gateway-widget.models';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { DialogComponent } from '@shared/components/dialog.component';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { AppState } from '@core/core.state';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { isEqual } from '@core/utils';
|
||||||
|
import { helpBaseUrl } from '@shared/models/constants';
|
||||||
|
|
||||||
|
@Directive()
|
||||||
|
export abstract class ModbusSlaveDialogAbstract<Component, Config> extends DialogComponent<Component, Config> implements OnDestroy {
|
||||||
|
|
||||||
|
slaveConfigFormGroup: UntypedFormGroup;
|
||||||
|
showSecurityControl: FormControl<boolean>;
|
||||||
|
portLimits = PortLimits;
|
||||||
|
|
||||||
|
readonly modbusProtocolTypes = Object.values(ModbusProtocolType);
|
||||||
|
readonly modbusMethodTypes = Object.values(ModbusMethodType);
|
||||||
|
readonly modbusSerialMethodTypes = Object.values(ModbusSerialMethodType);
|
||||||
|
readonly modbusParities = Object.values(ModbusParity);
|
||||||
|
readonly modbusByteSizes = ModbusByteSizes;
|
||||||
|
readonly modbusBaudrates = ModbusBaudrates;
|
||||||
|
readonly modbusOrderType = Object.values(ModbusOrderType);
|
||||||
|
readonly ModbusProtocolType = ModbusProtocolType;
|
||||||
|
readonly ModbusParityLabelsMap = ModbusParityLabelsMap;
|
||||||
|
readonly ModbusProtocolLabelsMap = ModbusProtocolLabelsMap;
|
||||||
|
readonly ModbusMethodLabelsMap = ModbusMethodLabelsMap;
|
||||||
|
readonly modbusHelpLink =
|
||||||
|
helpBaseUrl + '/docs/iot-gateway/config/modbus/#section-master-description-and-configuration-parameters';
|
||||||
|
|
||||||
|
private readonly serialSpecificControlKeys = ['serialPort', 'baudrate', 'stopbits', 'bytesize', 'parity', 'strict'];
|
||||||
|
private readonly tcpUdpSpecificControlKeys = ['port', 'security', 'host'];
|
||||||
|
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected fb: FormBuilder,
|
||||||
|
protected store: Store<AppState>,
|
||||||
|
protected router: Router,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: ModbusSlaveInfo,
|
||||||
|
public dialogRef: MatDialogRef<Component, Config>,
|
||||||
|
) {
|
||||||
|
super(store, router, dialogRef);
|
||||||
|
|
||||||
|
this.showSecurityControl = this.fb.control(false);
|
||||||
|
this.initializeSlaveFormGroup();
|
||||||
|
this.updateSlaveFormGroup();
|
||||||
|
this.updateControlsEnabling(this.data.value.type);
|
||||||
|
this.observeTypeChange();
|
||||||
|
this.observeShowSecurity();
|
||||||
|
this.showSecurityControl.patchValue(!!this.data.value.security && !isEqual(this.data.value.security, {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
get protocolType(): ModbusProtocolType {
|
||||||
|
return this.slaveConfigFormGroup.get('type').value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel(): void {
|
||||||
|
this.dialogRef.close(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
add(): void {
|
||||||
|
if (!this.slaveConfigFormGroup.valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dialogRef.close(this.getSlaveResultData());
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeSlaveFormGroup(): void {
|
||||||
|
this.slaveConfigFormGroup = this.fb.group({
|
||||||
|
name: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||||
|
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.SOCKET, [Validators.required]],
|
||||||
|
baudrate: [this.modbusBaudrates[0]],
|
||||||
|
stopbits: [1],
|
||||||
|
bytesize: [ModbusByteSizes[0]],
|
||||||
|
parity: [ModbusParity.None],
|
||||||
|
strict: [true],
|
||||||
|
unitId: [null, [Validators.required]],
|
||||||
|
deviceName: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||||
|
deviceType: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||||
|
timeout: [35],
|
||||||
|
byteOrder: [ModbusOrderType.BIG],
|
||||||
|
wordOrder: [ModbusOrderType.BIG],
|
||||||
|
retries: [true],
|
||||||
|
retryOnEmpty: [true],
|
||||||
|
retryOnInvalid: [true],
|
||||||
|
pollPeriod: [5000, [Validators.required]],
|
||||||
|
connectAttemptTimeMs: [5000, [Validators.required]],
|
||||||
|
connectAttemptCount: [5, [Validators.required]],
|
||||||
|
waitAfterFailedAttemptsMs: [300000, [Validators.required]],
|
||||||
|
values: [{}],
|
||||||
|
security: [{}],
|
||||||
|
});
|
||||||
|
this.addFieldsToFormGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateSlaveFormGroup(): void {
|
||||||
|
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 : '',
|
||||||
|
values: {
|
||||||
|
attributes: this.data.value.attributes ?? [],
|
||||||
|
timeseries: this.data.value.timeseries ?? [],
|
||||||
|
attributeUpdates: this.data.value.attributeUpdates ?? [],
|
||||||
|
rpc: this.data.value.rpc ?? [],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private observeTypeChange(): void {
|
||||||
|
this.slaveConfigFormGroup.get('type').valueChanges
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe(type => {
|
||||||
|
this.updateControlsEnabling(type);
|
||||||
|
this.updateMethodType(type);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateMethodType(type: ModbusProtocolType): void {
|
||||||
|
if (this.slaveConfigFormGroup.get('method').value !== ModbusMethodType.RTU) {
|
||||||
|
this.slaveConfigFormGroup.get('method').patchValue(
|
||||||
|
type === ModbusProtocolType.Serial
|
||||||
|
? ModbusSerialMethodType.ASCII
|
||||||
|
: ModbusMethodType.SOCKET,
|
||||||
|
{emitEvent: false}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateControlsEnabling(type: ModbusProtocolType): void {
|
||||||
|
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 => this.updateSecurityEnabling(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateSecurityEnabling(isEnabled: boolean): void {
|
||||||
|
if (isEnabled && this.protocolType !== ModbusProtocolType.Serial) {
|
||||||
|
this.slaveConfigFormGroup.get('security').enable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.slaveConfigFormGroup.get('security').disable({emitEvent: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract addFieldsToFormGroup(): void;
|
||||||
|
protected abstract getSlaveResultData(): Config;
|
||||||
|
}
|
||||||
@ -227,13 +227,16 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tb-form-row" fxLayoutAlign="space-between center">
|
<div *ngIf="data.hideNewFields else reportStrategy" class="tb-form-row" fxLayoutAlign="space-between center">
|
||||||
<mat-slide-toggle class="mat-slide" formControlName="sendDataOnlyOnChange">
|
<mat-slide-toggle class="mat-slide" formControlName="sendDataOnlyOnChange">
|
||||||
<mat-label>
|
<mat-label>
|
||||||
{{ 'gateway.send-data-on-change' | translate }}
|
{{ 'gateway.send-data-on-change' | translate }}
|
||||||
</mat-label>
|
</mat-label>
|
||||||
</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
|
<ng-template #reportStrategy>
|
||||||
|
<tb-report-strategy formControlName="reportStrategy" [isExpansionMode]="true"/>
|
||||||
|
</ng-template>
|
||||||
<div class="tb-form-panel stroked">
|
<div class="tb-form-panel stroked">
|
||||||
<mat-expansion-panel class="tb-settings">
|
<mat-expansion-panel class="tb-settings">
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
@ -345,7 +348,7 @@
|
|||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
</div>
|
</div>
|
||||||
<div class="tb-form-panel stroked">
|
<div class="tb-form-panel stroked">
|
||||||
<tb-modbus-values [singleMode]="true" formControlName="values"></tb-modbus-values>
|
<tb-modbus-values [singleMode]="true" [hideNewFields]="data.hideNewFields" formControlName="values"></tb-modbus-values>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -14,62 +14,35 @@
|
|||||||
/// limitations under the License.
|
/// limitations under the License.
|
||||||
///
|
///
|
||||||
|
|
||||||
import { ChangeDetectionStrategy, Component, forwardRef, Inject, OnDestroy } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
FormBuilder,
|
FormBuilder,
|
||||||
FormControl,
|
|
||||||
NG_VALIDATORS,
|
|
||||||
NG_VALUE_ACCESSOR,
|
|
||||||
UntypedFormGroup,
|
|
||||||
Validators,
|
|
||||||
} from '@angular/forms';
|
} from '@angular/forms';
|
||||||
import {
|
import {
|
||||||
ModbusBaudrates,
|
|
||||||
ModbusByteSizes,
|
|
||||||
ModbusMethodLabelsMap,
|
|
||||||
ModbusMethodType,
|
|
||||||
ModbusOrderType,
|
|
||||||
ModbusParity,
|
|
||||||
ModbusParityLabelsMap,
|
|
||||||
ModbusProtocolLabelsMap,
|
|
||||||
ModbusProtocolType,
|
ModbusProtocolType,
|
||||||
ModbusSerialMethodType,
|
|
||||||
ModbusSlaveInfo,
|
ModbusSlaveInfo,
|
||||||
noLeadTrailSpacesRegex,
|
|
||||||
PortLimits,
|
|
||||||
SlaveConfig,
|
SlaveConfig,
|
||||||
} 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';
|
||||||
import { Subject } from 'rxjs';
|
|
||||||
import { ModbusValuesComponent } from '../modbus-values/modbus-values.component';
|
import { ModbusValuesComponent } from '../modbus-values/modbus-values.component';
|
||||||
import { ModbusSecurityConfigComponent } from '../modbus-security-config/modbus-security-config.component';
|
import { ModbusSecurityConfigComponent } from '../modbus-security-config/modbus-security-config.component';
|
||||||
import { DialogComponent } from '@shared/components/dialog.component';
|
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { AppState } from '@core/core.state';
|
import { AppState } from '@core/core.state';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { GatewayPortTooltipPipe } from '@home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe';
|
import { GatewayPortTooltipPipe } from '@home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import {
|
||||||
import { isEqual } from '@core/utils';
|
ReportStrategyComponent
|
||||||
import { helpBaseUrl } from '@shared/models/constants';
|
} from '@home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component';
|
||||||
|
import {
|
||||||
|
ModbusSlaveDialogAbstract
|
||||||
|
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.abstract';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-modbus-slave-dialog',
|
selector: 'tb-modbus-slave-dialog',
|
||||||
templateUrl: './modbus-slave-dialog.component.html',
|
templateUrl: './modbus-slave-dialog.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
providers: [
|
|
||||||
{
|
|
||||||
provide: NG_VALUE_ACCESSOR,
|
|
||||||
useExisting: forwardRef(() => ModbusSlaveDialogComponent),
|
|
||||||
multi: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: NG_VALIDATORS,
|
|
||||||
useExisting: forwardRef(() => ModbusSlaveDialogComponent),
|
|
||||||
multi: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@ -77,70 +50,23 @@ import { helpBaseUrl } from '@shared/models/constants';
|
|||||||
ModbusValuesComponent,
|
ModbusValuesComponent,
|
||||||
ModbusSecurityConfigComponent,
|
ModbusSecurityConfigComponent,
|
||||||
GatewayPortTooltipPipe,
|
GatewayPortTooltipPipe,
|
||||||
|
ReportStrategyComponent,
|
||||||
],
|
],
|
||||||
styleUrls: ['./modbus-slave-dialog.component.scss'],
|
styleUrls: ['./modbus-slave-dialog.component.scss'],
|
||||||
})
|
})
|
||||||
export class ModbusSlaveDialogComponent extends DialogComponent<ModbusSlaveDialogComponent, SlaveConfig> implements OnDestroy {
|
export class ModbusSlaveDialogComponent extends ModbusSlaveDialogAbstract<ModbusSlaveDialogComponent, SlaveConfig> {
|
||||||
|
|
||||||
slaveConfigFormGroup: UntypedFormGroup;
|
|
||||||
showSecurityControl: FormControl<boolean>;
|
|
||||||
portLimits = PortLimits;
|
|
||||||
|
|
||||||
readonly modbusProtocolTypes = Object.values(ModbusProtocolType);
|
|
||||||
readonly modbusMethodTypes = Object.values(ModbusMethodType);
|
|
||||||
readonly modbusSerialMethodTypes = Object.values(ModbusSerialMethodType);
|
|
||||||
readonly modbusParities = Object.values(ModbusParity);
|
|
||||||
readonly modbusByteSizes = ModbusByteSizes;
|
|
||||||
readonly modbusBaudrates = ModbusBaudrates;
|
|
||||||
readonly modbusOrderType = Object.values(ModbusOrderType);
|
|
||||||
readonly ModbusProtocolType = ModbusProtocolType;
|
|
||||||
readonly ModbusParityLabelsMap = ModbusParityLabelsMap;
|
|
||||||
readonly ModbusProtocolLabelsMap = ModbusProtocolLabelsMap;
|
|
||||||
readonly ModbusMethodLabelsMap = ModbusMethodLabelsMap;
|
|
||||||
readonly modbusHelpLink =
|
|
||||||
helpBaseUrl + '/docs/iot-gateway/config/modbus/#section-master-description-and-configuration-parameters';
|
|
||||||
|
|
||||||
private readonly serialSpecificControlKeys = ['serialPort', 'baudrate', 'stopbits', 'bytesize', 'parity', 'strict'];
|
|
||||||
private readonly tcpUdpSpecificControlKeys = ['port', 'security', 'host'];
|
|
||||||
|
|
||||||
private destroy$ = new Subject<void>();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private fb: FormBuilder,
|
protected fb: FormBuilder,
|
||||||
protected store: Store<AppState>,
|
protected store: Store<AppState>,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: ModbusSlaveInfo,
|
@Inject(MAT_DIALOG_DATA) public data: ModbusSlaveInfo,
|
||||||
public dialogRef: MatDialogRef<ModbusSlaveDialogComponent, SlaveConfig>,
|
public dialogRef: MatDialogRef<ModbusSlaveDialogComponent, SlaveConfig>,
|
||||||
) {
|
) {
|
||||||
super(store, router, dialogRef);
|
super(fb, store, router, data, dialogRef);
|
||||||
|
|
||||||
this.showSecurityControl = this.fb.control(false);
|
|
||||||
this.initializeSlaveFormGroup();
|
|
||||||
this.updateSlaveFormGroup();
|
|
||||||
this.updateControlsEnabling(this.data.value.type);
|
|
||||||
this.observeTypeChange();
|
|
||||||
this.observeShowSecurity();
|
|
||||||
this.showSecurityControl.patchValue(!!this.data.value.security && !isEqual(this.data.value.security, {}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get protocolType(): ModbusProtocolType {
|
protected override getSlaveResultData(): SlaveConfig {
|
||||||
return this.slaveConfigFormGroup.get('type').value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
this.destroy$.next();
|
|
||||||
this.destroy$.complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel(): void {
|
|
||||||
this.dialogRef.close(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
add(): void {
|
|
||||||
if (!this.slaveConfigFormGroup.valid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { values, type, serialPort, ...rest } = this.slaveConfigFormGroup.value;
|
const { values, type, serialPort, ...rest } = this.slaveConfigFormGroup.value;
|
||||||
const slaveResult = { ...rest, type, ...values };
|
const slaveResult = { ...rest, type, ...values };
|
||||||
|
|
||||||
@ -148,97 +74,14 @@ export class ModbusSlaveDialogComponent extends DialogComponent<ModbusSlaveDialo
|
|||||||
slaveResult.port = serialPort;
|
slaveResult.port = serialPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dialogRef.close(slaveResult);
|
if (!slaveResult.reportStrategy) {
|
||||||
}
|
delete slaveResult.reportStrategy;
|
||||||
|
|
||||||
private initializeSlaveFormGroup(): void {
|
|
||||||
this.slaveConfigFormGroup = this.fb.group({
|
|
||||||
name: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
|
||||||
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.SOCKET, [Validators.required]],
|
|
||||||
baudrate: [this.modbusBaudrates[0]],
|
|
||||||
stopbits: [1],
|
|
||||||
bytesize: [ModbusByteSizes[0]],
|
|
||||||
parity: [ModbusParity.None],
|
|
||||||
strict: [true],
|
|
||||||
unitId: [null, [Validators.required]],
|
|
||||||
deviceName: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
|
||||||
deviceType: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
|
||||||
sendDataOnlyOnChange: [false],
|
|
||||||
timeout: [35],
|
|
||||||
byteOrder: [ModbusOrderType.BIG],
|
|
||||||
wordOrder: [ModbusOrderType.BIG],
|
|
||||||
retries: [true],
|
|
||||||
retryOnEmpty: [true],
|
|
||||||
retryOnInvalid: [true],
|
|
||||||
pollPeriod: [5000, [Validators.required]],
|
|
||||||
connectAttemptTimeMs: [5000, [Validators.required]],
|
|
||||||
connectAttemptCount: [5, [Validators.required]],
|
|
||||||
waitAfterFailedAttemptsMs: [300000, [Validators.required]],
|
|
||||||
values: [{}],
|
|
||||||
security: [{}],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateSlaveFormGroup(): void {
|
|
||||||
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 : '',
|
|
||||||
values: {
|
|
||||||
attributes: this.data.value.attributes ?? [],
|
|
||||||
timeseries: this.data.value.timeseries ?? [],
|
|
||||||
attributeUpdates: this.data.value.attributeUpdates ?? [],
|
|
||||||
rpc: this.data.value.rpc ?? [],
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private observeTypeChange(): void {
|
|
||||||
this.slaveConfigFormGroup.get('type').valueChanges
|
|
||||||
.pipe(takeUntil(this.destroy$))
|
|
||||||
.subscribe(type => {
|
|
||||||
this.updateControlsEnabling(type);
|
|
||||||
this.updateMethodType(type);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateMethodType(type: ModbusProtocolType): void {
|
|
||||||
if (this.slaveConfigFormGroup.get('method').value !== ModbusMethodType.RTU) {
|
|
||||||
this.slaveConfigFormGroup.get('method').patchValue(
|
|
||||||
type === ModbusProtocolType.Serial
|
|
||||||
? ModbusSerialMethodType.ASCII
|
|
||||||
: ModbusMethodType.SOCKET,
|
|
||||||
{emitEvent: false}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return slaveResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateControlsEnabling(type: ModbusProtocolType): void {
|
protected override addFieldsToFormGroup(): void {
|
||||||
const [enableKeys, disableKeys] = type === ModbusProtocolType.Serial
|
this.slaveConfigFormGroup.addControl('reportStrategy', this.fb.control(null));
|
||||||
? [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 => this.updateSecurityEnabling(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateSecurityEnabling(isEnabled: boolean): void {
|
|
||||||
if (isEnabled && this.protocolType !== ModbusProtocolType.Serial) {
|
|
||||||
this.slaveConfigFormGroup.get('security').enable({emitEvent: false});
|
|
||||||
} else {
|
|
||||||
this.slaveConfigFormGroup.get('security').disable({emitEvent: false});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -87,6 +87,9 @@ export class ModbusValuesComponent implements ControlValueAccessor, Validator, O
|
|||||||
@coerceBoolean()
|
@coerceBoolean()
|
||||||
@Input() singleMode = false;
|
@Input() singleMode = false;
|
||||||
|
|
||||||
|
@coerceBoolean()
|
||||||
|
@Input() hideNewFields = false;
|
||||||
|
|
||||||
disabled = false;
|
disabled = false;
|
||||||
modbusRegisterTypes: ModbusRegisterType[] = Object.values(ModbusRegisterType);
|
modbusRegisterTypes: ModbusRegisterType[] = Object.values(ModbusRegisterType);
|
||||||
modbusValueKeys = Object.values(ModbusValueKey);
|
modbusValueKeys = Object.values(ModbusValueKey);
|
||||||
@ -172,7 +175,8 @@ export class ModbusValuesComponent implements ControlValueAccessor, Validator, O
|
|||||||
panelTitle: ModbusKeysPanelTitleTranslationsMap.get(keysType),
|
panelTitle: ModbusKeysPanelTitleTranslationsMap.get(keysType),
|
||||||
addKeyTitle: ModbusKeysAddKeyTranslationsMap.get(keysType),
|
addKeyTitle: ModbusKeysAddKeyTranslationsMap.get(keysType),
|
||||||
deleteKeyTitle: ModbusKeysDeleteKeyTranslationsMap.get(keysType),
|
deleteKeyTitle: ModbusKeysDeleteKeyTranslationsMap.get(keysType),
|
||||||
noKeysText: ModbusKeysNoKeysTextTranslationsMap.get(keysType)
|
noKeysText: ModbusKeysNoKeysTextTranslationsMap.get(keysType),
|
||||||
|
hideNewFields: this.hideNewFields,
|
||||||
};
|
};
|
||||||
const dataKeysPanelPopover = this.popoverService.displayPopover(
|
const dataKeysPanelPopover = this.popoverService.displayPopover(
|
||||||
trigger,
|
trigger,
|
||||||
|
|||||||
@ -0,0 +1,57 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016-2024 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 [formGroup]="reportStrategyFormGroup" class="tb-form-panel stroked">
|
||||||
|
<mat-expansion-panel *ngIf="isExpansionMode else defaultMode" class="tb-settings" [expanded]="showStrategyControl.value">
|
||||||
|
<mat-expansion-panel-header fxLayout="row wrap">
|
||||||
|
<mat-panel-title>
|
||||||
|
<mat-slide-toggle fxLayoutAlign="center" [formControl]="showStrategyControl" class="mat-slide" (click)="$event.stopPropagation()">
|
||||||
|
<mat-label>
|
||||||
|
{{ 'gateway.report-strategy.label' | translate }}
|
||||||
|
</mat-label>
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</mat-panel-title>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<ng-container [ngTemplateOutlet]="strategyFields"></ng-container>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
<ng-template #defaultMode>
|
||||||
|
<div class="tb-form-panel-title" translate>gateway.report-strategy.label</div>
|
||||||
|
<ng-container [ngTemplateOutlet]="strategyFields"></ng-container>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template #strategyFields>
|
||||||
|
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||||
|
<div class="fixed-title-width">{{ 'gateway.type' | translate }}</div>
|
||||||
|
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
|
||||||
|
<mat-select formControlName="type">
|
||||||
|
<mat-option *ngFor="let type of reportStrategyTypes" [value]="type">{{ ReportTypeTranslateMap.get(type) | translate }}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="reportStrategyFormGroup.get('type').value !== ReportStrategyType.OnChange" class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||||
|
<div class="fixed-title-width tb-required">
|
||||||
|
<span tbTruncateWithTooltip translate>
|
||||||
|
gateway.report-strategy.report-period
|
||||||
|
</span>
|
||||||
|
</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" name="value" formControlName="reportPeriod" placeholder="{{ 'gateway.set' | translate }}"/>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,168 @@
|
|||||||
|
///
|
||||||
|
/// Copyright © 2016-2024 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 {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
forwardRef,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import {
|
||||||
|
ControlValueAccessor,
|
||||||
|
FormBuilder,
|
||||||
|
FormControl,
|
||||||
|
NG_VALIDATORS,
|
||||||
|
NG_VALUE_ACCESSOR,
|
||||||
|
UntypedFormGroup,
|
||||||
|
ValidationErrors,
|
||||||
|
Validators
|
||||||
|
} from '@angular/forms';
|
||||||
|
import {
|
||||||
|
ReportStrategyConfig,
|
||||||
|
ReportStrategyType,
|
||||||
|
ReportStrategyTypeTranslationsMap
|
||||||
|
} from '@home/components/widget/lib/gateway/gateway-widget.models';
|
||||||
|
import { filter, takeUntil } from 'rxjs/operators';
|
||||||
|
import { SharedModule } from '@shared/shared.module';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import {
|
||||||
|
ModbusSecurityConfigComponent
|
||||||
|
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component';
|
||||||
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-report-strategy',
|
||||||
|
templateUrl: './report-strategy.component.html',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => ReportStrategyComponent),
|
||||||
|
multi: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: NG_VALIDATORS,
|
||||||
|
useExisting: forwardRef(() => ReportStrategyComponent),
|
||||||
|
multi: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SharedModule,
|
||||||
|
ModbusSecurityConfigComponent,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class ReportStrategyComponent implements ControlValueAccessor, OnDestroy {
|
||||||
|
|
||||||
|
@coerceBoolean()
|
||||||
|
@Input() isExpansionMode = false;
|
||||||
|
|
||||||
|
reportStrategyFormGroup: UntypedFormGroup;
|
||||||
|
showStrategyControl: FormControl<boolean>;
|
||||||
|
|
||||||
|
readonly reportStrategyTypes = Object.values(ReportStrategyType);
|
||||||
|
readonly ReportTypeTranslateMap = ReportStrategyTypeTranslationsMap;
|
||||||
|
readonly ReportStrategyType = ReportStrategyType;
|
||||||
|
|
||||||
|
private onChange: (value: ReportStrategyConfig) => void;
|
||||||
|
private onTouched: () => void;
|
||||||
|
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder) {
|
||||||
|
this.showStrategyControl = this.fb.control(false);
|
||||||
|
|
||||||
|
this.reportStrategyFormGroup = this.fb.group({
|
||||||
|
type: [ReportStrategyType.OnReportPeriod, []],
|
||||||
|
reportPeriod: [5000, [Validators.required]],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.observeStrategyFormChange();
|
||||||
|
this.observeStrategyToggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(reportStrategyConfig: ReportStrategyConfig): void {
|
||||||
|
if (this.isExpansionMode) {
|
||||||
|
this.showStrategyControl.setValue(!!reportStrategyConfig, {emitEvent: false});
|
||||||
|
}
|
||||||
|
const { type = ReportStrategyType.OnReportPeriod, reportPeriod = 5000 } = reportStrategyConfig ?? {};
|
||||||
|
this.reportStrategyFormGroup.setValue({ type, reportPeriod }, {emitEvent: false});
|
||||||
|
this.onTypeChange(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(): ValidationErrors | null {
|
||||||
|
return this.reportStrategyFormGroup.valid || this.reportStrategyFormGroup.disabled ? null : {
|
||||||
|
reportStrategyForm: { valid: false }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: (value: ReportStrategyConfig) => void): void {
|
||||||
|
this.onChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: () => void): void {
|
||||||
|
this.onTouched = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private observeStrategyFormChange(): void {
|
||||||
|
this.reportStrategyFormGroup.valueChanges.pipe(
|
||||||
|
takeUntil(this.destroy$)
|
||||||
|
).subscribe((value) => {
|
||||||
|
this.onChange(value);
|
||||||
|
this.onTouched();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.reportStrategyFormGroup.get('type').valueChanges
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe(type => this.onTypeChange(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
private observeStrategyToggle(): void {
|
||||||
|
this.showStrategyControl.valueChanges
|
||||||
|
.pipe(takeUntil(this.destroy$), filter(() => this.isExpansionMode))
|
||||||
|
.subscribe(enable => {
|
||||||
|
if (enable) {
|
||||||
|
this.reportStrategyFormGroup.enable({emitEvent: false});
|
||||||
|
this.reportStrategyFormGroup.get('reportPeriod').addValidators(Validators.required);
|
||||||
|
this.onChange(this.reportStrategyFormGroup.value);
|
||||||
|
} else {
|
||||||
|
this.reportStrategyFormGroup.disable({emitEvent: false});
|
||||||
|
this.reportStrategyFormGroup.get('reportPeriod').removeValidators(Validators.required);
|
||||||
|
this.onChange(null);
|
||||||
|
}
|
||||||
|
this.reportStrategyFormGroup.updateValueAndValidity({emitEvent: false});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private onTypeChange(type: ReportStrategyType): void {
|
||||||
|
const reportPeriodControl = this.reportStrategyFormGroup.get('reportPeriod');
|
||||||
|
const isDisabled = reportPeriodControl.disabled;
|
||||||
|
|
||||||
|
if (type === ReportStrategyType.OnChange && !isDisabled) {
|
||||||
|
reportPeriodControl.disable({emitEvent: false});
|
||||||
|
} else if (isDisabled) {
|
||||||
|
reportPeriodControl.enable({emitEvent: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -299,5 +299,6 @@
|
|||||||
</mat-label>
|
</mat-label>
|
||||||
</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
|
<tb-report-strategy *ngIf="connectorForm.get('type').value === ConnectorType.MODBUS && connectorForm.get('configVersion').value === GatewayVersion.Current" formControlName="reportStrategy"/>
|
||||||
</section>
|
</section>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|||||||
@ -297,12 +297,13 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
private hasSameConfig(sharedDataConfigJson: ConnectorBaseInfo, connectorDataConfigJson: ConnectorBaseInfo): boolean {
|
private hasSameConfig(sharedDataConfigJson: ConnectorBaseInfo, connectorDataConfigJson: ConnectorBaseInfo): boolean {
|
||||||
const { name, id, enableRemoteLogging, logLevel, ...sharedDataConfig } = sharedDataConfigJson;
|
const { name, id, enableRemoteLogging, logLevel, reportStrategy, ...sharedDataConfig } = sharedDataConfigJson;
|
||||||
const {
|
const {
|
||||||
name: connectorName,
|
name: connectorName,
|
||||||
id: connectorId,
|
id: connectorId,
|
||||||
enableRemoteLogging: connectorEnableRemoteLogging,
|
enableRemoteLogging: connectorEnableRemoteLogging,
|
||||||
logLevel: connectorLogLevel,
|
logLevel: connectorLogLevel,
|
||||||
|
reportStrategy: connectorReportStrategy,
|
||||||
...connectorConfig
|
...connectorConfig
|
||||||
} = connectorDataConfigJson;
|
} = connectorDataConfigJson;
|
||||||
|
|
||||||
@ -351,7 +352,8 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
configuration: '',
|
configuration: '',
|
||||||
configurationJson: {},
|
configurationJson: {},
|
||||||
basicConfig: {},
|
basicConfig: {},
|
||||||
configVersion: ''
|
configVersion: '',
|
||||||
|
reportStrategy: [{ value: {}, disabled: true }],
|
||||||
}, {emitEvent: false});
|
}, {emitEvent: false});
|
||||||
this.connectorForm.markAsPristine();
|
this.connectorForm.markAsPristine();
|
||||||
}
|
}
|
||||||
@ -542,6 +544,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
configurationJson: [{}, [Validators.required]],
|
configurationJson: [{}, [Validators.required]],
|
||||||
basicConfig: [{}],
|
basicConfig: [{}],
|
||||||
configVersion: [''],
|
configVersion: [''],
|
||||||
|
reportStrategy: [{ value: {}, disabled: true }],
|
||||||
});
|
});
|
||||||
this.connectorForm.disable();
|
this.connectorForm.disable();
|
||||||
}
|
}
|
||||||
@ -751,6 +754,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updateConnector(connector: GatewayConnector): void {
|
private updateConnector(connector: GatewayConnector): void {
|
||||||
|
this.toggleReportStrategy(connector.type);
|
||||||
switch (connector.type) {
|
switch (connector.type) {
|
||||||
case ConnectorType.MQTT:
|
case ConnectorType.MQTT:
|
||||||
case ConnectorType.OPCUA:
|
case ConnectorType.OPCUA:
|
||||||
@ -770,6 +774,15 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
this.createJsonConfigWatcher();
|
this.createJsonConfigWatcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private toggleReportStrategy(type: ConnectorType): void {
|
||||||
|
const reportStrategyControl = this.connectorForm.get('reportStrategy');
|
||||||
|
if (type === ConnectorType.MODBUS) {
|
||||||
|
reportStrategyControl.enable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
reportStrategyControl.disable({emitEvent: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private setClientData(data: PageData<GatewayAttributeData>): void {
|
private setClientData(data: PageData<GatewayAttributeData>): void {
|
||||||
if (this.initialConnector) {
|
if (this.initialConnector) {
|
||||||
const clientConnectorData = data.data.find(attr => attr.key === this.initialConnector.name);
|
const clientConnectorData = data.data.find(attr => attr.key === this.initialConnector.name);
|
||||||
|
|||||||
@ -208,6 +208,7 @@ export interface ConnectorBaseInfo {
|
|||||||
id: string;
|
id: string;
|
||||||
enableRemoteLogging: boolean;
|
enableRemoteLogging: boolean;
|
||||||
logLevel: GatewayLogLevel;
|
logLevel: GatewayLogLevel;
|
||||||
|
reportStrategy?: ReportStrategyConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MQTTBasicConfig = MQTTBasicConfig_v3_5_2 | MQTTLegacyBasicConfig;
|
export type MQTTBasicConfig = MQTTBasicConfig_v3_5_2 | MQTTLegacyBasicConfig;
|
||||||
@ -254,7 +255,7 @@ export interface ModbusBasicConfig_v3_5_2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ModbusLegacyBasicConfig {
|
export interface ModbusLegacyBasicConfig {
|
||||||
master: ModbusMasterConfig;
|
master: ModbusMasterConfig<LegacySlaveConfig>;
|
||||||
slave: ModbusLegacySlave;
|
slave: ModbusLegacySlave;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,9 +589,10 @@ export interface MappingInfo {
|
|||||||
buttonTitle: string;
|
buttonTitle: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ModbusSlaveInfo {
|
export interface ModbusSlaveInfo<Slave = SlaveConfig> {
|
||||||
value: SlaveConfig;
|
value: Slave;
|
||||||
buttonTitle: string;
|
buttonTitle: string;
|
||||||
|
hideNewFields: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ConfigurationModes {
|
export enum ConfigurationModes {
|
||||||
@ -604,6 +606,20 @@ export enum SecurityType {
|
|||||||
CERTIFICATES = 'certificates'
|
CERTIFICATES = 'certificates'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ReportStrategyType {
|
||||||
|
OnChange = 'ON_CHANGE',
|
||||||
|
OnReportPeriod = 'ON_REPORT_PERIOD',
|
||||||
|
OnChangeOrReportPeriod = 'ON_CHANGE_OR_REPORT_PERIOD'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ReportStrategyTypeTranslationsMap = new Map<ReportStrategyType, string>(
|
||||||
|
[
|
||||||
|
[ReportStrategyType.OnChange, 'gateway.report-strategy.on-change'],
|
||||||
|
[ReportStrategyType.OnReportPeriod, 'gateway.report-strategy.on-report-period'],
|
||||||
|
[ReportStrategyType.OnChangeOrReportPeriod, 'gateway.report-strategy.on-change-or-report-period']
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
export enum ModeType {
|
export enum ModeType {
|
||||||
NONE = 'None',
|
NONE = 'None',
|
||||||
SIGN = 'Sign',
|
SIGN = 'Sign',
|
||||||
@ -1097,8 +1113,12 @@ export const ModbusKeysNoKeysTextTranslationsMap = new Map<ModbusValueKey, strin
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface ModbusMasterConfig {
|
export interface ModbusMasterConfig<Slave = SlaveConfig> {
|
||||||
slaves: SlaveConfig[];
|
slaves: Slave[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LegacySlaveConfig extends Omit<SlaveConfig, 'reportStrategy'> {
|
||||||
|
sendDataOnlyOnChange: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SlaveConfig {
|
export interface SlaveConfig {
|
||||||
@ -1118,7 +1138,7 @@ export interface SlaveConfig {
|
|||||||
unitId: number;
|
unitId: number;
|
||||||
deviceName: string;
|
deviceName: string;
|
||||||
deviceType?: string;
|
deviceType?: string;
|
||||||
sendDataOnlyOnChange: boolean;
|
reportStrategy: ReportStrategyConfig;
|
||||||
connectAttemptTimeMs: number;
|
connectAttemptTimeMs: number;
|
||||||
connectAttemptCount: number;
|
connectAttemptCount: number;
|
||||||
waitAfterFailedAttemptsMs: number;
|
waitAfterFailedAttemptsMs: number;
|
||||||
@ -1141,6 +1161,7 @@ export interface ModbusValue {
|
|||||||
objectsCount: number;
|
objectsCount: number;
|
||||||
address: number;
|
address: number;
|
||||||
value?: string;
|
value?: string;
|
||||||
|
reportStrategy?: ReportStrategyConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ModbusSecurity {
|
export interface ModbusSecurity {
|
||||||
@ -1205,4 +1226,9 @@ export interface ModbusIdentity {
|
|||||||
modelName?: string;
|
modelName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ReportStrategyConfig {
|
||||||
|
type: ReportStrategyType;
|
||||||
|
reportPeriod?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export const ModbusBaudrates = [4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600];
|
export const ModbusBaudrates = [4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600];
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
///
|
///
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
LegacySlaveConfig,
|
||||||
ModbusDataType,
|
ModbusDataType,
|
||||||
ModbusLegacyRegisterValues,
|
ModbusLegacyRegisterValues,
|
||||||
ModbusLegacySlave,
|
ModbusLegacySlave,
|
||||||
@ -23,17 +24,36 @@ import {
|
|||||||
ModbusSlave,
|
ModbusSlave,
|
||||||
ModbusValue,
|
ModbusValue,
|
||||||
ModbusValues,
|
ModbusValues,
|
||||||
|
ReportStrategyType,
|
||||||
SlaveConfig
|
SlaveConfig
|
||||||
} from '@home/components/widget/lib/gateway/gateway-widget.models';
|
} from '@home/components/widget/lib/gateway/gateway-widget.models';
|
||||||
|
|
||||||
export class ModbusVersionMappingUtil {
|
export class ModbusVersionMappingUtil {
|
||||||
|
|
||||||
static mapMasterToUpgradedVersion(master: ModbusMasterConfig): ModbusMasterConfig {
|
static mapMasterToUpgradedVersion(master: ModbusMasterConfig<LegacySlaveConfig>): ModbusMasterConfig {
|
||||||
return {
|
return {
|
||||||
slaves: master.slaves.map((slave: SlaveConfig) => ({
|
slaves: master.slaves.map((slave: LegacySlaveConfig) => {
|
||||||
...slave,
|
const { sendDataOnlyOnChange, ...restSlave } = slave;
|
||||||
deviceType: slave.deviceType ?? 'default',
|
return {
|
||||||
}))
|
...restSlave,
|
||||||
|
deviceType: slave.deviceType ?? 'default',
|
||||||
|
reportStrategy: sendDataOnlyOnChange
|
||||||
|
? { type: ReportStrategyType.OnChange }
|
||||||
|
: { type: ReportStrategyType.OnReportPeriod, reportPeriod: slave.pollPeriod }
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static mapMasterToDowngradedVersion(master: ModbusMasterConfig): ModbusMasterConfig<LegacySlaveConfig> {
|
||||||
|
return {
|
||||||
|
slaves: master.slaves.map((slave: SlaveConfig) => {
|
||||||
|
const { reportStrategy, ...restSlave } = slave;
|
||||||
|
return {
|
||||||
|
...restSlave,
|
||||||
|
sendDataOnlyOnChange: reportStrategy?.type !== ReportStrategyType.OnReportPeriod
|
||||||
|
};
|
||||||
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -160,6 +160,9 @@ import {
|
|||||||
import {
|
import {
|
||||||
ModbusLegacyBasicConfigComponent
|
ModbusLegacyBasicConfigComponent
|
||||||
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component';
|
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component';
|
||||||
|
import {
|
||||||
|
ReportStrategyComponent
|
||||||
|
} from '@home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -253,6 +256,7 @@ import {
|
|||||||
GatewayAdvancedConfigurationComponent,
|
GatewayAdvancedConfigurationComponent,
|
||||||
OpcUaLegacyBasicConfigComponent,
|
OpcUaLegacyBasicConfigComponent,
|
||||||
ModbusLegacyBasicConfigComponent,
|
ModbusLegacyBasicConfigComponent,
|
||||||
|
ReportStrategyComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
EntitiesTableWidgetComponent,
|
EntitiesTableWidgetComponent,
|
||||||
|
|||||||
@ -3348,6 +3348,13 @@
|
|||||||
"memory-storage": "Memory storage",
|
"memory-storage": "Memory storage",
|
||||||
"sqlite": "SQLITE"
|
"sqlite": "SQLITE"
|
||||||
},
|
},
|
||||||
|
"report-strategy": {
|
||||||
|
"label": "Report strategy",
|
||||||
|
"on-change": "On value change",
|
||||||
|
"on-report-period": "On report period",
|
||||||
|
"on-change-or-report-period": "On value change or report period",
|
||||||
|
"report-period": "Report period"
|
||||||
|
},
|
||||||
"source-type": {
|
"source-type": {
|
||||||
"msg": "Extract from message",
|
"msg": "Extract from message",
|
||||||
"topic": "Extract from topic",
|
"topic": "Extract from topic",
|
||||||
|
|||||||
@ -18,7 +18,6 @@
|
|||||||
"unitId": 1,
|
"unitId": 1,
|
||||||
"deviceName": "Temp Sensor",
|
"deviceName": "Temp Sensor",
|
||||||
"deviceType": "default",
|
"deviceType": "default",
|
||||||
"sendDataOnlyOnChange": true,
|
|
||||||
"connectAttemptTimeMs": 5000,
|
"connectAttemptTimeMs": 5000,
|
||||||
"connectAttemptCount": 5,
|
"connectAttemptCount": 5,
|
||||||
"waitAfterFailedAttemptsMs": 300000,
|
"waitAfterFailedAttemptsMs": 300000,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user