Merge pull request #11758 from maxunbearable/fix/3912-device-table

Gateway fixes
This commit is contained in:
Igor Kulikov 2024-09-30 17:08:47 +03:00 committed by GitHub
commit 64bf118b85
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 113 additions and 59 deletions

View File

@ -2159,17 +2159,17 @@
} }
}, },
{ {
"type": "entity", "type": "entityCount",
"entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4",
"filterId": "44038462-1bae-e075-7b31-283341cb2295", "filterId": "44038462-1bae-e075-7b31-283341cb2295",
"dataKeys": [ "dataKeys": [
{ {
"name": "count", "name": "count",
"type": "entityField", "type": "count",
"label": "modbusCount", "label": "modbusCount",
"color": "#4caf50", "color": "#ff5722",
"settings": {}, "settings": {},
"_hash": 0.9300660062254784, "_hash": 0.46402083951505624,
"aggregationType": null, "aggregationType": null,
"units": null, "units": null,
"decimals": null, "decimals": null,
@ -2192,7 +2192,7 @@
{ {
"name": "count", "name": "count",
"type": "count", "type": "count",
"label": "grcpCount", "label": "grpcCount",
"color": "#f44336", "color": "#f44336",
"settings": {}, "settings": {},
"_hash": 0.16110429492126088, "_hash": 0.16110429492126088,
@ -2605,9 +2605,9 @@
"padding": "0px", "padding": "0px",
"settings": { "settings": {
"useMarkdownTextFunction": false, "useMarkdownTextFunction": false,
"markdownTextPattern": "<div style=\"width: 100%; height: 100%; padding: 0;\" fxFlex fxLayout=\"column\">\r\n <mat-tab-group [(selectedIndex)]=\"selectedTabIndex\">\r\n <mat-tab label=\"All\" value=\"gateway_devices_0\"></mat-tab>\r\n <mat-tab *ngIf=\"${mqttCount}\" label=\"MQTT\" value=\"gateway_devices_1\"></mat-tab>\r\n <mat-tab *ngIf=\"${modbusCount}\" label=\"MODBUS\" value=\"gateway_devices_2\"></mat-tab>\r\n <mat-tab *ngIf=\"${grpcCount}\" label=\"GRPC\" value=\"gateway_devices_3\"></mat-tab>\r\n <mat-tab *ngIf=\"${opcuaCount}\" label=\"OPCUA\" value=\"gateway_devices_4\"> </mat-tab>\r\n <mat-tab *ngIf=\"${bleCount}\" label=\"BLE\" value=\"gateway_devices_6\"></mat-tab>\r\n <mat-tab *ngIf=\"${requestCount}\" label=\"REQUEST\" value=\"gateway_devices_7\"></mat-tab>\r\n <mat-tab *ngIf=\"${canCount}\" label=\"CAN\" value=\"gateway_devices_8\"></mat-tab>\r\n <mat-tab *ngIf=\"${bacnetCount}\" label=\"BACNET\" value=\"gateway_devices_9\"></mat-tab>\r\n <mat-tab *ngIf=\"${odbcCount}\" label=\"ODBC\" value=\"gateway_devices_10\"></mat-tab>\r\n <mat-tab *ngIf=\"${restCount}\" label=\"REST\" value=\"gateway_devices_11\"></mat-tab>\r\n <mat-tab *ngIf=\"${snmpCount}\" label=\"SNMP\" value=\"gateway_devices_12\"></mat-tab>\r\n <mat-tab *ngIf=\"${ftpCount}\" label=\"FTP\" value=\"gateway_devices_13\"></mat-tab>\r\n <mat-tab *ngIf=\"${socketCount}\" label=\"SOCKET\" value=\"gateway_devices_14\"></mat-tab>\r\n <mat-tab *ngIf=\"${xmppCount}\" label=\"XMPP\" value=\"gateway_devices_15\"></mat-tab>\r\n <mat-tab *ngIf=\"${occpCount}\" label=\"OCCP\" value=\"gateway_devices_16\"></mat-tab>\r\n <mat-tab *ngIf=\"${customCount}\" label=\"CUSTOM\" value=\"gateway_devices_17\"></mat-tab>\r\n </mat-tab-group><tb-dashboard-state *ngIf=\"selectedTabIndex == 1\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_1\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 2\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_2\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 3\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_3\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 4\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_4\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 6\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_6\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 7\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_7\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 8\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_8\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 9\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_9\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 10\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_10\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 11\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_11\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 12\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_12\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 13\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_13\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 14\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_14\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 15\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_15\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 16\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_16\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"selectedTabIndex == 17\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_17\"></tb-dashboard-state>\r\n <tb-dashboard-state *ngIf=\"!selectedTabIndex\" [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_0\"></tb-dashboard-state>\r\n</div>\r\n", "markdownTextPattern": "<div style=\"width: 100%; height: 100%; padding: 0;\" fxFlex fxLayout=\"column\">\n <mat-tab-group class=devices-tabs>\n <mat-tab label=\"All\" value=\"gateway_devices_0\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_0\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${mqttCount}\" label=\"MQTT\" value=\"gateway_devices_1\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_1\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${modbusCount}\" label=\"MODBUS\" value=\"gateway_devices_2\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_2\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${grpcCount}\" label=\"GRPC\" value=\"gateway_devices_3\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_3\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${opcuaCount}\" label=\"OPCUA\" value=\"gateway_devices_4\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_4\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${bleCount}\" label=\"BLE\" value=\"gateway_devices_6\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_6\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${requestCount}\" label=\"REQUEST\" value=\"gateway_devices_7\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_7\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${canCount}\" label=\"CAN\" value=\"gateway_devices_8\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_8\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${bacnetCount}\" label=\"BACNET\" value=\"gateway_devices_9\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_9\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${odbcCount}\" label=\"ODBC\" value=\"gateway_devices_10\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_10\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${restCount}\" label=\"REST\" value=\"gateway_devices_11\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_11\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${snmpCount}\" label=\"SNMP\" value=\"gateway_devices_12\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_12\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${ftpCount}\" label=\"FTP\" value=\"gateway_devices_13\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_13\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${socketCount}\" label=\"SOCKET\" value=\"gateway_devices_14\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_14\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${xmppCount}\" label=\"XMPP\" value=\"gateway_devices_15\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_15\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${ocppCount}\" label=\"OCPP\" value=\"gateway_devices_16\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_16\"></tb-dashboard-state>\n </mat-tab>\n <mat-tab *ngIf=\"${customCount}\" label=\"CUSTOM\" value=\"gateway_devices_17\">\n <tb-dashboard-state [ctx]=\"ctx\" fxFlex syncParentStateParams=\"true\" stateId=\"gateway_devices_17\"></tb-dashboard-state>\n </mat-tab>\n </mat-tab-group>\n</div>\n",
"applyDefaultMarkdownStyle": false, "applyDefaultMarkdownStyle": false,
"markdownCss": ".mat-mdc-form-field-subscript-wrapper {\n display: none !important;\n}" "markdownCss": ".mat-mdc-form-field-subscript-wrapper {\n display: none !important;\n}\n\n.devices-tabs {\n height: 100%;\n}\n\n::ng-deep .mat-mdc-tab-body-wrapper {\n height: 100%;\n}"
}, },
"title": "Gateway devices", "title": "Gateway devices",
"showTitleIcon": false, "showTitleIcon": false,
@ -6208,7 +6208,7 @@
} }
}, },
"gateway_devices_16": { "gateway_devices_16": {
"name": "gateway_devices_occp", "name": "gateway_devices_ocpp",
"root": false, "root": false,
"layouts": { "layouts": {
"main": { "main": {

View File

@ -18,7 +18,7 @@
<div [formGroup]="gatewayConfigGroup" class="gateway-config-container"> <div [formGroup]="gatewayConfigGroup" class="gateway-config-container">
<div class="content-wrapper"> <div class="content-wrapper">
<mat-toolbar color="primary" [class.page-header]="!dialogRef"> <mat-toolbar color="primary" [class.page-header]="!dialogRef">
<div class="tb-flex space-between"> <div class="tb-flex space-between align-center">
<h2 translate>gateway.gateway-configuration</h2> <h2 translate>gateway.gateway-configuration</h2>
<div class="toolbar-actions"> <div class="toolbar-actions">
<tb-toggle-select [class.dialog-toggle]="!!dialogRef" formControlName="mode" appearance="{{dialogRef ? 'stroked' : 'fill'}}"> <tb-toggle-select [class.dialog-toggle]="!!dialogRef" formControlName="mode" appearance="{{dialogRef ? 'stroked' : 'fill'}}">

View File

@ -223,6 +223,7 @@
</div> </div>
<tb-report-strategy <tb-report-strategy
*ngIf="withReportStrategy" *ngIf="withReportStrategy"
[defaultValue]="ReportStrategyDefaultValue.Key"
formControlName="reportStrategy" formControlName="reportStrategy"
[isExpansionMode]="true" [isExpansionMode]="true"
/> />

View File

@ -28,7 +28,8 @@ import {
import { TbPopoverComponent } from '@shared/components/popover.component'; import { TbPopoverComponent } from '@shared/components/popover.component';
import { import {
ModbusDataType, ModbusDataType,
ModbusEditableDataTypes, ModbusFormValue, ModbusEditableDataTypes,
ModbusFormValue,
ModbusFunctionCodeTranslationsMap, ModbusFunctionCodeTranslationsMap,
ModbusObjectCountByDataType, ModbusObjectCountByDataType,
ModbusValue, ModbusValue,
@ -37,6 +38,7 @@ import {
ModifierTypesMap, ModifierTypesMap,
noLeadTrailSpacesRegex, noLeadTrailSpacesRegex,
nonZeroFloat, nonZeroFloat,
ReportStrategyDefaultValue,
} from '@home/components/widget/lib/gateway/gateway-widget.models'; } from '@home/components/widget/lib/gateway/gateway-widget.models';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module'; import { SharedModule } from '@shared/shared.module';
@ -91,6 +93,7 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy {
readonly ModbusEditableDataTypes = ModbusEditableDataTypes; readonly ModbusEditableDataTypes = ModbusEditableDataTypes;
readonly ModbusFunctionCodeTranslationsMap = ModbusFunctionCodeTranslationsMap; readonly ModbusFunctionCodeTranslationsMap = ModbusFunctionCodeTranslationsMap;
readonly ModifierTypesMap = ModifierTypesMap; readonly ModifierTypesMap = ModifierTypesMap;
readonly ReportStrategyDefaultValue = ReportStrategyDefaultValue;
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();

View File

@ -62,6 +62,14 @@
</mat-toolbar> </mat-toolbar>
<div class="table-container"> <div class="table-container">
<table mat-table [dataSource]="dataSource"> <table mat-table [dataSource]="dataSource">
<ng-container [matColumnDef]="'deviceName'">
<mat-header-cell *matHeaderCellDef class="table-value-column">
<div tbTruncateWithTooltip>{{ 'gateway.device-name' | translate }}</div>
</mat-header-cell>
<mat-cell *matCellDef="let slave" class="table-value-column">
<div tbTruncateWithTooltip>{{ slave['deviceName'] }}</div>
</mat-cell>
</ng-container>
<ng-container [matColumnDef]="'info'"> <ng-container [matColumnDef]="'info'">
<mat-header-cell *matHeaderCellDef class="table-value-column"> <mat-header-cell *matHeaderCellDef class="table-value-column">
{{ 'gateway.info' | translate }} {{ 'gateway.info' | translate }}
@ -80,7 +88,7 @@
</ng-container> </ng-container>
<ng-container [matColumnDef]="'type'"> <ng-container [matColumnDef]="'type'">
<mat-header-cell *matHeaderCellDef class="table-value-column"> <mat-header-cell *matHeaderCellDef class="table-value-column">
<div tbTruncateWithTooltip>{{ 'gateway.client-communication-type' | translate }}</div> <div>{{ 'gateway.type' | translate }}</div>
</mat-header-cell> </mat-header-cell>
<mat-cell *matCellDef="let slave" class="table-value-column"> <mat-cell *matCellDef="let slave" class="table-value-column">
{{ ModbusProtocolLabelsMap.get(slave['type']) }} {{ ModbusProtocolLabelsMap.get(slave['type']) }}
@ -121,8 +129,8 @@
</div> </div>
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<mat-header-row [ngClass]="{'mat-row-select': true}" *matHeaderRowDef="['info', 'unitId', 'type', 'actions']; sticky: true"></mat-header-row> <mat-header-row [ngClass]="{'mat-row-select': true}" *matHeaderRowDef="['deviceName', 'info', 'unitId', 'type', 'actions']; sticky: true"></mat-header-row>
<mat-row *matRowDef="let slave; columns: ['info', 'unitId', 'type', 'actions']"></mat-row> <mat-row *matRowDef="let slave; columns: ['deviceName', 'info', 'unitId', 'type', 'actions']"></mat-row>
</table> </table>
<section [fxShow]="!textSearchMode && (dataSource.isEmpty() | async)" fxLayoutAlign="center center" <section [fxShow]="!textSearchMode && (dataSource.isEmpty() | async)" fxLayoutAlign="center center"
class="mat-headline-5 tb-absolute-fill tb-add-new"> class="mat-headline-5 tb-absolute-fill tb-add-new">

View File

@ -35,6 +35,7 @@ import {
ModbusSlaveInfo, ModbusSlaveInfo,
noLeadTrailSpacesRegex, noLeadTrailSpacesRegex,
PortLimits, PortLimits,
ReportStrategyDefaultValue,
} from '@home/components/widget/lib/gateway/gateway-widget.models'; } from '@home/components/widget/lib/gateway/gateway-widget.models';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { DialogComponent } from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
@ -64,6 +65,7 @@ export abstract class ModbusSlaveDialogAbstract<Component, Config> extends Dialo
readonly ModbusParityLabelsMap = ModbusParityLabelsMap; readonly ModbusParityLabelsMap = ModbusParityLabelsMap;
readonly ModbusProtocolLabelsMap = ModbusProtocolLabelsMap; readonly ModbusProtocolLabelsMap = ModbusProtocolLabelsMap;
readonly ModbusMethodLabelsMap = ModbusMethodLabelsMap; readonly ModbusMethodLabelsMap = ModbusMethodLabelsMap;
readonly ReportStrategyDefaultValue = ReportStrategyDefaultValue;
readonly modbusHelpLink = readonly modbusHelpLink =
helpBaseUrl + '/docs/iot-gateway/config/modbus/#section-master-description-and-configuration-parameters'; helpBaseUrl + '/docs/iot-gateway/config/modbus/#section-master-description-and-configuration-parameters';

View File

@ -218,7 +218,7 @@
</mat-slide-toggle> </mat-slide-toggle>
</div> </div>
<ng-template #reportStrategy> <ng-template #reportStrategy>
<tb-report-strategy formControlName="reportStrategy" [isExpansionMode]="true"/> <tb-report-strategy [defaultValue]="ReportStrategyDefaultValue.Device" formControlName="reportStrategy" [isExpansionMode]="true"/>
</ng-template> </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">

View File

@ -34,6 +34,7 @@ import {
} from '@angular/forms'; } from '@angular/forms';
import { import {
ReportStrategyConfig, ReportStrategyConfig,
ReportStrategyDefaultValue,
ReportStrategyType, ReportStrategyType,
ReportStrategyTypeTranslationsMap ReportStrategyTypeTranslationsMap
} from '@home/components/widget/lib/gateway/gateway-widget.models'; } from '@home/components/widget/lib/gateway/gateway-widget.models';
@ -43,7 +44,7 @@ import { CommonModule } from '@angular/common';
import { import {
ModbusSecurityConfigComponent ModbusSecurityConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component'; } from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component';
import { coerceBoolean } from '@shared/decorators/coercion'; import { coerceBoolean, coerceNumber } from '@shared/decorators/coercion';
@Component({ @Component({
selector: 'tb-report-strategy', selector: 'tb-report-strategy',
@ -73,6 +74,9 @@ export class ReportStrategyComponent implements ControlValueAccessor, OnDestroy
@coerceBoolean() @coerceBoolean()
@Input() isExpansionMode = false; @Input() isExpansionMode = false;
@coerceNumber()
@Input() defaultValue = ReportStrategyDefaultValue.Key;
reportStrategyFormGroup: UntypedFormGroup; reportStrategyFormGroup: UntypedFormGroup;
showStrategyControl: FormControl<boolean>; showStrategyControl: FormControl<boolean>;
@ -90,7 +94,7 @@ export class ReportStrategyComponent implements ControlValueAccessor, OnDestroy
this.reportStrategyFormGroup = this.fb.group({ this.reportStrategyFormGroup = this.fb.group({
type: [{ value: ReportStrategyType.OnReportPeriod, disabled: true }, []], type: [{ value: ReportStrategyType.OnReportPeriod, disabled: true }, []],
reportPeriod: [{ value: 5000, disabled: true }, [Validators.required]], reportPeriod: [{ value: this.defaultValue, disabled: true }, [Validators.required]],
}); });
this.observeStrategyFormChange(); this.observeStrategyFormChange();
@ -109,7 +113,7 @@ export class ReportStrategyComponent implements ControlValueAccessor, OnDestroy
if (reportStrategyConfig) { if (reportStrategyConfig) {
this.reportStrategyFormGroup.enable({emitEvent: false}); this.reportStrategyFormGroup.enable({emitEvent: false});
} }
const { type = ReportStrategyType.OnReportPeriod, reportPeriod = 5000 } = reportStrategyConfig ?? {}; const { type = ReportStrategyType.OnReportPeriod, reportPeriod = this.defaultValue } = reportStrategyConfig ?? {};
this.reportStrategyFormGroup.setValue({ type, reportPeriod }, {emitEvent: false}); this.reportStrategyFormGroup.setValue({ type, reportPeriod }, {emitEvent: false});
this.onTypeChange(type); this.onTypeChange(type);
} }

View File

@ -23,7 +23,7 @@
<button *ngIf="dataSource?.data?.length" <button *ngIf="dataSource?.data?.length"
mat-icon-button mat-icon-button
[disabled]="isLoading$ | async" [disabled]="isLoading$ | async"
(click)="addConnector($event)" (click)="onAddConnector($event)"
matTooltip="{{ 'action.add' | translate }}" matTooltip="{{ 'action.add' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
@ -33,7 +33,7 @@
<section *ngIf="!dataSource?.data?.length" fxLayoutAlign="center center" <section *ngIf="!dataSource?.data?.length" fxLayoutAlign="center center"
class="mat-headline-5 tb-absolute-fill tb-add-new"> class="mat-headline-5 tb-absolute-fill tb-add-new">
<button mat-button class="connector" <button mat-button class="connector"
(click)="addConnector($event)"> (click)="onAddConnector($event)">
<mat-icon class="tb-mat-96">add</mat-icon> <mat-icon class="tb-mat-96">add</mat-icon>
<span>{{ 'gateway.add-connector' | translate }}</span> <span>{{ 'gateway.add-connector' | translate }}</span>
</button> </button>
@ -244,7 +244,7 @@
<button mat-raised-button color="primary" <button mat-raised-button color="primary"
type="button" type="button"
[disabled]="!connectorForm.dirty || connectorForm.invalid" [disabled]="!connectorForm.dirty || connectorForm.invalid"
(click)="saveConnector(false)"> (click)="onSaveConnector()">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</button> </button>
</div> </div>
@ -315,6 +315,7 @@
</mat-slide-toggle> </mat-slide-toggle>
</div> </div>
<tb-report-strategy <tb-report-strategy
[defaultValue]="ReportStrategyDefaultValue.Connector"
*ngIf="connectorForm.get('type').value === ConnectorType.MODBUS && connectorForm.get('configVersion').value === GatewayVersion.Current" *ngIf="connectorForm.get('type').value === ConnectorType.MODBUS && connectorForm.get('configVersion').value === GatewayVersion.Current"
formControlName="reportStrategy" formControlName="reportStrategy"
/> />

View File

@ -42,7 +42,7 @@ import { MatTableDataSource } from '@angular/material/table';
import { ActionNotificationShow } from '@core/notification/notification.actions'; import { ActionNotificationShow } from '@core/notification/notification.actions';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { WidgetContext } from '@home/models/widget-component.models'; import { WidgetContext } from '@home/models/widget-component.models';
import { camelCase, deepClone, generateSecret, isEqual, isString } from '@core/utils'; import { camelCase, deepClone, isEqual, isString } from '@core/utils';
import { NULL_UUID } from '@shared/models/id/has-uuid'; import { NULL_UUID } from '@shared/models/id/has-uuid';
import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models';
import { DatasourceType, widgetType } from '@shared/models/widget.models'; import { DatasourceType, widgetType } from '@shared/models/widget.models';
@ -60,6 +60,8 @@ import {
GatewayLogLevel, GatewayLogLevel,
noLeadTrailSpacesRegex, noLeadTrailSpacesRegex,
GatewayVersion, GatewayVersion,
ReportStrategyDefaultValue,
ReportStrategyType,
} from './gateway-widget.models'; } from './gateway-widget.models';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { AddConnectorDialogComponent } from '@home/components/widget/lib/gateway/dialog/add-connector-dialog.component'; import { AddConnectorDialogComponent } from '@home/components/widget/lib/gateway/dialog/add-connector-dialog.component';
@ -103,6 +105,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
readonly GatewayConnectorTypesTranslatesMap = GatewayConnectorDefaultTypesTranslatesMap; readonly GatewayConnectorTypesTranslatesMap = GatewayConnectorDefaultTypesTranslatesMap;
readonly ConnectorConfigurationModes = ConfigurationModes; readonly ConnectorConfigurationModes = ConfigurationModes;
readonly GatewayVersion = GatewayVersion; readonly GatewayVersion = GatewayVersion;
readonly ReportStrategyDefaultValue = ReportStrategyDefaultValue;
pageLink: PageLink; pageLink: PageLink;
dataSource: MatTableDataSource<GatewayAttributeData>; dataSource: MatTableDataSource<GatewayAttributeData>;
@ -170,18 +173,21 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
super.ngOnDestroy(); super.ngOnDestroy();
} }
saveConnector(isNew = true): void { onSaveConnector(): void {
const value = this.getConnectorData(); this.saveConnector(this.getUpdatedConnectorData(this.connectorForm.value), false);
}
private saveConnector(connector: GatewayConnector, isNew = true): void {
const scope = (isNew || this.activeConnectors.includes(this.initialConnector.name)) const scope = (isNew || this.activeConnectors.includes(this.initialConnector.name))
? AttributeScope.SHARED_SCOPE ? AttributeScope.SHARED_SCOPE
: AttributeScope.SERVER_SCOPE; : AttributeScope.SERVER_SCOPE;
forkJoin(this.getEntityAttributeTasks(value, scope)).pipe(take(1)).subscribe(_ => { forkJoin(this.getEntityAttributeTasks(connector, scope)).pipe(take(1)).subscribe(_ => {
this.showToast(!this.initialConnector this.showToast(isNew
? this.translate.instant('gateway.connector-created') ? this.translate.instant('gateway.connector-created')
: this.translate.instant('gateway.connector-updated') : this.translate.instant('gateway.connector-updated')
); );
this.initialConnector = value; this.initialConnector = connector;
this.updateData(true); this.updateData(true);
this.connectorForm.markAsPristine(); this.connectorForm.markAsPristine();
}); });
@ -237,8 +243,8 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
} }
} }
private getConnectorData(): GatewayConnector { private getUpdatedConnectorData(connector: GatewayConnector): GatewayConnector {
const value = { ...this.connectorForm.value }; const value = {...connector };
value.configuration = `${camelCase(value.name)}.json`; value.configuration = `${camelCase(value.name)}.json`;
delete value.basicConfig; delete value.basicConfig;
@ -249,6 +255,16 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
delete value.class; delete value.class;
} }
if (value.type === ConnectorType.MODBUS && value.configVersion === GatewayVersion.Current) {
if (!value.reportStrategy) {
value.reportStrategy = {
type: ReportStrategyType.OnReportPeriod,
reportPeriod: ReportStrategyDefaultValue.Connector
};
delete value.sendDataOnlyOnChange;
}
}
if (this.gatewayVersion && !value.configVersion) { if (this.gatewayVersion && !value.configVersion) {
value.configVersion = this.gatewayVersion; value.configVersion = this.gatewayVersion;
} }
@ -472,7 +488,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
return (connector && this.activeConnectors.includes(connectorName)) ? (connector.data[0][1] || 0) : 'Inactive'; return (connector && this.activeConnectors.includes(connectorName)) ? (connector.data[0][1] || 0) : 'Inactive';
} }
addConnector(event?: Event): void { onAddConnector(event?: Event): void {
event?.stopPropagation(); event?.stopPropagation();
this.confirmConnectorChange() this.confirmConnectorChange()
@ -482,25 +498,42 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
switchMap(() => this.openAddConnectorDialog()), switchMap(() => this.openAddConnectorDialog()),
filter(Boolean), filter(Boolean),
) )
.subscribe(value => { .subscribe(connector => this.addConnector(connector));
}
private addConnector(connector: GatewayConnector): void {
if (this.connectorForm.disabled) { if (this.connectorForm.disabled) {
this.connectorForm.enable(); this.connectorForm.enable();
} }
if (!value.configurationJson) { if (!connector.configurationJson) {
value.configurationJson = {} as ConnectorBaseConfig; connector.configurationJson = {} as ConnectorBaseConfig;
} }
value.basicConfig = value.configurationJson; connector.basicConfig = connector.configurationJson;
this.initialConnector = value; this.initialConnector = connector;
this.connectorForm.patchValue(value, {emitEvent: false});
this.generate('basicConfig.broker.clientId'); const previousType = this.connectorForm.get('type').value;
if (this.connectorForm.get('type').value === value.type || !this.allowBasicConfig.has(value.type)) {
this.saveConnector(); this.setInitialConnectorValues(connector);
this.saveConnector(this.getUpdatedConnectorData(connector));
if (!previousType || previousType === connector.type || !this.allowBasicConfig.has(connector.type)) {
this.patchBasicConfigConnector(connector);
} else { } else {
this.basicConfigInitSubject.pipe(take(1)).subscribe(() => { this.basicConfigInitSubject.pipe(take(1)).subscribe(() => {
this.saveConnector(); this.patchBasicConfigConnector(connector);
}); });
} }
}); }
private setInitialConnectorValues(connector: GatewayConnector): void {
this.toggleReportStrategy(connector.type);
this.connectorForm.get('mode').setValue(this.allowBasicConfig.has(connector.type)
? connector.mode ?? ConfigurationModes.BASIC
: null, {emitEvent: false}
);
this.connectorForm.get('configVersion').setValue(connector.configVersion, {emitEvent: false});
this.connectorForm.get('type').setValue(connector.type, {emitEvent: false});
} }
private openAddConnectorDialog(): Observable<GatewayConnector> { private openAddConnectorDialog(): Observable<GatewayConnector> {
@ -514,10 +547,6 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
}).afterClosed(); }).afterClosed();
} }
generate(formControlName: string): void {
this.connectorForm.get(formControlName)?.patchValue('tb_gw_' + generateSecret(5));
}
uniqNameRequired(): ValidatorFn { uniqNameRequired(): ValidatorFn {
return (control: UntypedFormControl) => { return (control: UntypedFormControl) => {
const newName = control.value?.trim().toLowerCase(); const newName = control.value?.trim().toLowerCase();
@ -607,7 +636,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
const active = attributes.find(data => data.key === 'active').value; const active = attributes.find(data => data.key === 'active').value;
const lastDisconnectedTime = attributes.find(data => data.key === 'lastDisconnectTime')?.value; const lastDisconnectedTime = attributes.find(data => data.key === 'lastDisconnectTime')?.value;
const lastConnectedTime = attributes.find(data => data.key === 'lastConnectTime').value; const lastConnectedTime = attributes.find(data => data.key === 'lastConnectTime')?.value;
this.isGatewayActive = this.getGatewayStatus(active, lastConnectedTime, lastDisconnectedTime); this.isGatewayActive = this.getGatewayStatus(active, lastConnectedTime, lastDisconnectedTime);
}); });
@ -804,7 +833,6 @@ 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:
@ -819,8 +847,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
} }
private updateBasicConfigConnector(connector: GatewayConnector): void { private updateBasicConfigConnector(connector: GatewayConnector): void {
this.connectorForm.get('mode').setValue(connector.mode || ConfigurationModes.BASIC, {emitEvent: false}); this.setInitialConnectorValues(connector);
this.connectorForm.get('configVersion').setValue(connector.configVersion, {emitEvent: false});
if ((!connector.mode || connector.mode === ConfigurationModes.BASIC) && this.connectorForm.get('type').value !== connector.type) { if ((!connector.mode || connector.mode === ConfigurationModes.BASIC) && this.connectorForm.get('type').value !== connector.type) {
this.basicConfigInitSubject.asObservable().pipe(take(1)).subscribe(() => { this.basicConfigInitSubject.asObservable().pipe(take(1)).subscribe(() => {
this.patchBasicConfigConnector(connector); this.patchBasicConfigConnector(connector);

View File

@ -123,6 +123,9 @@ export interface GatewayConnectorBase {
class?: string; class?: string;
mode?: ConfigurationModes; mode?: ConfigurationModes;
configVersion?: string; configVersion?: string;
reportStrategy?: ReportStrategyConfig;
sendDataOnlyOnChange?: boolean;
ts?: number;
} }
export interface GatewayConnector<BaseConfig = ConnectorBaseConfig> extends GatewayConnectorBase { export interface GatewayConnector<BaseConfig = ConnectorBaseConfig> extends GatewayConnectorBase {
@ -645,6 +648,12 @@ export enum ReportStrategyType {
OnChangeOrReportPeriod = 'ON_CHANGE_OR_REPORT_PERIOD' OnChangeOrReportPeriod = 'ON_CHANGE_OR_REPORT_PERIOD'
} }
export enum ReportStrategyDefaultValue {
Connector = 60000,
Device = 30000,
Key = 15000
}
export const ReportStrategyTypeTranslationsMap = new Map<ReportStrategyType, string>( export const ReportStrategyTypeTranslationsMap = new Map<ReportStrategyType, string>(
[ [
[ReportStrategyType.OnChange, 'gateway.report-strategy.on-change'], [ReportStrategyType.OnChange, 'gateway.report-strategy.on-change'],

View File

@ -2955,7 +2955,6 @@
"configuration-delete-dialog-confirm": "Turn Off", "configuration-delete-dialog-confirm": "Turn Off",
"connector-duplicate-name": "Connector with such name already exists.", "connector-duplicate-name": "Connector with such name already exists.",
"connector-side": "Connector side", "connector-side": "Connector side",
"client-communication-type": "Client communication type",
"payload-type": "Payload type", "payload-type": "Payload type",
"platform-side": "Platform side", "platform-side": "Platform side",
"JSON": "JSON", "JSON": "JSON",