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