Merge branch 'rc'

This commit is contained in:
Igor Kulikov 2024-09-26 11:25:42 +03:00
commit a37ce176b2
24 changed files with 215 additions and 101 deletions

View File

@ -11,7 +11,7 @@
"resources": [],
"templateHtml": "<tb-gateway-connector [device]=\"entityId\" *ngIf=\"entityId\" [ctx]=\"ctx\"></tb-gateway-connector>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n if (self.ctx.datasources && self.ctx.datasources.length) {\n self.ctx.$scope.entityId = self.ctx.datasources[0].entity.id;\n }\n};\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n singleEntity: true\n };\n}",
"controllerScript": "self.onInit = function() {\n if (self.ctx.datasources && self.ctx.datasources.length) {\n self.ctx.$scope.entityId = self.ctx.datasources[0].entity.id;\n }\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.gatewayConnectors?.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n singleEntity: true\n };\n}",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Gateway connectors\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":500},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}"

View File

@ -360,6 +360,30 @@
"color": "#2196f3",
"settings": {},
"_hash": 0.7454705362378311
},
{
"name": "lastConnectTime",
"type": "attribute",
"label": "lastConnectTime",
"color": "#4caf50",
"settings": {},
"_hash": 0.7249585632235194
},
{
"name": "lastDisconnectTime",
"type": "attribute",
"label": "lastDisconnectTime",
"color": "#f44336",
"settings": {},
"_hash": 0.9812430092707332
},
{
"name": "active",
"type": "attribute",
"label": "active",
"color": "#ffc107",
"settings": {},
"_hash": 0.9216097189544408
}
]
}

View File

