Merge branch 'rc'
This commit is contained in:
		
						commit
						a37ce176b2
					
				@ -11,7 +11,7 @@
 | 
				
			|||||||
    "resources": [],
 | 
					    "resources": [],
 | 
				
			||||||
    "templateHtml": "<tb-gateway-connector [device]=\"entityId\" *ngIf=\"entityId\" [ctx]=\"ctx\"></tb-gateway-connector>",
 | 
					    "templateHtml": "<tb-gateway-connector [device]=\"entityId\" *ngIf=\"entityId\" [ctx]=\"ctx\"></tb-gateway-connector>",
 | 
				
			||||||
    "templateCss": "",
 | 
					    "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": "{}",
 | 
					    "settingsSchema": "{}",
 | 
				
			||||||
    "dataKeySettingsSchema": "{}\n",
 | 
					    "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}"
 | 
					    "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}"
 | 
				
			||||||
 | 
				
			|||||||
@ -360,6 +360,30 @@
 | 
				
			|||||||
                  "color": "#2196f3",
 | 
					                  "color": "#2196f3",
 | 
				
			||||||
                  "settings": {},
 | 
					                  "settings": {},
 | 
				
			||||||
                  "_hash": 0.7454705362378311
 | 
					                  "_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
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              ]
 | 
					              ]
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -14,16 +14,17 @@
 | 
				
			|||||||
/// limitations under the License.
 | 
					/// 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 { ControlValueAccessor, FormBuilder, FormGroup, ValidationErrors, Validator } from '@angular/forms';
 | 
				
			||||||
import { Subject } from 'rxjs';
 | 
					import { Subject } from 'rxjs';
 | 
				
			||||||
import { takeUntil } from 'rxjs/operators';
 | 
					import { takeUntil } from 'rxjs/operators';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Directive()
 | 
					@Directive()
 | 
				
			||||||
