Merge pull request #11656 from maxunbearable/fix/4413-gateway-fixes

Gateway UI improvements and minor bug fixes
This commit is contained in:
Igor Kulikov 2024-09-20 16:15:06 +03:00 committed by GitHub
commit 8a5cc8cad5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 54 additions and 38 deletions

View File

@ -74,8 +74,8 @@ export class MqttVersionProcessor extends GatewayConnectorVersionProcessor<MQTTB
getDowngradedVersion(): GatewayConnector<MQTTLegacyBasicConfig> { getDowngradedVersion(): GatewayConnector<MQTTLegacyBasicConfig> {
const { requestsMapping, mapping, ...restConfig } = this.connector.configurationJson as MQTTBasicConfig_v3_5_2; const { requestsMapping, mapping, ...restConfig } = this.connector.configurationJson as MQTTBasicConfig_v3_5_2;
const updatedRequestsMapping = const updatedRequestsMapping = requestsMapping
MqttVersionMappingUtil.mapRequestsToDowngradedVersion(requestsMapping as Record<RequestType, RequestMappingData[]>); ? MqttVersionMappingUtil.mapRequestsToDowngradedVersion(requestsMapping as Record<RequestType, RequestMappingData[]>) : {};
const updatedMapping = MqttVersionMappingUtil.mapMappingToDowngradedVersion(mapping); const updatedMapping = MqttVersionMappingUtil.mapMappingToDowngradedVersion(mapping);
return { return {

View File

@ -28,6 +28,7 @@ import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { SharedModule } from '@shared/shared.module'; import { SharedModule } from '@shared/shared.module';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { GatewayConfigValue } from '@home/components/widget/lib/gateway/configuration/models/gateway-configuration.models';
@Component({ @Component({
selector: 'tb-gateway-advanced-configuration', selector: 'tb-gateway-advanced-configuration',
@ -83,8 +84,8 @@ export class GatewayAdvancedConfigurationComponent implements OnDestroy, Control
this.onTouched = fn; this.onTouched = fn;
} }
writeValue(basicConfig: unknown): void { writeValue(advancedConfig: GatewayConfigValue): void {
this.advancedFormControl.reset(basicConfig, {emitEvent: false}); this.advancedFormControl.reset(advancedConfig, {emitEvent: false});
} }
validate(): ValidationErrors | null { validate(): ValidationErrors | null {

View File

@ -640,10 +640,11 @@
</mat-form-field> </mat-form-field>
</section> </section>
<button mat-icon-button (click)="removeCommandControl($index, $event)" <button mat-icon-button (click)="removeCommandControl($index, $event)"
class="tb-box-button"
[disabled]="!basicFormGroup.get('thingsboard.remoteConfiguration').value" [disabled]="!basicFormGroup.get('thingsboard.remoteConfiguration').value"
matTooltip="{{ 'gateway.statistics.remove' | translate }}" matTooltip="{{ 'gateway.statistics.remove' | translate }}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>close</mat-icon> <mat-icon>delete</mat-icon>
</button> </button>
</div> </div>
<button mat-stroked-button color="primary" <button mat-stroked-button color="primary"

View File

@ -20,7 +20,7 @@ import {
FormGroup, FormGroup,
} from '@angular/forms'; } from '@angular/forms';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { MatDialogRef } from '@angular/material/dialog';
import { AttributeService } from '@core/http/attribute.service'; import { AttributeService } from '@core/http/attribute.service';
import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models';
import { DeviceService } from '@core/http/device.service'; import { DeviceService } from '@core/http/device.service';
@ -40,7 +40,9 @@ import {
GatewayConfigSecurity, GatewayConfigSecurity,
GatewayConfigValue, GatewayConfigValue,
GatewayGeneralConfig, GatewayGeneralConfig,
GatewayGRPCConfig,
GatewayLogsConfig, GatewayLogsConfig,
GatewayStorageConfig,
LocalLogs, LocalLogs,
LogAttribute, LogAttribute,
LogConfig, LogConfig,
@ -113,7 +115,7 @@ export class GatewayConfigurationComponent implements AfterViewInit, OnDestroy {
this.gatewayConfigGroup.get('basicConfig').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => { this.gatewayConfigGroup.get('basicConfig').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => {
const advancedControl = this.gatewayConfigGroup.get('advancedConfig'); const advancedControl = this.gatewayConfigGroup.get('advancedConfig');
if (!isEqual(advancedControl.value, value)) { if (!isEqual(advancedControl.value, value) && this.gatewayConfigGroup.get('mode').value === ConfigurationModes.BASIC) {
advancedControl.patchValue(value, {emitEvent: false}); advancedControl.patchValue(value, {emitEvent: false});
} }
}); });
@ -121,7 +123,7 @@ export class GatewayConfigurationComponent implements AfterViewInit, OnDestroy {
this.gatewayConfigGroup.get('advancedConfig').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => { this.gatewayConfigGroup.get('advancedConfig').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => {
const basicControl = this.gatewayConfigGroup.get('basicConfig'); const basicControl = this.gatewayConfigGroup.get('basicConfig');
if (!isEqual(basicControl.value, value)) { if (!isEqual(basicControl.value, value) && this.gatewayConfigGroup.get('mode').value === ConfigurationModes.ADVANCED) {
basicControl.patchValue(value, {emitEvent: false}); basicControl.patchValue(value, {emitEvent: false});
} }
}); });
@ -320,10 +322,10 @@ export class GatewayConfigurationComponent implements AfterViewInit, OnDestroy {
private updateConfigs(attributes: AttributeData[]): void { private updateConfigs(attributes: AttributeData[]): void {
const formValue: GatewayConfigValue = { const formValue: GatewayConfigValue = {
thingsboard: null, thingsboard: {} as GatewayGeneralConfig,
grpc: null, grpc: {} as GatewayGRPCConfig,
logs: null, logs: {} as GatewayLogsConfig,
storage: null, storage: {} as GatewayStorageConfig,
mode: ConfigurationModes.BASIC mode: ConfigurationModes.BASIC
}; };

View File

@ -27,32 +27,36 @@ import { GatewayLogLevel } from '@home/components/widget/lib/gateway/gateway-for
export interface GatewayConfigValue { export interface GatewayConfigValue {
mode: ConfigurationModes; mode: ConfigurationModes;
thingsboard: GatewayGeneralConfig; thingsboard: GatewayGeneralConfig;
storage: { storage: GatewayStorageConfig;
type: StorageTypes; grpc: GatewayGRPCConfig;
read_records_count?: number;
max_records_count?: number;
data_folder_path?: string;
max_file_count?: number;
max_read_records_count?: number;
max_records_per_file?: number;
data_file_path?: string;
messages_ttl_check_in_hours?: number;
messages_ttl_in_days?: number;
};
grpc: {
enabled: boolean;
serverPort: number;
keepAliveTimeMs: number;
keepAliveTimeoutMs: number;
keepalivePermitWithoutCalls: boolean;
maxPingsWithoutData: number;
minTimeBetweenPingsMs: number;
minPingIntervalWithoutDataMs: number;
};
connectors?: GatewayConnector[]; connectors?: GatewayConnector[];
logs: GatewayLogsConfig; logs: GatewayLogsConfig;
} }
export interface GatewayGRPCConfig {
enabled: boolean;
serverPort: number;
keepAliveTimeMs: number;
keepAliveTimeoutMs: number;
keepalivePermitWithoutCalls: boolean;
maxPingsWithoutData: number;
minTimeBetweenPingsMs: number;
minPingIntervalWithoutDataMs: number;
}
export interface GatewayStorageConfig {
type: StorageTypes;
read_records_count?: number;
max_records_count?: number;
data_folder_path?: string;
max_file_count?: number;
max_read_records_count?: number;
max_records_per_file?: number;
data_file_path?: string;
messages_ttl_check_in_hours?: number;
messages_ttl_in_days?: number;
}
export interface GatewayGeneralConfig { export interface GatewayGeneralConfig {
host: string; host: string;
port: number; port: number;

View File

@ -256,10 +256,11 @@ export class MappingTableComponent implements ControlValueAccessor, Validator, A
private getMappingValue(value: ConnectorMapping): MappingValue { private getMappingValue(value: ConnectorMapping): MappingValue {
switch (this.mappingType) { switch (this.mappingType) {
case MappingType.DATA: case MappingType.DATA:
const converterType = ConvertorTypeTranslationsMap.get((value as ConverterConnectorMapping).converter?.type);
return { return {
topicFilter: (value as ConverterConnectorMapping).topicFilter, topicFilter: (value as ConverterConnectorMapping).topicFilter,
QoS: (value as ConverterConnectorMapping).subscriptionQos, QoS: (value as ConverterConnectorMapping).subscriptionQos,
converter: this.translate.instant(ConvertorTypeTranslationsMap.get((value as ConverterConnectorMapping).converter?.type) || '') converter: converterType ? this.translate.instant(converterType) : ''
}; };
case MappingType.REQUESTS: case MappingType.REQUESTS:
let details: string; let details: string;

View File

@ -54,7 +54,9 @@
</div> </div>
</div> </div>
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center"> <div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
<div class="fixed-title-width" tbTruncateWithTooltip translate>gateway.security</div> <div class="fixed-title-width" tb-hint-tooltip-icon="{{ 'gateway.hints.security-policy' | translate }}">
<div tbTruncateWithTooltip>{{ 'gateway.security-policy' | translate }}</div>
</div>
<div class="tb-flex no-gap"> <div class="tb-flex no-gap">
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic"> <mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="security"> <mat-select formControlName="security">

View File

@ -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, configVersion, ...sharedDataConfig } = sharedDataConfigJson;
const { const {
name: connectorName, name: connectorName,
id: connectorId, id: connectorId,
enableRemoteLogging: connectorEnableRemoteLogging, enableRemoteLogging: connectorEnableRemoteLogging,
logLevel: connectorLogLevel, logLevel: connectorLogLevel,
configVersion: connectorConfigVersion,
...connectorConfig ...connectorConfig
} = connectorDataConfigJson; } = connectorDataConfigJson;

View File

@ -208,6 +208,7 @@ export interface ConnectorBaseInfo {
id: string; id: string;
enableRemoteLogging: boolean; enableRemoteLogging: boolean;
logLevel: GatewayLogLevel; logLevel: GatewayLogLevel;
configVersion: string | number;
} }
export type MQTTBasicConfig = MQTTBasicConfig_v3_5_2 | MQTTLegacyBasicConfig; export type MQTTBasicConfig = MQTTBasicConfig_v3_5_2 | MQTTLegacyBasicConfig;

View File

@ -35,9 +35,10 @@ import {
export class OpcVersionMappingUtil { export class OpcVersionMappingUtil {
static mapServerToUpgradedVersion(server: LegacyServerConfig): ServerConfig { static mapServerToUpgradedVersion(server: LegacyServerConfig): ServerConfig {
const { mapping, disableSubscriptions, ...restServer } = server; const { mapping, disableSubscriptions, pollPeriodInMillis, ...restServer } = server;
return { return {
...restServer, ...restServer,
pollPeriodInMillis: pollPeriodInMillis ?? 5000,
enableSubscriptions: !disableSubscriptions, enableSubscriptions: !disableSubscriptions,
}; };
} }

View File

@ -3254,6 +3254,7 @@
"sub-check-period": "Subscription check period (ms)", "sub-check-period": "Subscription check period (ms)",
"sub-check-period-error": "Subscription check period should be at least {{min}} (ms).", "sub-check-period-error": "Subscription check period should be at least {{min}} (ms).",
"security": "Security", "security": "Security",
"security-policy": "Security policy",
"security-type": "Security type", "security-type": "Security type",
"security-types": { "security-types": {
"access-token": "Access Token", "access-token": "Access Token",
@ -3460,6 +3461,7 @@
"file": "Your data will be stored in separated files and will be saved even after the gateway restart.", "file": "Your data will be stored in separated files and will be saved even after the gateway restart.",
"sqlite": "Your data will be stored in file based database. And will be saved even after the gateway restart.", "sqlite": "Your data will be stored in file based database. And will be saved even after the gateway restart.",
"opc-timeout": "Timeout in milliseconds for connecting to OPC-UA server.", "opc-timeout": "Timeout in milliseconds for connecting to OPC-UA server.",
"security-policy": "Security Policy defines the security mechanisms to be applied.",
"scan-period": "Period in milliseconds to rescan the server.", "scan-period": "Period in milliseconds to rescan the server.",
"sub-check-period": "Period to check the subscriptions in the OPC-UA server.", "sub-check-period": "Period to check the subscriptions in the OPC-UA server.",
"enable-subscription": "If true - the gateway will subscribe to interesting nodes and wait for data update and if false - the gateway will rescan OPC-UA server every scanPeriodInMillis.", "enable-subscription": "If true - the gateway will subscribe to interesting nodes and wait for data update and if false - the gateway will rescan OPC-UA server every scanPeriodInMillis.",