@ -14,16 +14,17 @@
/// limitations under the License.
///
import { Directive, inject, Input, OnDestroy, TemplateRef } from '@angular/core';
import { AfterViewInit, Directive, EventEmitter, inject, Input, OnDestroy, Output, TemplateRef } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, ValidationErrors, Validator } from '@angular/forms';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Directive()
export abstract class GatewayConnectorBasicConfigDirective<InputBasicConfig, OutputBasicConfig>
implements ControlValueAccessor, Validator, OnDestroy {
implements AfterViewInit, ControlValueAccessor, Validator, OnDestroy {
@Input() generalTabContent: TemplateRef<any>;
@Output() initialized = new EventEmitter<void>();
basicFormGroup: FormGroup;
@ -45,6 +46,10 @@ export abstract class GatewayConnectorBasicConfigDirective<InputBasicConfig, Out
this.destroy$.complete();
}
ngAfterViewInit(): void {
this.initialized.emit();
}
validate(): ValidationErrors | null {
return this.basicFormGroup.valid ? null : { basicFormGroup: { valid: false } };
}

View File

@ -23,14 +23,22 @@ export abstract class GatewayConnectorVersionProcessor<BasicConfig> {
protected constructor(protected gatewayVersionIn: string | number, protected connector: GatewayConnector<BasicConfig>) {
this.gatewayVersion = this.parseVersion(this.gatewayVersionIn);
this.configVersion = this.parseVersion(connector.configVersion);
this.configVersion = this.parseVersion(this.connector.configVersion);
}
getProcessedByVersion(): GatewayConnector<BasicConfig> {
if (this.isVersionUpdateNeeded()) {
return this.isVersionUpgradeNeeded()
? this.getUpgradedVersion()
: this.getDowngradedVersion();
if (!this.isVersionUpdateNeeded()) {
return this.connector;
}
return this.processVersionUpdate();
}
private processVersionUpdate(): GatewayConnector<BasicConfig> {
if (this.isVersionUpgradeNeeded()) {
return this.getUpgradedVersion();
} else if (this.isVersionDowngradeNeeded()) {
return this.getDowngradedVersion();
}
return this.connector;
@ -48,6 +56,10 @@ export abstract class GatewayConnectorVersionProcessor<BasicConfig> {
return this.gatewayVersionIn === GatewayVersion.Current && (!this.configVersion || this.configVersion < this.gatewayVersion);
}
private isVersionDowngradeNeeded(): boolean {
return this.configVersion && this.connector.configVersion === GatewayVersion.Current && (this.configVersion > this.gatewayVersion);
}
private parseVersion(version: string | number): number {
if (isNumber(version)) {
return version as number;

View File

@ -38,7 +38,7 @@ export class OpcVersionProcessor extends GatewayConnectorVersionProcessor<OPCBas
...this.connector,
configurationJson: {
server: server ? OpcVersionMappingUtil.mapServerToUpgradedVersion(server) : {},
mapping: server.mapping ? OpcVersionMappingUtil.mapMappingToUpgradedVersion(server.mapping) : [],
mapping: server?.mapping ? OpcVersionMappingUtil.mapMappingToUpgradedVersion(server.mapping) : [],
},
configVersion: this.gatewayVersionIn
} as GatewayConnector<OPCBasicConfig_v3_5_2>;

View File

@ -693,7 +693,7 @@
</mat-form-field>
<mat-form-field appearance="outline" class="flex">
<mat-label translate>gateway.inactivity-check-period-seconds</mat-label>
<input matInput formControlName="inactivityCheckPeriodSeconds"/>
<input matInput type="number" min="0" formControlName="inactivityCheckPeriodSeconds"/>
<mat-error
*ngIf="basicFormGroup.get('thingsboard.checkingDeviceActivity.inactivityCheckPeriodSeconds').hasError('required')">
{{ 'gateway.inactivity-check-period-seconds-required' | translate }}

View File

@ -502,14 +502,14 @@ export class GatewayBasicConfigurationComponent implements OnDestroy, ControlVal
}
private addFileStorageValidators(group: FormGroup): void {
['data_folder_path', 'max_file_count', 'max_read_records_count', 'max_records_per_file'].forEach(field => {
['max_file_count', 'max_read_records_count', 'max_records_per_file'].forEach(field => {
group.get(field).addValidators([Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]);
group.get(field).updateValueAndValidity({ emitEvent: false });
});
}
private addSqliteStorageValidators(group: FormGroup): void {
['data_file_path', 'messages_ttl_check_in_hours', 'messages_ttl_in_days'].forEach(field => {
['messages_ttl_check_in_hours', 'messages_ttl_in_days'].forEach(field => {
group.get(field).addValidators([Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]);
group.get(field).updateValueAndValidity({ emitEvent: false });
});

View File

@ -242,7 +242,7 @@ export class GatewayConfigurationComponent implements AfterViewInit, OnDestroy {
consoleHandler: {
class: 'logging.StreamHandler',
formatter: 'LogFormatter',
level: 'DEBUG',
level: 0,
stream: 'ext://sys.stdout'
},
databaseHandler: {

View File

@ -128,7 +128,7 @@ interface LogFormatterConfig {
interface StreamHandlerConfig {
class: string;
formatter: string;
level: string;
level: string | number;
stream: string;
}

View File

@ -26,10 +26,13 @@
<mat-expansion-panel class="tb-settings" [expanded]="last">
<mat-expansion-panel-header fxLayout="row wrap">
<mat-panel-title>
<div class="title-container" *ngIf="keysType !== MappingKeysType.RPC_METHODS">
{{ keyControl.get('key').value }}{{ '-' }}
</div>
<div class="title-container">{{ valueTitle(keyControl) }}</div>
<ng-container *ngIf="keysType !== MappingKeysType.RPC_METHODS">
<div tbTruncateWithTooltip class="title-container">
{{ keyControl.get('key').value }}
</div>
{{ '-' }}
</ng-container>
<div tbTruncateWithTooltip class="title-container">{{ valueTitle(keyControl) }}</div>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>

View File

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

View File

@ -113,7 +113,6 @@ export abstract class ModbusSlaveDialogAbstract<Component, Config> extends Dialo
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)]],

View File

@ -27,23 +27,6 @@
</button>
</mat-toolbar>
<div mat-dialog-content [formGroup]="slaveConfigFormGroup" class="tb-form-panel">
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
<div class="fixed-title-width slave-name-label tb-required" translate>gateway.name</div>
<div class="tb-flex no-gap">
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
<input matInput name="value" formControlName="name" placeholder="{{ 'gateway.set' | translate }}"/>
<mat-icon matSuffix
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="('gateway.name-required') | translate"
*ngIf="slaveConfigFormGroup.get('name').hasError('required') &&
slaveConfigFormGroup.get('name').touched"
class="tb-error">
warning
</mat-icon>
</mat-form-field>
</div>
</div>
<div class="stroked tb-form-panel">
<div class="tb-form-panel no-border no-padding padding-top">
<div class="tb-flex row space-between align-center no-gap fill-width">

View File

@ -17,11 +17,14 @@
import { Directive } from '@angular/core';
import { FormGroup } from '@angular/forms';
import {
BrokerConfig,
MappingType,
MQTTBasicConfig, MQTTBasicConfig_v3_5_2,
MQTTBasicConfig,
MQTTBasicConfig_v3_5_2,
RequestMappingData,
RequestMappingValue,
RequestType
RequestType,
WorkersConfig
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { isObject } from '@core/utils';
import {
@ -73,6 +76,14 @@ export abstract class MqttBasicConfigDirective<BasicConfig>
});
}
protected getBrokerMappedValue(broker: BrokerConfig, workers: WorkersConfig): BrokerConfig {
return {
...broker,
maxNumberOfWorkers: workers.maxNumberOfWorkers ?? 100,
maxMessageNumberPerWorker: workers.maxMessageNumberPerWorker ?? 10,
};
}
writeValue(basicConfig: BasicConfig): void {
this.basicFormGroup.setValue(this.mapConfigToFormValue(basicConfig), { emitEvent: false });
}

View File

@ -26,7 +26,6 @@ import {
import {
MqttBasicConfigDirective
} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.abstract';
import { isDefinedAndNotNull } from '@core/utils';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module';
import {
@ -85,19 +84,14 @@ export class MqttBasicConfigComponent extends MqttBasicConfigDirective<MQTTBasic
}
protected override getMappedValue(basicConfig: MQTTBasicConfig_v3_5_2): MQTTBasicConfig_v3_5_2 {
let { broker, workers, mapping, requestsMapping } = basicConfig || {};
const { broker, workers, mapping, requestsMapping } = basicConfig || {};
if (isDefinedAndNotNull(workers.maxNumberOfWorkers) || isDefinedAndNotNull(workers.maxMessageNumberPerWorker)) {
broker = {
...broker,
...workers,
};
}
if ((requestsMapping as RequestMappingData[])?.length) {
requestsMapping = this.getRequestDataObject(requestsMapping as RequestMappingValue[]);
}
return { broker, mapping, requestsMapping };
return {
broker: this.getBrokerMappedValue(broker, workers),
mapping,
requestsMapping: (requestsMapping as RequestMappingData[])?.length
? this.getRequestDataObject(requestsMapping as RequestMappingValue[])
: {} as Record<RequestType, RequestMappingValue[]>
};
}
}

View File

@ -102,23 +102,16 @@ export class MqttLegacyBasicConfigComponent extends MqttBasicConfigDirective<MQT
}
protected override getMappedValue(basicConfig: MQTTBasicConfig_v3_5_2): MQTTLegacyBasicConfig {
let { broker, workers, mapping, requestsMapping } = basicConfig || {};
const { broker, workers, mapping, requestsMapping } = basicConfig || {};
if (isDefinedAndNotNull(workers.maxNumberOfWorkers) || isDefinedAndNotNull(workers.maxMessageNumberPerWorker)) {
broker = {
...broker,
...workers,
};
}
if ((requestsMapping as RequestMappingData[])?.length) {
requestsMapping = this.getRequestDataObject(requestsMapping as RequestMappingValue[]);
}
const updatedRequestMapping = (requestsMapping as RequestMappingData[])?.length
? this.getRequestDataObject(requestsMapping as RequestMappingValue[])
: {} as Record<RequestType, RequestMappingData[]>;
return {
broker,
broker: this.getBrokerMappedValue(broker, workers),
mapping: MqttVersionMappingUtil.mapMappingToDowngradedVersion(mapping),
...(MqttVersionMappingUtil.mapRequestsToDowngradedVersion(requestsMapping as Record<RequestType, RequestMappingData[]>))
...(MqttVersionMappingUtil.mapRequestsToDowngradedVersion(updatedRequestMapping as Record<RequestType, RequestMappingData[]>))
};
}
}

View File

@ -70,8 +70,8 @@
matTooltipPosition="above"
matTooltipClass="tb-error-tooltip"
[matTooltip]="('gateway.value-required') | translate"
*ngIf="keyControl.get(keyControl.get('value').value).hasError('required')
&& keyControl.get(keyControl.get('value').value).touched"
*ngIf="keyControl.get(keyControl.get('type').value).hasError('required')
&& keyControl.get(keyControl.get('type').value).touched"
class="tb-error">
warning
</mat-icon>

View File

@ -181,9 +181,14 @@
*ngIf="connectorForm.get('configVersion').value === GatewayVersion.Current else legacy"
formControlName="basicConfig"
[generalTabContent]="generalTabContent"
(initialized)="basicConfigInitSubject.next()"
/>
<ng-template #legacy>
<tb-mqtt-legacy-basic-config formControlName="basicConfig" [generalTabContent]="generalTabContent"/>
<tb-mqtt-legacy-basic-config
(initialized)="basicConfigInitSubject.next()"
formControlName="basicConfig"
[generalTabContent]="generalTabContent"
/>
</ng-template>
</ng-container>
<ng-container *ngSwitchCase="ConnectorType.OPCUA">
@ -191,9 +196,14 @@
*ngIf="connectorForm.get('configVersion').value === GatewayVersion.Current else legacy"
formControlName="basicConfig"
[generalTabContent]="generalTabContent"
(initialized)="basicConfigInitSubject.next()"
/>
<ng-template #legacy>
<tb-opc-ua-legacy-basic-config formControlName="basicConfig" [generalTabContent]="generalTabContent"/>
<tb-opc-ua-legacy-basic-config
(initialized)="basicConfigInitSubject.next()"
formControlName="basicConfig"
[generalTabContent]="generalTabContent"
/>
</ng-template>
</ng-container>
<ng-container *ngSwitchCase="ConnectorType.MODBUS">
@ -201,9 +211,14 @@
*ngIf="connectorForm.get('configVersion').value === GatewayVersion.Current else legacy"
formControlName="basicConfig"
[generalTabContent]="generalTabContent"
(initialized)="basicConfigInitSubject.next()"
/>
<ng-template #legacy>
<tb-modbus-legacy-basic-config formControlName="basicConfig" [generalTabContent]="generalTabContent"/>
<tb-modbus-legacy-basic-config
formControlName="basicConfig"
(initialized)="basicConfigInitSubject.next()"
[generalTabContent]="generalTabContent"
/>
</ng-template>
</ng-container>
</ng-container>
@ -229,7 +244,7 @@
<button mat-raised-button color="primary"
type="button"
[disabled]="!connectorForm.dirty || connectorForm.invalid"
(click)="saveConnector()">
(click)="saveConnector(false)">
{{ 'action.save' | translate }}
</button>
</div>

View File

@ -31,7 +31,7 @@ import { EntityId } from '@shared/models/id/entity-id';
import { AttributeService } from '@core/http/attribute.service';
import { TranslateService } from '@ngx-translate/core';
import { forkJoin, Observable, of, Subject, Subscription } from 'rxjs';
import { AttributeScope } from '@shared/models/telemetry/telemetry.models';
import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models';
import { PageComponent } from '@shared/components/page.component';
import { PageLink } from '@shared/models/page/page-link';
import { AttributeDatasource } from '@home/models/datasource/attribute-datasource';
@ -110,8 +110,10 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
activeConnectors: Array<string>;
mode: ConfigurationModes = this.ConnectorConfigurationModes.BASIC;
initialConnector: GatewayConnector;
basicConfigInitSubject = new Subject<void>();
private gatewayVersion: string;
private isGatewayActive: boolean;
private inactiveConnectors: Array<string>;
private attributeDataSource: AttributeDatasource;
private inactiveConnectorsDataSource: AttributeDatasource;
@ -124,7 +126,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
private subscriptionOptions: WidgetSubscriptionOptions = {
callbacks: {
onDataUpdated: () => this.ctx.ngZone.run(() => {
this.onDataUpdated();
this.onErrorsUpdated();
}),
onDataUpdateError: (_, e) => this.ctx.ngZone.run(() => {
this.onDataUpdateError(e);
@ -155,8 +157,10 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
ngAfterViewInit(): void {
this.dataSource.sort = this.sort;
this.dataSource.sortingDataAccessor = this.getSortingDataAccessor();
this.ctx.$scope.gatewayConnectors = this;
this.loadConnectors();
this.loadGatewayState();
this.observeModeChange();
}
@ -166,9 +170,9 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
super.ngOnDestroy();
}
saveConnector(): void {
saveConnector(isNew = true): void {
const value = this.getConnectorData();
const scope = (!this.initialConnector || this.activeConnectors.includes(this.initialConnector.name))
const scope = (isNew || this.activeConnectors.includes(this.initialConnector.name))
? AttributeScope.SHARED_SCOPE
: AttributeScope.SERVER_SCOPE;
@ -275,7 +279,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
isConnectorSynced(attribute: GatewayAttributeData): boolean {
const connectorData = attribute.value;
if (!connectorData.ts || attribute.skipSync) {
if (!connectorData.ts || attribute.skipSync || !this.isGatewayActive) {
return false;
}
const clientIndex = this.activeData.findIndex(data => {
@ -479,7 +483,6 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
filter(Boolean),
)
.subscribe(value => {
this.initialConnector = null;
if (this.connectorForm.disabled) {
this.connectorForm.enable();
}
@ -487,9 +490,16 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
value.configurationJson = {} as ConnectorBaseConfig;
}
value.basicConfig = value.configurationJson;
this.updateConnector(value);
this.initialConnector = value;
this.connectorForm.patchValue(value, {emitEvent: false});
this.generate('basicConfig.broker.clientId');
setTimeout(() => this.saveConnector());
if (this.connectorForm.get('type').value === value.type || !this.allowBasicConfig.has(value.type)) {
this.saveConnector();
} else {
this.basicConfigInitSubject.pipe(take(1)).subscribe(() => {
this.saveConnector();
});
}
});
}
@ -590,6 +600,19 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
});
}
private loadGatewayState(): void {
this.attributeService.getEntityAttributes(this.device, AttributeScope.SERVER_SCOPE)
.pipe(takeUntil(this.destroy$))
.subscribe((attributes: AttributeData[]) => {
const active = attributes.find(data => data.key === 'active').value;
const lastDisconnectedTime = attributes.find(data => data.key === 'lastDisconnectTime')?.value;
const lastConnectedTime = attributes.find(data => data.key === 'lastConnectTime').value;
this.isGatewayActive = this.getGatewayStatus(active, lastConnectedTime, lastDisconnectedTime);
});
}
private parseConnectors(attribute: GatewayAttributeData[]): string[] {
const connectors = attribute?.[0]?.value || [];
return isString(connectors) ? JSON.parse(connectors) : connectors;
@ -598,7 +621,14 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
private observeModeChange(): void {
this.connectorForm.get('mode').valueChanges
.pipe(takeUntil(this.destroy$))
.subscribe(() => this.connectorForm.get('mode').markAsPristine());
.subscribe((mode) => {
this.connectorForm.get('mode').markAsPristine();
if (mode === ConfigurationModes.BASIC) {
this.basicConfigInitSubject.pipe(take(1)).subscribe(() => {
this.patchBasicConfigConnector({...this.initialConnector, mode: ConfigurationModes.BASIC});
});
}
});
}
private observeAttributeChange(): void {
@ -664,10 +694,29 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
console.error(errorText);
}
private onDataUpdated(): void {
private onErrorsUpdated(): void {
this.cd.detectChanges();
}
private onDataUpdated(): void {
const dataSources = this.ctx.defaultSubscription.data;
const active = dataSources.find(data => data.dataKey.name === 'active').data[0][1];
const lastDisconnectedTime = dataSources.find(data => data.dataKey.name === 'lastDisconnectTime').data[0][1];
const lastConnectedTime = dataSources.find(data => data.dataKey.name === 'lastConnectTime').data[0][1];
this.isGatewayActive = this.getGatewayStatus(active, lastConnectedTime, lastDisconnectedTime);
this.cd.detectChanges();
}
private getGatewayStatus(active: boolean, lastConnectedTime: number, lastDisconnectedTime: number): boolean {
if (!active) {
return false;
}
return !lastDisconnectedTime || lastConnectedTime > lastDisconnectedTime;
}
private generateSubscription(): void {
if (this.subscription) {
this.subscription.unsubscribe();
@ -760,13 +809,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
case ConnectorType.MQTT:
case ConnectorType.OPCUA:
case ConnectorType.MODBUS:
this.connectorForm.get('mode').setValue(connector.mode || ConfigurationModes.BASIC, {emitEvent: false});
this.connectorForm.get('configVersion').setValue(connector.configVersion, {emitEvent: false});
setTimeout(() => {
this.connectorForm.patchValue(connector, {emitEvent: false});
this.connectorForm.markAsPristine();
this.createBasicConfigWatcher();
});
this.updateBasicConfigConnector(connector);
break;
default:
this.connectorForm.patchValue({...connector, mode: null});
@ -775,6 +818,24 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
this.createJsonConfigWatcher();
}
private updateBasicConfigConnector(connector: GatewayConnector): void {
this.connectorForm.get('mode').setValue(connector.mode || ConfigurationModes.BASIC, {emitEvent: false});
this.connectorForm.get('configVersion').setValue(connector.configVersion, {emitEvent: false});
if ((!connector.mode || connector.mode === ConfigurationModes.BASIC) && this.connectorForm.get('type').value !== connector.type) {
this.basicConfigInitSubject.asObservable().pipe(take(1)).subscribe(() => {
this.patchBasicConfigConnector(connector);
});
} else {
this.patchBasicConfigConnector(connector);
}
}
private patchBasicConfigConnector(connector: GatewayConnector): void {
this.connectorForm.patchValue(connector, {emitEvent: false});
this.connectorForm.markAsPristine();
this.createBasicConfigWatcher();
}
private toggleReportStrategy(type: ConnectorType): void {
const reportStrategyControl = this.connectorForm.get('reportStrategy');
if (type === ConnectorType.MODBUS) {

View File

@ -711,7 +711,7 @@ export const HelpLinkByMappingTypeMap = new Map<MappingType, string>(
[
[MappingType.DATA, helpBaseUrl + '/docs/iot-gateway/config/mqtt/#section-mapping'],
[MappingType.OPCUA, helpBaseUrl + '/docs/iot-gateway/config/opc-ua/#section-mapping'],
[MappingType.REQUESTS, helpBaseUrl + '/docs/iot-gateway/config/mqtt/#section-mapping']
[MappingType.REQUESTS, helpBaseUrl + '/docs/iot-gateway/config/mqtt/#requests-mapping']
]
);

View File

@ -45,7 +45,7 @@ export class OpcVersionMappingUtil {
static mapServerToDowngradedVersion(config: OPCBasicConfig_v3_5_2): LegacyServerConfig {
const { mapping, server } = config;
const { enableSubscriptions, ...restServer } = server;
const { enableSubscriptions, ...restServer } = server ?? {} as ServerConfig;
return {
...restServer,
mapping: mapping ? this.mapMappingToDowngradedVersion(mapping) : [],

View File

@ -464,6 +464,12 @@ export function constructTableCssString(widgetConfig: WidgetConfig): string {
'.mat-mdc-table .mat-mdc-cell button.mat-mdc-icon-button[disabled][disabled] mat-icon {\n' +
'color: ' + mdDarkDisabled + ';\n' +
'}\n' +
'.mat-mdc-table .mat-mdc-cell button.mat-mdc-icon-button tb-icon {\n' +
'color: ' + mdDarkSecondary + ';\n' +
'}\n' +
'.mat-mdc-table .mat-mdc-cell button.mat-mdc-icon-button[disabled][disabled] tb-icon {\n' +
'color: ' + mdDarkDisabled + ';\n' +
'}\n' +
'.mat-divider {\n' +
'border-top-color: ' + mdDarkDivider + ';\n' +
'}\n' +

View File

@ -3036,6 +3036,7 @@
"grpc-max-pings-without-data-required": "Max pings without data is required",
"grpc-max-pings-without-data-min": "Max pings without data can not be less then 1",
"grpc-max-pings-without-data-pattern": "Max pings without data is not valid",
"info": "Info",
"identity": "Identity",
"inactivity-check-period-seconds": "Inactivity check period (in sec)",
"inactivity-check-period-seconds-required": "Inactivity check period is required",
@ -3317,7 +3318,7 @@
"check-connectors-configuration-min": "Check connectors configuration can not be less then 1",
"check-connectors-configuration-pattern": "Check connectors configuration is not valid",
"add": "Add command",
"timeout": "Timeout",
"timeout": "Timeout (in sec)",
"timeout-ms": "Timeout (in ms)",
"timeout-required": "Timeout is required",
"timeout-min": "Timeout can not be less then 1",

View File

@ -3,7 +3,6 @@
"master": {
"slaves": [
{
"name": "Slave 1",
"host": "127.0.0.1",
"port": 5021,
"type": "tcp",
@ -231,10 +230,10 @@
"attributes": [
{
"address": 5,
"type": "string",
"tag": "sm",
"type": "8int",
"tag": "coil",
"objectsCount": 1,
"value": "12"
"value": 0
}
],
"timeseries": [],