export abstract class GatewayConnectorBasicConfigDirective<InputBasicConfig, OutputBasicConfig>
 | 
					export abstract class GatewayConnectorBasicConfigDirective<InputBasicConfig, OutputBasicConfig>
 | 
				
			||||||
  implements ControlValueAccessor, Validator, OnDestroy {
 | 
					  implements AfterViewInit, ControlValueAccessor, Validator, OnDestroy {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input() generalTabContent: TemplateRef<any>;
 | 
					  @Input() generalTabContent: TemplateRef<any>;
 | 
				
			||||||
 | 
					  @Output() initialized = new EventEmitter<void>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  basicFormGroup: FormGroup;
 | 
					  basicFormGroup: FormGroup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -45,6 +46,10 @@ export abstract class GatewayConnectorBasicConfigDirective<InputBasicConfig, Out
 | 
				
			|||||||
    this.destroy$.complete();
 | 
					    this.destroy$.complete();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ngAfterViewInit(): void {
 | 
				
			||||||
 | 
					    this.initialized.emit();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  validate(): ValidationErrors | null {
 | 
					  validate(): ValidationErrors | null {
 | 
				
			||||||
    return this.basicFormGroup.valid ? null : { basicFormGroup: { valid: false } };
 | 
					    return this.basicFormGroup.valid ? null : { basicFormGroup: { valid: false } };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -23,14 +23,22 @@ export abstract class GatewayConnectorVersionProcessor<BasicConfig> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  protected constructor(protected gatewayVersionIn: string | number, protected connector: GatewayConnector<BasicConfig>) {
 | 
					  protected constructor(protected gatewayVersionIn: string | number, protected connector: GatewayConnector<BasicConfig>) {
 | 
				
			||||||
    this.gatewayVersion = this.parseVersion(this.gatewayVersionIn);
 | 
					    this.gatewayVersion = this.parseVersion(this.gatewayVersionIn);
 | 
				
			||||||
    this.configVersion = this.parseVersion(connector.configVersion);
 | 
					    this.configVersion = this.parseVersion(this.connector.configVersion);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getProcessedByVersion(): GatewayConnector<BasicConfig> {
 | 
					  getProcessedByVersion(): GatewayConnector<BasicConfig> {
 | 
				
			||||||
    if (this.isVersionUpdateNeeded()) {
 | 
					    if (!this.isVersionUpdateNeeded()) {
 | 
				
			||||||
      return this.isVersionUpgradeNeeded()
 | 
					      return this.connector;
 | 
				
			||||||
        ? this.getUpgradedVersion()
 | 
					    }
 | 
				
			||||||
        : this.getDowngradedVersion();
 | 
					
 | 
				
			||||||
 | 
					    return this.processVersionUpdate();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private processVersionUpdate(): GatewayConnector<BasicConfig> {
 | 
				
			||||||
 | 
					    if (this.isVersionUpgradeNeeded()) {
 | 
				
			||||||
 | 
					      return this.getUpgradedVersion();
 | 
				
			||||||
 | 
					    } else if (this.isVersionDowngradeNeeded()) {
 | 
				
			||||||
 | 
					      return this.getDowngradedVersion();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return this.connector;
 | 
					    return this.connector;
 | 
				
			||||||
@ -48,6 +56,10 @@ export abstract class GatewayConnectorVersionProcessor<BasicConfig> {
 | 
				
			|||||||
    return this.gatewayVersionIn === GatewayVersion.Current && (!this.configVersion || this.configVersion < this.gatewayVersion);
 | 
					    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 {
 | 
					  private parseVersion(version: string | number): number {
 | 
				
			||||||
    if (isNumber(version)) {
 | 
					    if (isNumber(version)) {
 | 
				
			||||||
      return version as number;
 | 
					      return version as number;
 | 
				
			||||||
 | 
				
			|||||||
@ -38,7 +38,7 @@ export class OpcVersionProcessor extends GatewayConnectorVersionProcessor<OPCBas
 | 
				
			|||||||
      ...this.connector,
 | 
					      ...this.connector,
 | 
				
			||||||
      configurationJson: {
 | 
					      configurationJson: {
 | 
				
			||||||
        server: server ? OpcVersionMappingUtil.mapServerToUpgradedVersion(server) : {},
 | 
					        server: server ? OpcVersionMappingUtil.mapServerToUpgradedVersion(server) : {},
 | 
				
			||||||
        mapping: server.mapping ? OpcVersionMappingUtil.mapMappingToUpgradedVersion(server.mapping) : [],
 | 
					        mapping: server?.mapping ? OpcVersionMappingUtil.mapMappingToUpgradedVersion(server.mapping) : [],
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      configVersion: this.gatewayVersionIn
 | 
					      configVersion: this.gatewayVersionIn
 | 
				
			||||||
    } as GatewayConnector<OPCBasicConfig_v3_5_2>;
 | 
					    } as GatewayConnector<OPCBasicConfig_v3_5_2>;
 | 
				
			||||||
 | 
				
			|||||||
@ -693,7 +693,7 @@
 | 
				
			|||||||
            </mat-form-field>
 | 
					            </mat-form-field>
 | 
				
			||||||
            <mat-form-field appearance="outline" class="flex">
 | 
					            <mat-form-field appearance="outline" class="flex">
 | 
				
			||||||
              <mat-label translate>gateway.inactivity-check-period-seconds</mat-label>
 | 
					              <mat-label translate>gateway.inactivity-check-period-seconds</mat-label>
 | 
				
			||||||
              <input matInput formControlName="inactivityCheckPeriodSeconds"/>
 | 
					              <input matInput type="number" min="0" formControlName="inactivityCheckPeriodSeconds"/>
 | 
				
			||||||
              <mat-error
 | 
					              <mat-error
 | 
				
			||||||
                *ngIf="basicFormGroup.get('thingsboard.checkingDeviceActivity.inactivityCheckPeriodSeconds').hasError('required')">
 | 
					                *ngIf="basicFormGroup.get('thingsboard.checkingDeviceActivity.inactivityCheckPeriodSeconds').hasError('required')">
 | 
				
			||||||
                {{ 'gateway.inactivity-check-period-seconds-required' | translate }}
 | 
					                {{ 'gateway.inactivity-check-period-seconds-required' | translate }}
 | 
				
			||||||
 | 
				
			|||||||
@ -502,14 +502,14 @@ export class GatewayBasicConfigurationComponent implements OnDestroy, ControlVal
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private addFileStorageValidators(group: FormGroup): void {
 | 
					  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).addValidators([Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]);
 | 
				
			||||||
      group.get(field).updateValueAndValidity({ emitEvent: false });
 | 
					      group.get(field).updateValueAndValidity({ emitEvent: false });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private addSqliteStorageValidators(group: FormGroup): void {
 | 
					  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).addValidators([Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]);
 | 
				
			||||||
      group.get(field).updateValueAndValidity({ emitEvent: false });
 | 
					      group.get(field).updateValueAndValidity({ emitEvent: false });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
				
			|||||||
@ -242,7 +242,7 @@ export class GatewayConfigurationComponent implements AfterViewInit, OnDestroy {
 | 
				
			|||||||
        consoleHandler: {
 | 
					        consoleHandler: {
 | 
				
			||||||
          class: 'logging.StreamHandler',
 | 
					          class: 'logging.StreamHandler',
 | 
				
			||||||
          formatter: 'LogFormatter',
 | 
					          formatter: 'LogFormatter',
 | 
				
			||||||
          level: 'DEBUG',
 | 
					          level: 0,
 | 
				
			||||||
          stream: 'ext://sys.stdout'
 | 
					          stream: 'ext://sys.stdout'
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        databaseHandler: {
 | 
					        databaseHandler: {
 | 
				
			||||||
 | 
				
			|||||||
@ -128,7 +128,7 @@ interface LogFormatterConfig {
 | 
				
			|||||||
interface StreamHandlerConfig {
 | 
					interface StreamHandlerConfig {
 | 
				
			||||||
  class: string;
 | 
					  class: string;
 | 
				
			||||||
  formatter: string;
 | 
					  formatter: string;
 | 
				
			||||||
  level: string;
 | 
					  level: string | number;
 | 
				
			||||||
  stream: string;
 | 
					  stream: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -26,10 +26,13 @@
 | 
				
			|||||||
            <mat-expansion-panel class="tb-settings" [expanded]="last">
 | 
					            <mat-expansion-panel class="tb-settings" [expanded]="last">
 | 
				
			||||||
              <mat-expansion-panel-header fxLayout="row wrap">
 | 
					              <mat-expansion-panel-header fxLayout="row wrap">
 | 
				
			||||||
                <mat-panel-title>
 | 
					                <mat-panel-title>
 | 
				
			||||||
                  <div class="title-container" *ngIf="keysType !== MappingKeysType.RPC_METHODS">
 | 
					                  <ng-container *ngIf="keysType !== MappingKeysType.RPC_METHODS">
 | 
				
			||||||
                    {{ keyControl.get('key').value }}{{ '-' }}
 | 
					                    <div tbTruncateWithTooltip class="title-container">
 | 
				
			||||||
                  </div>
 | 
					                      {{ keyControl.get('key').value }}
 | 
				
			||||||
                  <div class="title-container">{{ valueTitle(keyControl) }}</div>
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    {{ '-' }}
 | 
				
			||||||
 | 
					                  </ng-container>
 | 
				
			||||||
 | 
					                  <div tbTruncateWithTooltip class="title-container">{{ valueTitle(keyControl) }}</div>
 | 
				
			||||||
                </mat-panel-title>
 | 
					                </mat-panel-title>
 | 
				
			||||||
              </mat-expansion-panel-header>
 | 
					              </mat-expansion-panel-header>
 | 
				
			||||||
              <ng-template matExpansionPanelContent>
 | 
					              <ng-template matExpansionPanelContent>
 | 
				
			||||||
 | 
				
			|||||||
@ -62,17 +62,25 @@
 | 
				
			|||||||
    </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]="'name'">
 | 
					        <ng-container [matColumnDef]="'info'">
 | 
				
			||||||
          <mat-header-cell *matHeaderCellDef class="table-value-column">
 | 
					          <mat-header-cell *matHeaderCellDef class="table-value-column">
 | 
				
			||||||
            {{ 'gateway.name' | translate }}
 | 
					            {{ 'gateway.info' | translate }}
 | 
				
			||||||
          </mat-header-cell>
 | 
					          </mat-header-cell>
 | 
				
			||||||
          <mat-cell *matCellDef="let slave" class="table-value-column">
 | 
					          <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>
 | 
					          </mat-cell>
 | 
				
			||||||
        </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">
 | 
				
			||||||
            {{ 'gateway.client-communication-type' | translate }}
 | 
					            <div tbTruncateWithTooltip>{{ 'gateway.client-communication-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']) }}
 | 
				
			||||||
@ -113,8 +121,8 @@
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </mat-cell>
 | 
					          </mat-cell>
 | 
				
			||||||
        </ng-container>
 | 
					        </ng-container>
 | 
				
			||||||
        <mat-header-row [ngClass]="{'mat-row-select': true}" *matHeaderRowDef="['name', 'type', 'actions']; sticky: true"></mat-header-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: ['name', 'type', 'actions']"></mat-row>
 | 
					        <mat-row *matRowDef="let slave; columns: ['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">
 | 
				
			||||||
 | 
				
			|||||||
@ -113,7 +113,6 @@ export abstract class ModbusSlaveDialogAbstract<Component, Config> extends Dialo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private initializeSlaveFormGroup(): void {
 | 
					  private initializeSlaveFormGroup(): void {
 | 
				
			||||||
    this.slaveConfigFormGroup = this.fb.group({
 | 
					    this.slaveConfigFormGroup = this.fb.group({
 | 
				
			||||||
      name: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
					 | 
				
			||||||
      type: [ModbusProtocolType.TCP],
 | 
					      type: [ModbusProtocolType.TCP],
 | 
				
			||||||
      host: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
					      host: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
				
			||||||
      port: [null, [Validators.required, Validators.min(PortLimits.MIN), Validators.max(PortLimits.MAX)]],
 | 
					      port: [null, [Validators.required, Validators.min(PortLimits.MIN), Validators.max(PortLimits.MAX)]],
 | 
				
			||||||
 | 
				
			|||||||
@ -27,23 +27,6 @@
 | 
				
			|||||||
    </button>
 | 
					    </button>
 | 
				
			||||||
  </mat-toolbar>
 | 
					  </mat-toolbar>
 | 
				
			||||||
  <div mat-dialog-content [formGroup]="slaveConfigFormGroup" class="tb-form-panel">
 | 
					  <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="stroked tb-form-panel">
 | 
				
			||||||
      <div class="tb-form-panel no-border no-padding padding-top">
 | 
					      <div class="tb-form-panel no-border no-padding padding-top">
 | 
				
			||||||
        <div class="tb-flex row space-between align-center no-gap fill-width">
 | 
					        <div class="tb-flex row space-between align-center no-gap fill-width">
 | 
				
			||||||
 | 
				
			|||||||
@ -17,11 +17,14 @@
 | 
				
			|||||||
import { Directive } from '@angular/core';
 | 
					import { Directive } from '@angular/core';
 | 
				
			||||||
import { FormGroup } from '@angular/forms';
 | 
					import { FormGroup } from '@angular/forms';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
 | 
					  BrokerConfig,
 | 
				
			||||||
  MappingType,
 | 
					  MappingType,
 | 
				
			||||||
  MQTTBasicConfig, MQTTBasicConfig_v3_5_2,
 | 
					  MQTTBasicConfig,
 | 
				
			||||||
 | 
					  MQTTBasicConfig_v3_5_2,
 | 
				
			||||||
  RequestMappingData,
 | 
					  RequestMappingData,
 | 
				
			||||||
  RequestMappingValue,
 | 
					  RequestMappingValue,
 | 
				
			||||||
  RequestType
 | 
					  RequestType,
 | 
				
			||||||
 | 
					  WorkersConfig
 | 
				
			||||||
} from '@home/components/widget/lib/gateway/gateway-widget.models';
 | 
					} from '@home/components/widget/lib/gateway/gateway-widget.models';
 | 
				
			||||||
import { isObject } from '@core/utils';
 | 
					import { isObject } from '@core/utils';
 | 
				
			||||||
import {
 | 
					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 {
 | 
					  writeValue(basicConfig: BasicConfig): void {
 | 
				
			||||||
    this.basicFormGroup.setValue(this.mapConfigToFormValue(basicConfig), { emitEvent: false });
 | 
					    this.basicFormGroup.setValue(this.mapConfigToFormValue(basicConfig), { emitEvent: false });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -26,7 +26,6 @@ import {
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
  MqttBasicConfigDirective
 | 
					  MqttBasicConfigDirective
 | 
				
			||||||
} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.abstract';
 | 
					} 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 { CommonModule } from '@angular/common';
 | 
				
			||||||
import { SharedModule } from '@shared/shared.module';
 | 
					import { SharedModule } from '@shared/shared.module';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@ -85,19 +84,14 @@ export class MqttBasicConfigComponent extends MqttBasicConfigDirective<MQTTBasic
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  protected override getMappedValue(basicConfig: MQTTBasicConfig_v3_5_2): MQTTBasicConfig_v3_5_2 {
 | 
					  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)) {
 | 
					    return {
 | 
				
			||||||
      broker = {
 | 
					      broker: this.getBrokerMappedValue(broker, workers),
 | 
				
			||||||
        ...broker,
 | 
					      mapping,
 | 
				
			||||||
        ...workers,
 | 
					      requestsMapping: (requestsMapping as RequestMappingData[])?.length
 | 
				
			||||||
      };
 | 
					        ? this.getRequestDataObject(requestsMapping as RequestMappingValue[])
 | 
				
			||||||
    }
 | 
					        : {} as Record<RequestType, RequestMappingValue[]>
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
    if ((requestsMapping as RequestMappingData[])?.length) {
 | 
					 | 
				
			||||||
      requestsMapping = this.getRequestDataObject(requestsMapping as RequestMappingValue[]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return { broker, mapping, requestsMapping };
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -102,23 +102,16 @@ export class MqttLegacyBasicConfigComponent extends MqttBasicConfigDirective<MQT
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  protected override getMappedValue(basicConfig: MQTTBasicConfig_v3_5_2): MQTTLegacyBasicConfig {
 | 
					  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)) {
 | 
					    const updatedRequestMapping = (requestsMapping as RequestMappingData[])?.length
 | 
				
			||||||
      broker = {
 | 
					      ? this.getRequestDataObject(requestsMapping as RequestMappingValue[])
 | 
				
			||||||
        ...broker,
 | 
					      : {} as Record<RequestType, RequestMappingData[]>;
 | 
				
			||||||
        ...workers,
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if ((requestsMapping as RequestMappingData[])?.length) {
 | 
					 | 
				
			||||||
      requestsMapping = this.getRequestDataObject(requestsMapping as RequestMappingValue[]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      broker,
 | 
					      broker: this.getBrokerMappedValue(broker, workers),
 | 
				
			||||||
      mapping: MqttVersionMappingUtil.mapMappingToDowngradedVersion(mapping),
 | 
					      mapping: MqttVersionMappingUtil.mapMappingToDowngradedVersion(mapping),
 | 
				
			||||||
      ...(MqttVersionMappingUtil.mapRequestsToDowngradedVersion(requestsMapping as Record<RequestType, RequestMappingData[]>))
 | 
					      ...(MqttVersionMappingUtil.mapRequestsToDowngradedVersion(updatedRequestMapping as Record<RequestType, RequestMappingData[]>))
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -70,8 +70,8 @@
 | 
				
			|||||||
                              matTooltipPosition="above"
 | 
					                              matTooltipPosition="above"
 | 
				
			||||||
                              matTooltipClass="tb-error-tooltip"
 | 
					                              matTooltipClass="tb-error-tooltip"
 | 
				
			||||||
                              [matTooltip]="('gateway.value-required') | translate"
 | 
					                              [matTooltip]="('gateway.value-required') | translate"
 | 
				
			||||||
                              *ngIf="keyControl.get(keyControl.get('value').value).hasError('required')
 | 
					                              *ngIf="keyControl.get(keyControl.get('type').value).hasError('required')
 | 
				
			||||||
                              && keyControl.get(keyControl.get('value').value).touched"
 | 
					                              && keyControl.get(keyControl.get('type').value).touched"
 | 
				
			||||||
                              class="tb-error">
 | 
					                              class="tb-error">
 | 
				
			||||||
                      warning
 | 
					                      warning
 | 
				
			||||||
                    </mat-icon>
 | 
					                    </mat-icon>
 | 
				
			||||||
 | 
				
			|||||||
@ -181,9 +181,14 @@
 | 
				
			|||||||
              *ngIf="connectorForm.get('configVersion').value === GatewayVersion.Current else legacy"
 | 
					              *ngIf="connectorForm.get('configVersion').value === GatewayVersion.Current else legacy"
 | 
				
			||||||
              formControlName="basicConfig"
 | 
					              formControlName="basicConfig"
 | 
				
			||||||
              [generalTabContent]="generalTabContent"
 | 
					              [generalTabContent]="generalTabContent"
 | 
				
			||||||
 | 
					              (initialized)="basicConfigInitSubject.next()"
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
            <ng-template #legacy>
 | 
					            <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-template>
 | 
				
			||||||
          </ng-container>
 | 
					          </ng-container>
 | 
				
			||||||
          <ng-container *ngSwitchCase="ConnectorType.OPCUA">
 | 
					          <ng-container *ngSwitchCase="ConnectorType.OPCUA">
 | 
				
			||||||
@ -191,9 +196,14 @@
 | 
				
			|||||||
              *ngIf="connectorForm.get('configVersion').value === GatewayVersion.Current else legacy"
 | 
					              *ngIf="connectorForm.get('configVersion').value === GatewayVersion.Current else legacy"
 | 
				
			||||||
              formControlName="basicConfig"
 | 
					              formControlName="basicConfig"
 | 
				
			||||||
              [generalTabContent]="generalTabContent"
 | 
					              [generalTabContent]="generalTabContent"
 | 
				
			||||||
 | 
					              (initialized)="basicConfigInitSubject.next()"
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
            <ng-template #legacy>
 | 
					            <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-template>
 | 
				
			||||||
          </ng-container>
 | 
					          </ng-container>
 | 
				
			||||||
          <ng-container *ngSwitchCase="ConnectorType.MODBUS">
 | 
					          <ng-container *ngSwitchCase="ConnectorType.MODBUS">
 | 
				
			||||||
@ -201,9 +211,14 @@
 | 
				
			|||||||
              *ngIf="connectorForm.get('configVersion').value === GatewayVersion.Current else legacy"
 | 
					              *ngIf="connectorForm.get('configVersion').value === GatewayVersion.Current else legacy"
 | 
				
			||||||
              formControlName="basicConfig"
 | 
					              formControlName="basicConfig"
 | 
				
			||||||
              [generalTabContent]="generalTabContent"
 | 
					              [generalTabContent]="generalTabContent"
 | 
				
			||||||
 | 
					              (initialized)="basicConfigInitSubject.next()"
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
            <ng-template #legacy>
 | 
					            <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-template>
 | 
				
			||||||
          </ng-container>
 | 
					          </ng-container>
 | 
				
			||||||
        </ng-container>
 | 
					        </ng-container>
 | 
				
			||||||
@ -229,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()">
 | 
					                (click)="saveConnector(false)">
 | 
				
			||||||
          {{ 'action.save' | translate }}
 | 
					          {{ 'action.save' | translate }}
 | 
				
			||||||
        </button>
 | 
					        </button>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
				
			|||||||
@ -31,7 +31,7 @@ import { EntityId } from '@shared/models/id/entity-id';
 | 
				
			|||||||
import { AttributeService } from '@core/http/attribute.service';
 | 
					import { AttributeService } from '@core/http/attribute.service';
 | 
				
			||||||
import { TranslateService } from '@ngx-translate/core';
 | 
					import { TranslateService } from '@ngx-translate/core';
 | 
				
			||||||
import { forkJoin, Observable, of, Subject, Subscription } from 'rxjs';
 | 
					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 { PageComponent } from '@shared/components/page.component';
 | 
				
			||||||
import { PageLink } from '@shared/models/page/page-link';
 | 
					import { PageLink } from '@shared/models/page/page-link';
 | 
				
			||||||
import { AttributeDatasource } from '@home/models/datasource/attribute-datasource';
 | 
					import { AttributeDatasource } from '@home/models/datasource/attribute-datasource';
 | 
				
			||||||
@ -110,8 +110,10 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
 | 
				
			|||||||
  activeConnectors: Array<string>;
 | 
					  activeConnectors: Array<string>;
 | 
				
			||||||
  mode: ConfigurationModes = this.ConnectorConfigurationModes.BASIC;
 | 
					  mode: ConfigurationModes = this.ConnectorConfigurationModes.BASIC;
 | 
				
			||||||
  initialConnector: GatewayConnector;
 | 
					  initialConnector: GatewayConnector;
 | 
				
			||||||
 | 
					  basicConfigInitSubject = new Subject<void>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private gatewayVersion: string;
 | 
					  private gatewayVersion: string;
 | 
				
			||||||
 | 
					  private isGatewayActive: boolean;
 | 
				
			||||||
  private inactiveConnectors: Array<string>;
 | 
					  private inactiveConnectors: Array<string>;
 | 
				
			||||||
  private attributeDataSource: AttributeDatasource;
 | 
					  private attributeDataSource: AttributeDatasource;
 | 
				
			||||||
  private inactiveConnectorsDataSource: AttributeDatasource;
 | 
					  private inactiveConnectorsDataSource: AttributeDatasource;
 | 
				
			||||||
@ -124,7 +126,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
 | 
				
			|||||||
  private subscriptionOptions: WidgetSubscriptionOptions = {
 | 
					  private subscriptionOptions: WidgetSubscriptionOptions = {
 | 
				
			||||||
    callbacks: {
 | 
					    callbacks: {
 | 
				
			||||||
      onDataUpdated: () => this.ctx.ngZone.run(() => {
 | 
					      onDataUpdated: () => this.ctx.ngZone.run(() => {
 | 
				
			||||||
        this.onDataUpdated();
 | 
					        this.onErrorsUpdated();
 | 
				
			||||||
      }),
 | 
					      }),
 | 
				
			||||||
      onDataUpdateError: (_, e) => this.ctx.ngZone.run(() => {
 | 
					      onDataUpdateError: (_, e) => this.ctx.ngZone.run(() => {
 | 
				
			||||||
        this.onDataUpdateError(e);
 | 
					        this.onDataUpdateError(e);
 | 
				
			||||||
@ -155,8 +157,10 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
 | 
				
			|||||||
  ngAfterViewInit(): void {
 | 
					  ngAfterViewInit(): void {
 | 
				
			||||||
    this.dataSource.sort = this.sort;
 | 
					    this.dataSource.sort = this.sort;
 | 
				
			||||||
    this.dataSource.sortingDataAccessor = this.getSortingDataAccessor();
 | 
					    this.dataSource.sortingDataAccessor = this.getSortingDataAccessor();
 | 
				
			||||||
 | 
					    this.ctx.$scope.gatewayConnectors = this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.loadConnectors();
 | 
					    this.loadConnectors();
 | 
				
			||||||
 | 
					    this.loadGatewayState();
 | 
				
			||||||
    this.observeModeChange();
 | 
					    this.observeModeChange();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -166,9 +170,9 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
 | 
				
			|||||||
    super.ngOnDestroy();
 | 
					    super.ngOnDestroy();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  saveConnector(): void {
 | 
					  saveConnector(isNew = true): void {
 | 
				
			||||||
    const value = this.getConnectorData();
 | 
					    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.SHARED_SCOPE
 | 
				
			||||||
      : AttributeScope.SERVER_SCOPE;
 | 
					      : AttributeScope.SERVER_SCOPE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -275,7 +279,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  isConnectorSynced(attribute: GatewayAttributeData): boolean {
 | 
					  isConnectorSynced(attribute: GatewayAttributeData): boolean {
 | 
				
			||||||
    const connectorData = attribute.value;
 | 
					    const connectorData = attribute.value;
 | 
				
			||||||
    if (!connectorData.ts || attribute.skipSync) {
 | 
					    if (!connectorData.ts || attribute.skipSync || !this.isGatewayActive) {
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const clientIndex = this.activeData.findIndex(data => {
 | 
					    const clientIndex = this.activeData.findIndex(data => {
 | 
				
			||||||
@ -479,7 +483,6 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
 | 
				
			|||||||
        filter(Boolean),
 | 
					        filter(Boolean),
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      .subscribe(value => {
 | 
					      .subscribe(value => {
 | 
				
			||||||
        this.initialConnector = null;
 | 
					 | 
				
			||||||
        if (this.connectorForm.disabled) {
 | 
					        if (this.connectorForm.disabled) {
 | 
				
			||||||
          this.connectorForm.enable();
 | 
					          this.connectorForm.enable();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -487,9 +490,16 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
 | 
				
			|||||||
          value.configurationJson = {} as ConnectorBaseConfig;
 | 
					          value.configurationJson = {} as ConnectorBaseConfig;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        value.basicConfig = value.configurationJson;
 | 
					        value.basicConfig = value.configurationJson;
 | 
				
			||||||
        this.updateConnector(value);
 | 
					        this.initialConnector = value;
 | 
				
			||||||
 | 
					        this.connectorForm.patchValue(value, {emitEvent: false});
 | 
				
			||||||
        this.generate('basicConfig.broker.clientId');
 | 
					        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[] {
 | 
					  private parseConnectors(attribute: GatewayAttributeData[]): string[] {
 | 
				
			||||||
    const connectors = attribute?.[0]?.value || [];
 | 
					    const connectors = attribute?.[0]?.value || [];
 | 
				
			||||||
    return isString(connectors) ? JSON.parse(connectors) : connectors;
 | 
					    return isString(connectors) ? JSON.parse(connectors) : connectors;
 | 
				
			||||||
@ -598,7 +621,14 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
 | 
				
			|||||||
  private observeModeChange(): void {
 | 
					  private observeModeChange(): void {
 | 
				
			||||||
    this.connectorForm.get('mode').valueChanges
 | 
					    this.connectorForm.get('mode').valueChanges
 | 
				
			||||||
      .pipe(takeUntil(this.destroy$))
 | 
					      .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 {
 | 
					  private observeAttributeChange(): void {
 | 
				
			||||||
@ -664,10 +694,29 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
 | 
				
			|||||||
    console.error(errorText);
 | 
					    console.error(errorText);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private onDataUpdated(): void {
 | 
					  private onErrorsUpdated(): void {
 | 
				
			||||||
    this.cd.detectChanges();
 | 
					    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 {
 | 
					  private generateSubscription(): void {
 | 
				
			||||||
    if (this.subscription) {
 | 
					    if (this.subscription) {
 | 
				
			||||||
      this.subscription.unsubscribe();
 | 
					      this.subscription.unsubscribe();
 | 
				
			||||||
@ -760,13 +809,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
 | 
				
			|||||||
      case ConnectorType.MQTT:
 | 
					      case ConnectorType.MQTT:
 | 
				
			||||||
      case ConnectorType.OPCUA:
 | 
					      case ConnectorType.OPCUA:
 | 
				
			||||||
      case ConnectorType.MODBUS:
 | 
					      case ConnectorType.MODBUS:
 | 
				
			||||||
        this.connectorForm.get('mode').setValue(connector.mode || ConfigurationModes.BASIC, {emitEvent: false});
 | 
					        this.updateBasicConfigConnector(connector);
 | 
				
			||||||
        this.connectorForm.get('configVersion').setValue(connector.configVersion, {emitEvent: false});
 | 
					 | 
				
			||||||
        setTimeout(() => {
 | 
					 | 
				
			||||||
          this.connectorForm.patchValue(connector, {emitEvent: false});
 | 
					 | 
				
			||||||
          this.connectorForm.markAsPristine();
 | 
					 | 
				
			||||||
          this.createBasicConfigWatcher();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        this.connectorForm.patchValue({...connector, mode: null});
 | 
					        this.connectorForm.patchValue({...connector, mode: null});
 | 
				
			||||||
@ -775,6 +818,24 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
 | 
				
			|||||||
    this.createJsonConfigWatcher();
 | 
					    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 {
 | 
					  private toggleReportStrategy(type: ConnectorType): void {
 | 
				
			||||||
    const reportStrategyControl = this.connectorForm.get('reportStrategy');
 | 
					    const reportStrategyControl = this.connectorForm.get('reportStrategy');
 | 
				
			||||||
    if (type === ConnectorType.MODBUS) {
 | 
					    if (type === ConnectorType.MODBUS) {
 | 
				
			||||||
 | 
				
			|||||||
@ -711,7 +711,7 @@ export const HelpLinkByMappingTypeMap = new Map<MappingType, string>(
 | 
				
			|||||||
  [
 | 
					  [
 | 
				
			||||||
    [MappingType.DATA, helpBaseUrl + '/docs/iot-gateway/config/mqtt/#section-mapping'],
 | 
					    [MappingType.DATA, helpBaseUrl + '/docs/iot-gateway/config/mqtt/#section-mapping'],
 | 
				
			||||||
    [MappingType.OPCUA, helpBaseUrl + '/docs/iot-gateway/config/opc-ua/#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']
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -45,7 +45,7 @@ export class OpcVersionMappingUtil {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  static mapServerToDowngradedVersion(config: OPCBasicConfig_v3_5_2): LegacyServerConfig {
 | 
					  static mapServerToDowngradedVersion(config: OPCBasicConfig_v3_5_2): LegacyServerConfig {
 | 
				
			||||||
    const { mapping, server } = config;
 | 
					    const { mapping, server } = config;
 | 
				
			||||||
    const { enableSubscriptions, ...restServer } = server;
 | 
					    const { enableSubscriptions, ...restServer } = server ?? {} as ServerConfig;
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      ...restServer,
 | 
					      ...restServer,
 | 
				
			||||||
      mapping: mapping ? this.mapMappingToDowngradedVersion(mapping) : [],
 | 
					      mapping: mapping ? this.mapMappingToDowngradedVersion(mapping) : [],
 | 
				
			||||||
 | 
				
			|||||||
@ -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' +
 | 
					    '.mat-mdc-table .mat-mdc-cell button.mat-mdc-icon-button[disabled][disabled] mat-icon {\n' +
 | 
				
			||||||
    'color: ' + mdDarkDisabled + ';\n' +
 | 
					    'color: ' + mdDarkDisabled + ';\n' +
 | 
				
			||||||
    '}\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' +
 | 
					    '.mat-divider {\n' +
 | 
				
			||||||
    'border-top-color: ' + mdDarkDivider + ';\n' +
 | 
					    'border-top-color: ' + mdDarkDivider + ';\n' +
 | 
				
			||||||
    '}\n' +
 | 
					    '}\n' +
 | 
				
			||||||
 | 
				
			|||||||
@ -3036,6 +3036,7 @@
 | 
				
			|||||||
        "grpc-max-pings-without-data-required": "Max pings without data is required",
 | 
					        "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-min": "Max pings without data can not be less then 1",
 | 
				
			||||||
        "grpc-max-pings-without-data-pattern": "Max pings without data is not valid",
 | 
					        "grpc-max-pings-without-data-pattern": "Max pings without data is not valid",
 | 
				
			||||||
 | 
					        "info": "Info",
 | 
				
			||||||
        "identity": "Identity",
 | 
					        "identity": "Identity",
 | 
				
			||||||
        "inactivity-check-period-seconds": "Inactivity check period (in sec)",
 | 
					        "inactivity-check-period-seconds": "Inactivity check period (in sec)",
 | 
				
			||||||
        "inactivity-check-period-seconds-required": "Inactivity check period is required",
 | 
					        "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-min": "Check connectors configuration can not be less then 1",
 | 
				
			||||||
            "check-connectors-configuration-pattern": "Check connectors configuration is not valid",
 | 
					            "check-connectors-configuration-pattern": "Check connectors configuration is not valid",
 | 
				
			||||||
            "add": "Add command",
 | 
					            "add": "Add command",
 | 
				
			||||||
            "timeout": "Timeout",
 | 
					            "timeout": "Timeout (in sec)",
 | 
				
			||||||
            "timeout-ms": "Timeout (in ms)",
 | 
					            "timeout-ms": "Timeout (in ms)",
 | 
				
			||||||
            "timeout-required": "Timeout is required",
 | 
					            "timeout-required": "Timeout is required",
 | 
				
			||||||
            "timeout-min": "Timeout can not be less then 1",
 | 
					            "timeout-min": "Timeout can not be less then 1",
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,6 @@
 | 
				
			|||||||
        "master": {
 | 
					        "master": {
 | 
				
			||||||
            "slaves": [
 | 
					            "slaves": [
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    "name": "Slave 1",
 | 
					 | 
				
			||||||
                    "host": "127.0.0.1",
 | 
					                    "host": "127.0.0.1",
 | 
				
			||||||
                    "port": 5021,
 | 
					                    "port": 5021,
 | 
				
			||||||
                    "type": "tcp",
 | 
					                    "type": "tcp",
 | 
				
			||||||
@ -231,10 +230,10 @@
 | 
				
			|||||||
                    "attributes": [
 | 
					                    "attributes": [
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            "address": 5,
 | 
					                            "address": 5,
 | 
				
			||||||
                            "type": "string",
 | 
					                            "type": "8int",
 | 
				
			||||||
                            "tag": "sm",
 | 
					                            "tag": "coil",
 | 
				
			||||||
                            "objectsCount": 1,
 | 
					                            "objectsCount": 1,
 | 
				
			||||||
                            "value": "12"
 | 
					                            "value": 0
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    ],
 | 
					                    ],
 | 
				
			||||||
                    "timeseries": [],
 | 
					                    "timeseries": [],
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user