Merge pull request #11718 from maxunbearable/task/4143-rpc-templates-update
RPC templates update for MQTT, MODBUS, OPC-UA
This commit is contained in:
		
						commit
						61a0c65d3e
					
				@ -16,20 +16,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
-->
 | 
					-->
 | 
				
			||||||
<ng-container [formGroup]="rpcParametersFormGroup">
 | 
					<ng-container [formGroup]="rpcParametersFormGroup">
 | 
				
			||||||
  <div fxFlex fxLayout="row">
 | 
					  <div class="tb-form-hint tb-primary-fill tb-flex no-padding-top hint-container">
 | 
				
			||||||
    <mat-form-field fxFlex="100">
 | 
					    {{ 'gateway.rpc.hint.modbus-response-reading' | translate }}<br>
 | 
				
			||||||
      <mat-label>{{ 'gateway.key' | translate }}</mat-label>
 | 
					    {{ 'gateway.rpc.hint.modbus-writing-functions' | translate }}
 | 
				
			||||||
      <input matInput name="value" formControlName="tag" placeholder="{{ 'gateway.set' | translate }}"/>
 | 
					 | 
				
			||||||
      <mat-icon matSuffix
 | 
					 | 
				
			||||||
                matTooltipPosition="above"
 | 
					 | 
				
			||||||
                matTooltipClass="tb-error-tooltip"
 | 
					 | 
				
			||||||
                [matTooltip]="('gateway.key-required') | translate"
 | 
					 | 
				
			||||||
                *ngIf="rpcParametersFormGroup.get('tag').hasError('required') &&
 | 
					 | 
				
			||||||
                                           rpcParametersFormGroup.get('tag').touched"
 | 
					 | 
				
			||||||
                class="tb-error">
 | 
					 | 
				
			||||||
        warning
 | 
					 | 
				
			||||||
      </mat-icon>
 | 
					 | 
				
			||||||
    </mat-form-field>
 | 
					 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
  <div fxFlex fxLayout="row" fxLayoutGap="10px">
 | 
					  <div fxFlex fxLayout="row" fxLayoutGap="10px">
 | 
				
			||||||
    <mat-form-field fxFlex="50" class="mat-block">
 | 
					    <mat-form-field fxFlex="50" class="mat-block">
 | 
				
			||||||
@ -45,21 +34,6 @@
 | 
				
			|||||||
      </mat-select>
 | 
					      </mat-select>
 | 
				
			||||||
    </mat-form-field>
 | 
					    </mat-form-field>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
  <div fxFlex fxLayout="row">
 | 
					 | 
				
			||||||
    <mat-form-field fxFlex="100" *ngIf="writeFunctionCodes.includes(rpcParametersFormGroup.get('functionCode').value)">
 | 
					 | 
				
			||||||
      <mat-label>{{ 'gateway.rpc.value' | translate }}</mat-label>
 | 
					 | 
				
			||||||
      <input matInput name="value" formControlName="value" placeholder="{{ 'gateway.set' | translate }}"/>
 | 
					 | 
				
			||||||
      <mat-icon matSuffix
 | 
					 | 
				
			||||||
                matTooltipPosition="above"
 | 
					 | 
				
			||||||
                matTooltipClass="tb-error-tooltip"
 | 
					 | 
				
			||||||
                [matTooltip]="('gateway.value-required') | translate"
 | 
					 | 
				
			||||||
                *ngIf="rpcParametersFormGroup.get('value').hasError('required') &&
 | 
					 | 
				
			||||||
                                           rpcParametersFormGroup.get('value').touched"
 | 
					 | 
				
			||||||
                class="tb-error">
 | 
					 | 
				
			||||||
        warning
 | 
					 | 
				
			||||||
      </mat-icon>
 | 
					 | 
				
			||||||
    </mat-form-field>
 | 
					 | 
				
			||||||
  </div>
 | 
					 | 
				
			||||||
  <div fxFlex fxLayout="row" fxLayoutGap="10px">
 | 
					  <div fxFlex fxLayout="row" fxLayoutGap="10px">
 | 
				
			||||||
    <mat-form-field fxFlex="50">
 | 
					    <mat-form-field fxFlex="50">
 | 
				
			||||||
      <mat-label>{{ 'gateway.rpc.address' | translate }}</mat-label>
 | 
					      <mat-label>{{ 'gateway.rpc.address' | translate }}</mat-label>
 | 
				
			||||||
@ -88,5 +62,20 @@
 | 
				
			|||||||
      />
 | 
					      />
 | 
				
			||||||
    </mat-form-field>
 | 
					    </mat-form-field>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					  <div *ngIf="writeFunctionCodes.includes(rpcParametersFormGroup.get('functionCode').value)" fxFlex fxLayout="row">
 | 
				
			||||||
 | 
					    <mat-form-field fxFlex="100">
 | 
				
			||||||
 | 
					      <mat-label>{{ 'gateway.rpc.value' | translate }}</mat-label>
 | 
				
			||||||
 | 
					      <input matInput name="value" formControlName="value" placeholder="{{ 'gateway.set' | translate }}"/>
 | 
				
			||||||
 | 
					      <mat-icon matSuffix
 | 
				
			||||||
 | 
					                matTooltipPosition="above"
 | 
				
			||||||
 | 
					                matTooltipClass="tb-error-tooltip"
 | 
				
			||||||
 | 
					                [matTooltip]="('gateway.value-required') | translate"
 | 
				
			||||||
 | 
					                *ngIf="rpcParametersFormGroup.get('value').hasError('required') && rpcParametersFormGroup.get('value').touched"
 | 
				
			||||||
 | 
					                class="tb-error"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        warning
 | 
				
			||||||
 | 
					      </mat-icon>
 | 
				
			||||||
 | 
					    </mat-form-field>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
</ng-container>
 | 
					</ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					:host {
 | 
				
			||||||
 | 
					  .hint-container {
 | 
				
			||||||
 | 
					    margin-bottom: 12px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -39,13 +39,14 @@ import {
 | 
				
			|||||||
  ModbusEditableDataTypes,
 | 
					  ModbusEditableDataTypes,
 | 
				
			||||||
  ModbusFunctionCodeTranslationsMap,
 | 
					  ModbusFunctionCodeTranslationsMap,
 | 
				
			||||||
  ModbusObjectCountByDataType,
 | 
					  ModbusObjectCountByDataType,
 | 
				
			||||||
  ModbusValue,
 | 
					 | 
				
			||||||
  noLeadTrailSpacesRegex,
 | 
					  noLeadTrailSpacesRegex,
 | 
				
			||||||
 | 
					  RPCTemplateConfigModbus,
 | 
				
			||||||
} from '@home/components/widget/lib/gateway/gateway-widget.models';
 | 
					} from '@home/components/widget/lib/gateway/gateway-widget.models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'tb-modbus-rpc-parameters',
 | 
					  selector: 'tb-modbus-rpc-parameters',
 | 
				
			||||||
  templateUrl: './modbus-rpc-parameters.component.html',
 | 
					  templateUrl: './modbus-rpc-parameters.component.html',
 | 
				
			||||||
 | 
					  styleUrls: ['./modbus-rpc-parameters.component.scss'],
 | 
				
			||||||
  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
					  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
				
			||||||
  providers: [
 | 
					  providers: [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@ -80,14 +81,13 @@ export class ModbusRpcParametersComponent implements ControlValueAccessor, Valid
 | 
				
			|||||||
  private readonly readFunctionCodes = [1, 2, 3, 4];
 | 
					  private readonly readFunctionCodes = [1, 2, 3, 4];
 | 
				
			||||||
  private readonly bitsFunctionCodes = [...this.readFunctionCodes, ...this.writeFunctionCodes];
 | 
					  private readonly bitsFunctionCodes = [...this.readFunctionCodes, ...this.writeFunctionCodes];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private onChange: (value: ModbusValue) => void;
 | 
					  private onChange: (value: RPCTemplateConfigModbus) => void;
 | 
				
			||||||
  private onTouched: () => void;
 | 
					  private onTouched: () => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private destroy$ = new Subject<void>();
 | 
					  private destroy$ = new Subject<void>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(private fb: FormBuilder) {
 | 
					  constructor(private fb: FormBuilder) {
 | 
				
			||||||
    this.rpcParametersFormGroup = this.fb.group({
 | 
					    this.rpcParametersFormGroup = this.fb.group({
 | 
				
			||||||
      tag: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
					 | 
				
			||||||
      type: [ModbusDataType.BYTES, [Validators.required]],
 | 
					      type: [ModbusDataType.BYTES, [Validators.required]],
 | 
				
			||||||
      functionCode: [this.defaultFunctionCodes[0], [Validators.required]],
 | 
					      functionCode: [this.defaultFunctionCodes[0], [Validators.required]],
 | 
				
			||||||
      value: [{value: '', disabled: true}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
					      value: [{value: '', disabled: true}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
				
			||||||
@ -106,7 +106,7 @@ export class ModbusRpcParametersComponent implements ControlValueAccessor, Valid
 | 
				
			|||||||
    this.destroy$.complete();
 | 
					    this.destroy$.complete();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  registerOnChange(fn: (value: ModbusValue) => void): void {
 | 
					  registerOnChange(fn: (value: RPCTemplateConfigModbus) => void): void {
 | 
				
			||||||
    this.onChange = fn;
 | 
					    this.onChange = fn;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -120,7 +120,7 @@ export class ModbusRpcParametersComponent implements ControlValueAccessor, Valid
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  writeValue(value: ModbusValue): void {
 | 
					  writeValue(value: RPCTemplateConfigModbus): void {
 | 
				
			||||||
    this.rpcParametersFormGroup.patchValue(value, {emitEvent: false});
 | 
					    this.rpcParametersFormGroup.patchValue(value, {emitEvent: false});
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					<!--
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<ng-container [formGroup]="rpcParametersFormGroup">
 | 
				
			||||||
 | 
					  <mat-form-field>
 | 
				
			||||||
 | 
					    <mat-label>{{ 'gateway.rpc.method-name' | translate }}</mat-label>
 | 
				
			||||||
 | 
					    <input matInput formControlName="methodFilter"
 | 
				
			||||||
 | 
					           placeholder="echo"/>
 | 
				
			||||||
 | 
					  </mat-form-field>
 | 
				
			||||||
 | 
					  <mat-form-field>
 | 
				
			||||||
 | 
					    <mat-label>{{ 'gateway.rpc.requestTopicExpression' | translate }}</mat-label>
 | 
				
			||||||
 | 
					    <input matInput formControlName="requestTopicExpression"
 | 
				
			||||||
 | 
					           placeholder="sensor/${deviceName}/request/${methodName}/${requestId}"/>
 | 
				
			||||||
 | 
					  </mat-form-field>
 | 
				
			||||||
 | 
					  <mat-slide-toggle class="margin" (click)="$event.stopPropagation()" formControlName="withResponse">
 | 
				
			||||||
 | 
					    {{ 'gateway.rpc.withResponse' | translate }}
 | 
				
			||||||
 | 
					  </mat-slide-toggle>
 | 
				
			||||||
 | 
					  <mat-form-field *ngIf="rpcParametersFormGroup.get('withResponse')?.value">
 | 
				
			||||||
 | 
					    <mat-label>{{ 'gateway.rpc.responseTopicExpression' | translate }}</mat-label>
 | 
				
			||||||
 | 
					    <input matInput formControlName="responseTopicExpression"
 | 
				
			||||||
 | 
					           placeholder="sensor/${deviceName}/response/${methodName}/${requestId}"/>
 | 
				
			||||||
 | 
					  </mat-form-field>
 | 
				
			||||||
 | 
					  <mat-form-field *ngIf="rpcParametersFormGroup.get('withResponse')?.value">
 | 
				
			||||||
 | 
					    <mat-label>{{ 'gateway.rpc.responseTimeout' | translate }}</mat-label>
 | 
				
			||||||
 | 
					    <input matInput formControlName="responseTimeout" type="number"
 | 
				
			||||||
 | 
					           placeholder="10000" min="10" step="1"/>
 | 
				
			||||||
 | 
					  </mat-form-field>
 | 
				
			||||||
 | 
					  <mat-form-field>
 | 
				
			||||||
 | 
					    <mat-label>{{ 'gateway.rpc.valueExpression' | translate }}</mat-label>
 | 
				
			||||||
 | 
					    <input matInput formControlName="valueExpression"
 | 
				
			||||||
 | 
					           placeholder="${params}"/>
 | 
				
			||||||
 | 
					  </mat-form-field>
 | 
				
			||||||
 | 
					</ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					:host {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .mat-mdc-slide-toggle.margin {
 | 
				
			||||||
 | 
					    margin-bottom: 10px;
 | 
				
			||||||
 | 
					    margin-left: 10px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,139 @@
 | 
				
			|||||||
 | 
					///
 | 
				
			||||||
 | 
					/// 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,
 | 
				
			||||||
 | 
					  OnDestroy,
 | 
				
			||||||
 | 
					} from '@angular/core';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  ControlValueAccessor,
 | 
				
			||||||
 | 
					  FormBuilder,
 | 
				
			||||||
 | 
					  NG_VALIDATORS,
 | 
				
			||||||
 | 
					  NG_VALUE_ACCESSOR,
 | 
				
			||||||
 | 
					  UntypedFormGroup,
 | 
				
			||||||
 | 
					  ValidationErrors,
 | 
				
			||||||
 | 
					  Validator, Validators,
 | 
				
			||||||
 | 
					} from '@angular/forms';
 | 
				
			||||||
 | 
					import { SharedModule } from '@shared/shared.module';
 | 
				
			||||||
 | 
					import { CommonModule } from '@angular/common';
 | 
				
			||||||
 | 
					import { Subject } from 'rxjs';
 | 
				
			||||||
 | 
					import { takeUntil, tap } from 'rxjs/operators';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  integerRegex,
 | 
				
			||||||
 | 
					  noLeadTrailSpacesRegex,
 | 
				
			||||||
 | 
					  RPCTemplateConfigMQTT
 | 
				
			||||||
 | 
					} from '@home/components/widget/lib/gateway/gateway-widget.models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					  selector: 'tb-mqtt-rpc-parameters',
 | 
				
			||||||
 | 
					  templateUrl: './mqtt-rpc-parameters.component.html',
 | 
				
			||||||
 | 
					  styleUrls: ['./mqtt-rpc-parameters.component.scss'],
 | 
				
			||||||
 | 
					  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
				
			||||||
 | 
					  providers: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      provide: NG_VALUE_ACCESSOR,
 | 
				
			||||||
 | 
					      useExisting: forwardRef(() => MqttRpcParametersComponent),
 | 
				
			||||||
 | 
					      multi: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      provide: NG_VALIDATORS,
 | 
				
			||||||
 | 
					      useExisting: forwardRef(() => MqttRpcParametersComponent),
 | 
				
			||||||
 | 
					      multi: true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  standalone: true,
 | 
				
			||||||
 | 
					  imports: [
 | 
				
			||||||
 | 
					    CommonModule,
 | 
				
			||||||
 | 
					    SharedModule,
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class MqttRpcParametersComponent implements ControlValueAccessor, Validator, OnDestroy {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  rpcParametersFormGroup: UntypedFormGroup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private onChange: (value: RPCTemplateConfigMQTT) => void = (_) => {};
 | 
				
			||||||
 | 
					  private onTouched: () => void = () => {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private destroy$ = new Subject<void>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(private fb: FormBuilder) {
 | 
				
			||||||
 | 
					    this.rpcParametersFormGroup = this.fb.group({
 | 
				
			||||||
 | 
					      methodFilter: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
				
			||||||
 | 
					      requestTopicExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
				
			||||||
 | 
					      responseTopicExpression: [{ value: null, disabled: true }, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
				
			||||||
 | 
					      responseTimeout: [{ value: null, disabled: true }, [Validators.min(10), Validators.pattern(integerRegex)]],
 | 
				
			||||||
 | 
					      valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
				
			||||||
 | 
					      withResponse: [false, []],
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.observeValueChanges();
 | 
				
			||||||
 | 
					    this.observeWithResponse();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ngOnDestroy(): void {
 | 
				
			||||||
 | 
					    this.destroy$.next();
 | 
				
			||||||
 | 
					    this.destroy$.complete();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  registerOnChange(fn: (value: RPCTemplateConfigMQTT) => void): void {
 | 
				
			||||||
 | 
					    this.onChange = fn;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  registerOnTouched(fn: () => void): void {
 | 
				
			||||||
 | 
					    this.onTouched = fn;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  validate(): ValidationErrors | null {
 | 
				
			||||||
 | 
					    return this.rpcParametersFormGroup.valid ? null : {
 | 
				
			||||||
 | 
					      rpcParametersFormGroup: { valid: false }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  writeValue(value: RPCTemplateConfigMQTT): void {
 | 
				
			||||||
 | 
					    this.rpcParametersFormGroup.patchValue(value, {emitEvent: false});
 | 
				
			||||||
 | 
					    this.toggleResponseFields(value.withResponse);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private observeValueChanges(): void {
 | 
				
			||||||
 | 
					    this.rpcParametersFormGroup.valueChanges.pipe(
 | 
				
			||||||
 | 
					      takeUntil(this.destroy$)
 | 
				
			||||||
 | 
					    ).subscribe((value) => {
 | 
				
			||||||
 | 
					      this.onChange(value);
 | 
				
			||||||
 | 
					      this.onTouched();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private observeWithResponse(): void {
 | 
				
			||||||
 | 
					    this.rpcParametersFormGroup.get('withResponse').valueChanges.pipe(
 | 
				
			||||||
 | 
					      tap((isActive: boolean) => this.toggleResponseFields(isActive)),
 | 
				
			||||||
 | 
					      takeUntil(this.destroy$),
 | 
				
			||||||
 | 
					    ).subscribe();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private toggleResponseFields(enabled: boolean): void {
 | 
				
			||||||
 | 
					    const responseTopicControl = this.rpcParametersFormGroup.get('responseTopicExpression');
 | 
				
			||||||
 | 
					    const responseTimeoutControl = this.rpcParametersFormGroup.get('responseTimeout');
 | 
				
			||||||
 | 
					    if (enabled) {
 | 
				
			||||||
 | 
					      responseTopicControl.enable();
 | 
				
			||||||
 | 
					      responseTimeoutControl.enable();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      responseTopicControl.disable();
 | 
				
			||||||
 | 
					      responseTimeoutControl.disable();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					<!--
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<ng-container [formGroup]="rpcParametersFormGroup">
 | 
				
			||||||
 | 
					  <div class="tb-form-hint tb-primary-fill tb-flex no-padding-top hint-container">
 | 
				
			||||||
 | 
					    {{ 'gateway.rpc.hint.opc-method' | translate }}
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <mat-form-field class="tb-flex">
 | 
				
			||||||
 | 
					    <mat-label>{{ 'gateway.rpc.method' | translate }}</mat-label>
 | 
				
			||||||
 | 
					    <input matInput formControlName="method" placeholder="multiply"/>
 | 
				
			||||||
 | 
					  </mat-form-field>
 | 
				
			||||||
 | 
					  <fieldset class="tb-form-panel stroked arguments-container" fxLayout="column" formArrayName="arguments">
 | 
				
			||||||
 | 
					    <strong>
 | 
				
			||||||
 | 
					      <span class="fields-label">{{ 'gateway.rpc.arguments' | translate }}</span>
 | 
				
			||||||
 | 
					    </strong>
 | 
				
			||||||
 | 
					    <div fxFlex fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="center center"
 | 
				
			||||||
 | 
					         *ngFor="let argumentFormGroup of rpcParametersFormGroup.get('arguments')['controls']; let i = index" [formGroup]="argumentFormGroup">
 | 
				
			||||||
 | 
					      <div class="tb-form-row column-xs type-container" fxLayoutAlign="space-between center">
 | 
				
			||||||
 | 
					        <div class="tb-required" translate>gateway.type</div>
 | 
				
			||||||
 | 
					        <div class="tb-flex no-gap">
 | 
				
			||||||
 | 
					          <mat-form-field class="tb-flex no-gap fill-width" appearance="outline" subscriptSizing="dynamic">
 | 
				
			||||||
 | 
					            <mat-select formControlName="type">
 | 
				
			||||||
 | 
					              <mat-select-trigger>
 | 
				
			||||||
 | 
					                <div class="tb-flex align-center">
 | 
				
			||||||
 | 
					                  <mat-icon class="tb-mat-18" [svgIcon]="valueTypes.get(argumentFormGroup.get('type').value)?.icon">
 | 
				
			||||||
 | 
					                  </mat-icon>
 | 
				
			||||||
 | 
					                  <span>{{ valueTypes.get(argumentFormGroup.get('type').value)?.name | translate }}</span>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </mat-select-trigger>
 | 
				
			||||||
 | 
					              <mat-option *ngFor="let valueType of valueTypeKeys" [value]="valueType">
 | 
				
			||||||
 | 
					                <mat-icon class="tb-mat-20" svgIcon="{{ valueTypes.get(valueType).icon }}">
 | 
				
			||||||
 | 
					                </mat-icon>
 | 
				
			||||||
 | 
					                <span>{{ valueTypes.get(valueType).name | translate }}</span>
 | 
				
			||||||
 | 
					              </mat-option>
 | 
				
			||||||
 | 
					            </mat-select>
 | 
				
			||||||
 | 
					          </mat-form-field>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="tb-form-row column-xs value-container" fxLayoutAlign="space-between center">
 | 
				
			||||||
 | 
					        <div class="tb-required" translate>gateway.value</div>
 | 
				
			||||||
 | 
					        <mat-form-field fxFlex appearance="outline" subscriptSizing="dynamic" class="tb-inline-field flex tb-suffix-absolute">
 | 
				
			||||||
 | 
					          <ng-container [ngSwitch]="argumentFormGroup.get('type').value">
 | 
				
			||||||
 | 
					            <input *ngSwitchCase="MappingValueType.STRING" matInput required formControlName="string"
 | 
				
			||||||
 | 
					                   placeholder="{{ 'gateway.set' | translate }}" />
 | 
				
			||||||
 | 
					            <input *ngSwitchCase="MappingValueType.INTEGER" matInput required formControlName="integer" type="number"
 | 
				
			||||||
 | 
					                   placeholder="{{ 'gateway.set' | translate }}" />
 | 
				
			||||||
 | 
					            <input *ngSwitchCase="MappingValueType.DOUBLE" matInput required formControlName="double" type="number"
 | 
				
			||||||
 | 
					                   placeholder="{{ 'gateway.set' | translate }}" />
 | 
				
			||||||
 | 
					            <mat-select *ngSwitchCase="MappingValueType.BOOLEAN" formControlName="boolean">
 | 
				
			||||||
 | 
					              <mat-option [value]="true">true</mat-option>
 | 
				
			||||||
 | 
					              <mat-option [value]="false">false</mat-option>
 | 
				
			||||||
 | 
					            </mat-select>
 | 
				
			||||||
 | 
					          </ng-container>
 | 
				
			||||||
 | 
					          <mat-icon matSuffix
 | 
				
			||||||
 | 
					                    matTooltipPosition="above"
 | 
				
			||||||
 | 
					                    matTooltipClass="tb-error-tooltip"
 | 
				
			||||||
 | 
					                    [matTooltip]="('gateway.value-required') | translate"
 | 
				
			||||||
 | 
					                    *ngIf="argumentFormGroup.get(argumentFormGroup.get('type').value).hasError('required')
 | 
				
			||||||
 | 
					                              && argumentFormGroup.get(argumentFormGroup.get('type').value).touched"
 | 
				
			||||||
 | 
					                    class="tb-error">
 | 
				
			||||||
 | 
					            warning
 | 
				
			||||||
 | 
					          </mat-icon>
 | 
				
			||||||
 | 
					        </mat-form-field>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <button mat-icon-button (click)="removeArgument(i)"
 | 
				
			||||||
 | 
					              class="tb-box-button"
 | 
				
			||||||
 | 
					              matTooltip="{{ 'gateway.rpc.remove' | translate }}"
 | 
				
			||||||
 | 
					              matTooltipPosition="above">
 | 
				
			||||||
 | 
					        <mat-icon>delete</mat-icon>
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <button mat-raised-button
 | 
				
			||||||
 | 
					            fxFlexAlign="start"
 | 
				
			||||||
 | 
					            (click)="addArgument()">
 | 
				
			||||||
 | 
					      {{ 'gateway.rpc.add-argument' | translate }}
 | 
				
			||||||
 | 
					    </button>
 | 
				
			||||||
 | 
					  </fieldset>
 | 
				
			||||||
 | 
					</ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					:host {
 | 
				
			||||||
 | 
					  .arguments-container {
 | 
				
			||||||
 | 
					    margin-bottom: 10px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .type-container {
 | 
				
			||||||
 | 
					    width: 40%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .value-container {
 | 
				
			||||||
 | 
					    width: 50%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .hint-container {
 | 
				
			||||||
 | 
					    margin-bottom: 12px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,169 @@
 | 
				
			|||||||
 | 
					///
 | 
				
			||||||
 | 
					/// 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,
 | 
				
			||||||
 | 
					  ChangeDetectorRef,
 | 
				
			||||||
 | 
					  Component,
 | 
				
			||||||
 | 
					  forwardRef,
 | 
				
			||||||
 | 
					  OnDestroy,
 | 
				
			||||||
 | 
					} from '@angular/core';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  ControlValueAccessor,
 | 
				
			||||||
 | 
					  FormArray,
 | 
				
			||||||
 | 
					  FormBuilder,
 | 
				
			||||||
 | 
					  FormGroup,
 | 
				
			||||||
 | 
					  NG_VALIDATORS,
 | 
				
			||||||
 | 
					  NG_VALUE_ACCESSOR,
 | 
				
			||||||
 | 
					  UntypedFormGroup,
 | 
				
			||||||
 | 
					  ValidationErrors,
 | 
				
			||||||
 | 
					  Validator, Validators,
 | 
				
			||||||
 | 
					} from '@angular/forms';
 | 
				
			||||||
 | 
					import { SharedModule } from '@shared/shared.module';
 | 
				
			||||||
 | 
					import { CommonModule } from '@angular/common';
 | 
				
			||||||
 | 
					import { Subject } from 'rxjs';
 | 
				
			||||||
 | 
					import { takeUntil } from 'rxjs/operators';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  integerRegex,
 | 
				
			||||||
 | 
					  MappingValueType,
 | 
				
			||||||
 | 
					  mappingValueTypesMap,
 | 
				
			||||||
 | 
					  noLeadTrailSpacesRegex,
 | 
				
			||||||
 | 
					  OPCTypeValue,
 | 
				
			||||||
 | 
					  RPCTemplateConfigOPC
 | 
				
			||||||
 | 
					} from '@home/components/widget/lib/gateway/gateway-widget.models';
 | 
				
			||||||
 | 
					import { isDefinedAndNotNull, isEqual } from '@core/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					  selector: 'tb-opc-rpc-parameters',
 | 
				
			||||||
 | 
					  templateUrl: './opc-rpc-parameters.component.html',
 | 
				
			||||||
 | 
					  styleUrls: ['./opc-rpc-parameters.component.scss'],
 | 
				
			||||||
 | 
					  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
				
			||||||
 | 
					  providers: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      provide: NG_VALUE_ACCESSOR,
 | 
				
			||||||
 | 
					      useExisting: forwardRef(() => OpcRpcParametersComponent),
 | 
				
			||||||
 | 
					      multi: true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      provide: NG_VALIDATORS,
 | 
				
			||||||
 | 
					      useExisting: forwardRef(() => OpcRpcParametersComponent),
 | 
				
			||||||
 | 
					      multi: true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  standalone: true,
 | 
				
			||||||
 | 
					  imports: [
 | 
				
			||||||
 | 
					    CommonModule,
 | 
				
			||||||
 | 
					    SharedModule,
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class OpcRpcParametersComponent implements ControlValueAccessor, Validator, OnDestroy {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  rpcParametersFormGroup: UntypedFormGroup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  readonly valueTypeKeys: MappingValueType[] = Object.values(MappingValueType);
 | 
				
			||||||
 | 
					  readonly MappingValueType = MappingValueType;
 | 
				
			||||||
 | 
					  readonly valueTypes = mappingValueTypesMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private onChange: (value: RPCTemplateConfigOPC) => void = (_) => {} ;
 | 
				
			||||||
 | 
					  private onTouched: () => void = () => {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private destroy$ = new Subject<void>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(private fb: FormBuilder, private cdr: ChangeDetectorRef) {
 | 
				
			||||||
 | 
					    this.rpcParametersFormGroup = this.fb.group({
 | 
				
			||||||
 | 
					      method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
				
			||||||
 | 
					      arguments: this.fb.array([]),
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.observeValueChanges();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ngOnDestroy(): void {
 | 
				
			||||||
 | 
					    this.destroy$.next();
 | 
				
			||||||
 | 
					    this.destroy$.complete();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  registerOnChange(fn: (value: RPCTemplateConfigOPC) => void): void {
 | 
				
			||||||
 | 
					    this.onChange = fn;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  registerOnTouched(fn: () => void): void {
 | 
				
			||||||
 | 
					    this.onTouched = fn;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  validate(): ValidationErrors | null {
 | 
				
			||||||
 | 
					    return this.rpcParametersFormGroup.valid ? null : {
 | 
				
			||||||
 | 
					      rpcParametersFormGroup: { valid: false }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  writeValue(params: RPCTemplateConfigOPC): void {
 | 
				
			||||||
 | 
					    this.clearArguments();
 | 
				
			||||||
 | 
					    params.arguments?.map(({type, value}) => ({type, [type]: value }))
 | 
				
			||||||
 | 
					      .forEach(argument => this.addArgument(argument as OPCTypeValue));
 | 
				
			||||||
 | 
					    this.cdr.markForCheck();
 | 
				
			||||||
 | 
					    this.rpcParametersFormGroup.get('method').patchValue(params.method);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private observeValueChanges(): void {
 | 
				
			||||||
 | 
					    this.rpcParametersFormGroup.valueChanges.pipe(
 | 
				
			||||||
 | 
					      takeUntil(this.destroy$)
 | 
				
			||||||
 | 
					    ).subscribe(params => {
 | 
				
			||||||
 | 
					      const updatedArguments = params.arguments.map(({type, ...config}) => ({type, value: config[type]}));
 | 
				
			||||||
 | 
					      this.onChange({method: params.method, arguments: updatedArguments});
 | 
				
			||||||
 | 
					      this.onTouched();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  removeArgument(index: number): void {
 | 
				
			||||||
 | 
					    (this.rpcParametersFormGroup.get('arguments') as FormArray).removeAt(index);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addArgument(value: OPCTypeValue = {} as OPCTypeValue): void {
 | 
				
			||||||
 | 
					    const fromGroup = this.fb.group({
 | 
				
			||||||
 | 
					      type: [value.type ?? MappingValueType.STRING],
 | 
				
			||||||
 | 
					      string: [
 | 
				
			||||||
 | 
					        value.string ?? { value: '', disabled: !(isEqual(value, {}) || value.string)},
 | 
				
			||||||
 | 
					        [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      integer: [
 | 
				
			||||||
 | 
					        {value: value.integer ?? 0, disabled: !isDefinedAndNotNull(value.integer)},
 | 
				
			||||||
 | 
					        [Validators.required, Validators.pattern(integerRegex)]
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      double: [{value: value.double ?? 0, disabled: !isDefinedAndNotNull(value.double)}, [Validators.required]],
 | 
				
			||||||
 | 
					      boolean: [{value: value.boolean ?? false, disabled: !isDefinedAndNotNull(value.boolean)}, [Validators.required]],
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    this.observeTypeChange(fromGroup);
 | 
				
			||||||
 | 
					    (this.rpcParametersFormGroup.get('arguments') as FormArray).push(fromGroup, {emitEvent: false});
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  clearArguments(): void {
 | 
				
			||||||
 | 
					    const formArray = this.rpcParametersFormGroup.get('arguments') as FormArray;
 | 
				
			||||||
 | 
					    while (formArray.length !== 0) {
 | 
				
			||||||
 | 
					      formArray.removeAt(0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private observeTypeChange(dataKeyFormGroup: FormGroup): void {
 | 
				
			||||||
 | 
					    dataKeyFormGroup.get('type').valueChanges
 | 
				
			||||||
 | 
					      .pipe(takeUntil(this.destroy$))
 | 
				
			||||||
 | 
					      .subscribe(type => {
 | 
				
			||||||
 | 
					        dataKeyFormGroup.disable({emitEvent: false});
 | 
				
			||||||
 | 
					        dataKeyFormGroup.get('type').enable({emitEvent: false});
 | 
				
			||||||
 | 
					        dataKeyFormGroup.get(type).enable({emitEvent: false});
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -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('type').value).hasError('required')
 | 
					                              *ngIf="keyControl.get(keyControl.get('value').value).hasError('required')
 | 
				
			||||||
                              && keyControl.get(keyControl.get('type').value).touched"
 | 
					                              && keyControl.get(keyControl.get('value').value).touched"
 | 
				
			||||||
                              class="tb-error">
 | 
					                              class="tb-error">
 | 
				
			||||||
                      warning
 | 
					                      warning
 | 
				
			||||||
                    </mat-icon>
 | 
					                    </mat-icon>
 | 
				
			||||||
 | 
				
			|||||||
@ -104,7 +104,7 @@ export class TypeValuePanelComponent implements ControlValueAccessor, Validator,
 | 
				
			|||||||
        dataKeyFormGroup.disable({emitEvent: false});
 | 
					        dataKeyFormGroup.disable({emitEvent: false});
 | 
				
			||||||
        dataKeyFormGroup.get('type').enable({emitEvent: false});
 | 
					        dataKeyFormGroup.get('type').enable({emitEvent: false});
 | 
				
			||||||
        dataKeyFormGroup.get(type).enable({emitEvent: false});
 | 
					        dataKeyFormGroup.get(type).enable({emitEvent: false});
 | 
				
			||||||
      })
 | 
					      });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  deleteKey($event: Event, index: number): void {
 | 
					  deleteKey($event: Event, index: number): void {
 | 
				
			||||||
 | 
				
			|||||||
@ -44,7 +44,11 @@
 | 
				
			|||||||
      <div class="template-key">
 | 
					      <div class="template-key">
 | 
				
			||||||
        {{!innerValue ? ('gateway.rpc.' + config.key | translate) : config.key}}
 | 
					        {{!innerValue ? ('gateway.rpc.' + config.key | translate) : config.key}}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div *ngIf="!isObject(config.value) else RPCObjectRow"
 | 
					      <div *ngIf="isArray(config.value)" tbTruncateWithTooltip class="array-value">
 | 
				
			||||||
 | 
					        {{ config.value | getRpcTemplateArrayView }}
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <ng-container *ngIf="isObject(config.value)" [ngTemplateOutlet]="RPCObjectRow"></ng-container>
 | 
				
			||||||
 | 
					      <div *ngIf="!isObject(config.value) && !isArray(config.value)"
 | 
				
			||||||
           [ngClass]="{'boolean-true': config.value === true,
 | 
					           [ngClass]="{'boolean-true': config.value === true,
 | 
				
			||||||
                   'boolean-false': config.value === false  }">
 | 
					                   'boolean-false': config.value === false  }">
 | 
				
			||||||
        <ng-container *ngIf="config.key === 'method' else value" [ngTemplateOutlet]="SNMPMethod"></ng-container>
 | 
					        <ng-container *ngIf="config.key === 'method' else value" [ngTemplateOutlet]="SNMPMethod"></ng-container>
 | 
				
			||||||
 | 
				
			|||||||
@ -104,6 +104,10 @@
 | 
				
			|||||||
    flex: 1;
 | 
					    flex: 1;
 | 
				
			||||||
    margin: 0;
 | 
					    margin: 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .array-value {
 | 
				
			||||||
 | 
					    margin-left: 10px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -49,7 +49,8 @@ export class GatewayServiceRPCConnectorTemplatesComponent implements OnInit {
 | 
				
			|||||||
  rpcTemplates: Array<RPCTemplate>;
 | 
					  rpcTemplates: Array<RPCTemplate>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public readonly originalOrder = (): number => 0;
 | 
					  public readonly originalOrder = (): number => 0;
 | 
				
			||||||
  public readonly isObject = (value: any) => isLiteralObject(value);
 | 
					  public readonly isObject = (value: unknown) => isLiteralObject(value);
 | 
				
			||||||
 | 
					  public readonly isArray = (value: unknown) => Array.isArray(value);
 | 
				
			||||||
  public readonly SNMPMethodsTranslations = SNMPMethodsTranslations;
 | 
					  public readonly SNMPMethodsTranslations = SNMPMethodsTranslations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(private attributeService: AttributeService) {
 | 
					  constructor(private attributeService: AttributeService) {
 | 
				
			||||||
 | 
				
			|||||||
@ -20,36 +20,6 @@
 | 
				
			|||||||
    class="mat-subtitle-1 title">{{ 'gateway.rpc.title' | translate: {type: gatewayConnectorDefaultTypesTranslates.get(connectorType)} }}</div>
 | 
					    class="mat-subtitle-1 title">{{ 'gateway.rpc.title' | translate: {type: gatewayConnectorDefaultTypesTranslates.get(connectorType)} }}</div>
 | 
				
			||||||
  <ng-template [ngIf]="connectorType">
 | 
					  <ng-template [ngIf]="connectorType">
 | 
				
			||||||
    <ng-container [ngSwitch]="connectorType">
 | 
					    <ng-container [ngSwitch]="connectorType">
 | 
				
			||||||
      <ng-template [ngSwitchCase]="ConnectorType.MQTT">
 | 
					 | 
				
			||||||
        <mat-form-field>
 | 
					 | 
				
			||||||
          <mat-label>{{ 'gateway.rpc.methodFilter' | translate }}</mat-label>
 | 
					 | 
				
			||||||
          <input matInput formControlName="methodFilter"
 | 
					 | 
				
			||||||
                 placeholder="echo"/>
 | 
					 | 
				
			||||||
        </mat-form-field>
 | 
					 | 
				
			||||||
        <mat-form-field>
 | 
					 | 
				
			||||||
          <mat-label>{{ 'gateway.rpc.requestTopicExpression' | translate }}</mat-label>
 | 
					 | 
				
			||||||
          <input matInput formControlName="requestTopicExpression"
 | 
					 | 
				
			||||||
                 placeholder="sensor/${deviceName}/request/${methodName}/${requestId}"/>
 | 
					 | 
				
			||||||
        </mat-form-field>
 | 
					 | 
				
			||||||
        <mat-slide-toggle class="margin" (click)="$event.stopPropagation()" formControlName="withResponse">
 | 
					 | 
				
			||||||
          {{ 'gateway.rpc.withResponse' | translate }}
 | 
					 | 
				
			||||||
        </mat-slide-toggle>
 | 
					 | 
				
			||||||
        <mat-form-field *ngIf="commandForm.get('withResponse')?.value">
 | 
					 | 
				
			||||||
          <mat-label>{{ 'gateway.rpc.responseTopicExpression' | translate }}</mat-label>
 | 
					 | 
				
			||||||
          <input matInput formControlName="responseTopicExpression"
 | 
					 | 
				
			||||||
                 placeholder="sensor/${deviceName}/response/${methodName}/${requestId}"/>
 | 
					 | 
				
			||||||
        </mat-form-field>
 | 
					 | 
				
			||||||
        <mat-form-field *ngIf="commandForm.get('withResponse')?.value">
 | 
					 | 
				
			||||||
          <mat-label>{{ 'gateway.rpc.responseTimeout' | translate }}</mat-label>
 | 
					 | 
				
			||||||
          <input matInput formControlName="responseTimeout" type="number"
 | 
					 | 
				
			||||||
                 placeholder="10000" min="10" step="1"/>
 | 
					 | 
				
			||||||
        </mat-form-field>
 | 
					 | 
				
			||||||
        <mat-form-field>
 | 
					 | 
				
			||||||
          <mat-label>{{ 'gateway.rpc.valueExpression' | translate }}</mat-label>
 | 
					 | 
				
			||||||
          <input matInput formControlName="valueExpression"
 | 
					 | 
				
			||||||
                 placeholder="${params}"/>
 | 
					 | 
				
			||||||
        </mat-form-field>
 | 
					 | 
				
			||||||
      </ng-template>
 | 
					 | 
				
			||||||
      <ng-template [ngSwitchCase]="ConnectorType.BACNET">
 | 
					      <ng-template [ngSwitchCase]="ConnectorType.BACNET">
 | 
				
			||||||
        <mat-form-field>
 | 
					        <mat-form-field>
 | 
				
			||||||
          <mat-label>{{ 'gateway.rpc.methodRPC' | translate }}</mat-label>
 | 
					          <mat-label>{{ 'gateway.rpc.methodRPC' | translate }}</mat-label>
 | 
				
			||||||
@ -407,31 +377,6 @@
 | 
				
			|||||||
          </button>
 | 
					          </button>
 | 
				
			||||||
        </fieldset>
 | 
					        </fieldset>
 | 
				
			||||||
      </ng-template>
 | 
					      </ng-template>
 | 
				
			||||||
      <ng-template [ngSwitchCase]="ConnectorType.OPCUA" #OPCUAForm>
 | 
					 | 
				
			||||||
        <mat-form-field >
 | 
					 | 
				
			||||||
          <mat-label>{{ 'gateway.rpc.method' | translate }}</mat-label>
 | 
					 | 
				
			||||||
          <input matInput formControlName="method" placeholder="multiply"/>
 | 
					 | 
				
			||||||
        </mat-form-field>
 | 
					 | 
				
			||||||
        <fieldset class="fields border" fxLayout="column" fxLayoutGap="10px" formArrayName="arguments">
 | 
					 | 
				
			||||||
          <span class="fields-label">{{ 'gateway.rpc.arguments' | translate }}</span>
 | 
					 | 
				
			||||||
          <div fxFlex fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="center center"
 | 
					 | 
				
			||||||
               *ngFor="let control of getFormArrayControls('arguments'); let i = index">
 | 
					 | 
				
			||||||
            <mat-form-field appearance="outline" fxFlex>
 | 
					 | 
				
			||||||
              <input matInput [formControl]="control" required/>
 | 
					 | 
				
			||||||
            </mat-form-field>
 | 
					 | 
				
			||||||
            <mat-icon style="cursor:pointer;"
 | 
					 | 
				
			||||||
                      fxFlex="30px"
 | 
					 | 
				
			||||||
                      (click)="removeOCPUAArguments(i)"
 | 
					 | 
				
			||||||
                      matTooltip="{{ 'gateway.rpc.remove' | translate }}">delete
 | 
					 | 
				
			||||||
            </mat-icon>
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <button mat-raised-button
 | 
					 | 
				
			||||||
                  fxFlexAlign="start"
 | 
					 | 
				
			||||||
                  (click)="addOCPUAArguments()">
 | 
					 | 
				
			||||||
            {{ 'gateway.rpc.add-argument' | translate }}
 | 
					 | 
				
			||||||
          </button>
 | 
					 | 
				
			||||||
        </fieldset>
 | 
					 | 
				
			||||||
      </ng-template>
 | 
					 | 
				
			||||||
      <ng-template ngSwitchDefault>
 | 
					      <ng-template ngSwitchDefault>
 | 
				
			||||||
        <mat-form-field>
 | 
					        <mat-form-field>
 | 
				
			||||||
          <mat-label>{{ 'gateway.statistics.command' | translate }}</mat-label>
 | 
					          <mat-label>{{ 'gateway.statistics.command' | translate }}</mat-label>
 | 
				
			||||||
 | 
				
			|||||||
@ -51,7 +51,6 @@ import {
 | 
				
			|||||||
} from '@shared/components/dialog/json-object-edit-dialog.component';
 | 
					} from '@shared/components/dialog/json-object-edit-dialog.component';
 | 
				
			||||||
import { jsonRequired } from '@shared/components/json-object-edit.component';
 | 
					import { jsonRequired } from '@shared/components/json-object-edit.component';
 | 
				
			||||||
import { deepClone } from '@core/utils';
 | 
					import { deepClone } from '@core/utils';
 | 
				
			||||||
import { takeUntil, tap } from "rxjs/operators";
 | 
					 | 
				
			||||||
import { Subject } from "rxjs";
 | 
					import { Subject } from "rxjs";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
@ -129,7 +128,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
 | 
				
			|||||||
        this.propagateChange({...this.commandForm.value, ...value});
 | 
					        this.propagateChange({...this.commandForm.value, ...value});
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    this.observeMQTTWithResponse();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnDestroy(): void {
 | 
					  ngOnDestroy(): void {
 | 
				
			||||||
@ -141,16 +139,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
 | 
				
			|||||||
    let formGroup: FormGroup;
 | 
					    let formGroup: FormGroup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (type) {
 | 
					    switch (type) {
 | 
				
			||||||
      case ConnectorType.MQTT:
 | 
					 | 
				
			||||||
        formGroup = this.fb.group({
 | 
					 | 
				
			||||||
          methodFilter: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
					 | 
				
			||||||
          requestTopicExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
					 | 
				
			||||||
          responseTopicExpression: [{ value: null, disabled: true }, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
					 | 
				
			||||||
          responseTimeout: [{ value: null, disabled: true }, [Validators.min(10), Validators.pattern(this.numbersOnlyPattern)]],
 | 
					 | 
				
			||||||
          valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
					 | 
				
			||||||
          withResponse: [false, []],
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      case ConnectorType.BACNET:
 | 
					      case ConnectorType.BACNET:
 | 
				
			||||||
        formGroup = this.fb.group({
 | 
					        formGroup = this.fb.group({
 | 
				
			||||||
          method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
					          method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
				
			||||||
@ -246,12 +234,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
 | 
				
			|||||||
          httpHeaders: this.fb.array([]),
 | 
					          httpHeaders: this.fb.array([]),
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      case ConnectorType.OPCUA:
 | 
					 | 
				
			||||||
        formGroup = this.fb.group({
 | 
					 | 
				
			||||||
          method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
					 | 
				
			||||||
          arguments: this.fb.array([]),
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        formGroup = this.fb.group({
 | 
					        formGroup = this.fb.group({
 | 
				
			||||||
          command: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
					          command: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
 | 
				
			||||||
@ -293,18 +275,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
 | 
				
			|||||||
    return (this.commandForm.get(path) as FormArray).controls as FormControl[];
 | 
					    return (this.commandForm.get(path) as FormArray).controls as FormControl[];
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  addOCPUAArguments(value: string = null) {
 | 
					 | 
				
			||||||
    const oidsFA = this.commandForm.get('arguments') as FormArray;
 | 
					 | 
				
			||||||
    if (oidsFA) {
 | 
					 | 
				
			||||||
      oidsFA.push(this.fb.control(value, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]), {emitEvent: false});
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  removeOCPUAArguments(index: number) {
 | 
					 | 
				
			||||||
    const oidsFA = this.commandForm.get('arguments') as FormArray;
 | 
					 | 
				
			||||||
    oidsFA.removeAt(index);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  openEditJSONDialog($event: Event) {
 | 
					  openEditJSONDialog($event: Event) {
 | 
				
			||||||
    if ($event) {
 | 
					    if ($event) {
 | 
				
			||||||
      $event.stopPropagation();
 | 
					      $event.stopPropagation();
 | 
				
			||||||
@ -368,34 +338,8 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
 | 
				
			|||||||
          })
 | 
					          })
 | 
				
			||||||
          delete value.httpHeaders;
 | 
					          delete value.httpHeaders;
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
        case ConnectorType.OPCUA:
 | 
					 | 
				
			||||||
          this.clearFromArrayByName("arguments");
 | 
					 | 
				
			||||||
          value.arguments.forEach(value => {
 | 
					 | 
				
			||||||
            this.addOCPUAArguments(value)
 | 
					 | 
				
			||||||
          })
 | 
					 | 
				
			||||||
          delete value.arguments;
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.commandForm.patchValue(value, {onlySelf: false});
 | 
					      this.commandForm.patchValue(value, {onlySelf: false});
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  private observeMQTTWithResponse(): void {
 | 
					 | 
				
			||||||
    if (this.connectorType === ConnectorType.MQTT) {
 | 
					 | 
				
			||||||
      this.commandForm.get('withResponse').valueChanges.pipe(
 | 
					 | 
				
			||||||
        tap((isActive: boolean) => {
 | 
					 | 
				
			||||||
          const responseTopicControl = this.commandForm.get('responseTopicExpression');
 | 
					 | 
				
			||||||
          const responseTimeoutControl = this.commandForm.get('responseTimeout');
 | 
					 | 
				
			||||||
          if (isActive) {
 | 
					 | 
				
			||||||
            responseTopicControl.enable();
 | 
					 | 
				
			||||||
            responseTimeoutControl.enable();
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            responseTopicControl.disable();
 | 
					 | 
				
			||||||
            responseTimeoutControl.disable();
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }),
 | 
					 | 
				
			||||||
        takeUntil(this.destroy$),
 | 
					 | 
				
			||||||
      ).subscribe();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -42,16 +42,20 @@
 | 
				
			|||||||
    </ng-container>
 | 
					    </ng-container>
 | 
				
			||||||
    <ng-template #connectorForm>
 | 
					    <ng-template #connectorForm>
 | 
				
			||||||
      <tb-gateway-service-rpc-connector
 | 
					      <tb-gateway-service-rpc-connector
 | 
				
			||||||
        *ngIf="connectorType !== ConnectorType.MODBUS else modbusParameters"
 | 
					        *ngIf="!typesWithUpdatedParams.has(connectorType) else updatedParameters"
 | 
				
			||||||
        formControlName="params"
 | 
					        formControlName="params"
 | 
				
			||||||
        [connectorType]="connectorType"
 | 
					        [connectorType]="connectorType"
 | 
				
			||||||
        (sendCommand)="sendCommand()"
 | 
					        (sendCommand)="sendCommand()"
 | 
				
			||||||
        (saveTemplate)="saveTemplate()"
 | 
					        (saveTemplate)="saveTemplate()"
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
      <ng-template #modbusParameters>
 | 
					      <ng-template #updatedParameters>
 | 
				
			||||||
        <div fxLayout="column" class="rpc-parameters">
 | 
					        <div fxLayout="column" class="rpc-parameters">
 | 
				
			||||||
          <div class="mat-subtitle-1 tb-form-panel-title">{{ 'gateway.rpc.title' | translate: {type: gatewayConnectorDefaultTypesTranslates.get(connectorType)} }}</div>
 | 
					          <div class="mat-subtitle-1 tb-form-panel-title">{{ 'gateway.rpc.title' | translate: {type: gatewayConnectorDefaultTypesTranslates.get(connectorType)} }}</div>
 | 
				
			||||||
          <tb-modbus-rpc-parameters formControlName="params"/>
 | 
					          <ng-container [ngSwitch]="connectorType">
 | 
				
			||||||
 | 
					            <tb-modbus-rpc-parameters *ngSwitchCase="ConnectorType.MODBUS" formControlName="params"/>
 | 
				
			||||||
 | 
					            <tb-mqtt-rpc-parameters *ngSwitchCase="ConnectorType.MQTT" formControlName="params"/>
 | 
				
			||||||
 | 
					            <tb-opc-rpc-parameters *ngSwitchCase="ConnectorType.OPCUA" formControlName="params"/>
 | 
				
			||||||
 | 
					          </ng-container>
 | 
				
			||||||
          <div class="template-actions" fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="10px">
 | 
					          <div class="template-actions" fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="10px">
 | 
				
			||||||
            <button mat-raised-button
 | 
					            <button mat-raised-button
 | 
				
			||||||
                    (click)="saveTemplate()"
 | 
					                    (click)="saveTemplate()"
 | 
				
			||||||
 | 
				
			|||||||
@ -74,6 +74,11 @@ export class GatewayServiceRPCComponent implements OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  readonly ConnectorType = ConnectorType;
 | 
					  readonly ConnectorType = ConnectorType;
 | 
				
			||||||
  readonly gatewayConnectorDefaultTypesTranslates = GatewayConnectorDefaultTypesTranslatesMap;
 | 
					  readonly gatewayConnectorDefaultTypesTranslates = GatewayConnectorDefaultTypesTranslatesMap;
 | 
				
			||||||
 | 
					  readonly typesWithUpdatedParams = new Set<ConnectorType>([
 | 
				
			||||||
 | 
					    ConnectorType.MQTT,
 | 
				
			||||||
 | 
					    ConnectorType.OPCUA,
 | 
				
			||||||
 | 
					    ConnectorType.MODBUS,
 | 
				
			||||||
 | 
					  ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private subscription: IWidgetSubscription;
 | 
					  private subscription: IWidgetSubscription;
 | 
				
			||||||
  private subscriptionOptions: WidgetSubscriptionOptions = {
 | 
					  private subscriptionOptions: WidgetSubscriptionOptions = {
 | 
				
			||||||
 | 
				
			|||||||
@ -297,7 +297,7 @@ export interface LegacyTimeseries {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export interface RpcArgument {
 | 
					export interface RpcArgument {
 | 
				
			||||||
  type: string;
 | 
					  type: string;
 | 
				
			||||||
  value: number;
 | 
					  value: number | string | boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface RpcMethod {
 | 
					export interface RpcMethod {
 | 
				
			||||||
@ -542,6 +542,37 @@ export interface RPCTemplateConfig {
 | 
				
			|||||||
  [key: string]: any;
 | 
					  [key: string]: any;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface RPCTemplateConfigMQTT {
 | 
				
			||||||
 | 
					  methodFilter: string;
 | 
				
			||||||
 | 
					  requestTopicExpression: string;
 | 
				
			||||||
 | 
					  responseTopicExpression?: string;
 | 
				
			||||||
 | 
					  responseTimeout?: number;
 | 
				
			||||||
 | 
					  valueExpression: string;
 | 
				
			||||||
 | 
					  withResponse: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface RPCTemplateConfigModbus {
 | 
				
			||||||
 | 
					  tag: string;
 | 
				
			||||||
 | 
					  type: ModbusDataType;
 | 
				
			||||||
 | 
					  functionCode?: number;
 | 
				
			||||||
 | 
					  objectsCount: number;
 | 
				
			||||||
 | 
					  address: number;
 | 
				
			||||||
 | 
					  value?: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface RPCTemplateConfigOPC {
 | 
				
			||||||
 | 
					  method: string;
 | 
				
			||||||
 | 
					  arguments: RpcArgument[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface OPCTypeValue {
 | 
				
			||||||
 | 
					  type: MappingValueType;
 | 
				
			||||||
 | 
					  boolean?: boolean;
 | 
				
			||||||
 | 
					  double?: number;
 | 
				
			||||||
 | 
					  integer?: number;
 | 
				
			||||||
 | 
					  string?: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface SaveRPCTemplateData {
 | 
					export interface SaveRPCTemplateData {
 | 
				
			||||||
  config: RPCTemplateConfig;
 | 
					  config: RPCTemplateConfig;
 | 
				
			||||||
  templates: Array<RPCTemplate>;
 | 
					  templates: Array<RPCTemplate>;
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					///
 | 
				
			||||||
 | 
					/// 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 { Pipe, PipeTransform } from '@angular/core';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Pipe({
 | 
				
			||||||
 | 
					  name: 'getRpcTemplateArrayView',
 | 
				
			||||||
 | 
					  standalone: true,
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class RpcTemplateArrayViewPipe implements PipeTransform {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  transform(values: {value: string | boolean | number}[]): string {
 | 
				
			||||||
 | 
					    return values.map(({value}) => value.toString()).join(', ');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -141,9 +141,6 @@ import {
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
  TypeValuePanelComponent
 | 
					  TypeValuePanelComponent
 | 
				
			||||||
} from '@home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component';
 | 
					} from '@home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component';
 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  ModbusRpcParametersComponent
 | 
					 | 
				
			||||||
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-rpc-parameters/modbus-rpc-parameters.component';
 | 
					 | 
				
			||||||
import { ScadaSymbolWidgetComponent } from '@home/components/widget/lib/scada/scada-symbol-widget.component';
 | 
					import { ScadaSymbolWidgetComponent } from '@home/components/widget/lib/scada/scada-symbol-widget.component';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  MqttLegacyBasicConfigComponent
 | 
					  MqttLegacyBasicConfigComponent
 | 
				
			||||||
@ -161,7 +158,17 @@ import {
 | 
				
			|||||||
  ModbusLegacyBasicConfigComponent
 | 
					  ModbusLegacyBasicConfigComponent
 | 
				
			||||||
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component';
 | 
					} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    ReportStrategyComponent
 | 
					  MqttRpcParametersComponent
 | 
				
			||||||
 | 
					} from '@home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  OpcRpcParametersComponent
 | 
				
			||||||
 | 
					} from '@home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  ModbusRpcParametersComponent
 | 
				
			||||||
 | 
					} from '@home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component';
 | 
				
			||||||
 | 
					import { RpcTemplateArrayViewPipe } from '@home/components/widget/lib/gateway/pipes/rpc-template-array-view.pipe';
 | 
				
			||||||
 | 
					import { 
 | 
				
			||||||
 | 
					  ReportStrategyComponent
 | 
				
			||||||
} from '@home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component';
 | 
					} from '@home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@NgModule({
 | 
					@NgModule({
 | 
				
			||||||
@ -256,6 +263,10 @@ import {
 | 
				
			|||||||
    GatewayAdvancedConfigurationComponent,
 | 
					    GatewayAdvancedConfigurationComponent,
 | 
				
			||||||
    OpcUaLegacyBasicConfigComponent,
 | 
					    OpcUaLegacyBasicConfigComponent,
 | 
				
			||||||
    ModbusLegacyBasicConfigComponent,
 | 
					    ModbusLegacyBasicConfigComponent,
 | 
				
			||||||
 | 
					    MqttRpcParametersComponent,
 | 
				
			||||||
 | 
					    OpcRpcParametersComponent,
 | 
				
			||||||
 | 
					    ModbusRpcParametersComponent,
 | 
				
			||||||
 | 
					    RpcTemplateArrayViewPipe,
 | 
				
			||||||
    ReportStrategyComponent,
 | 
					    ReportStrategyComponent,
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  exports: [
 | 
					  exports: [
 | 
				
			||||||
 | 
				
			|||||||
@ -3142,10 +3142,11 @@
 | 
				
			|||||||
            "title": "{{type}} Connector RPC parameters",
 | 
					            "title": "{{type}} Connector RPC parameters",
 | 
				
			||||||
            "templates-title": "Connector RPC Templates",
 | 
					            "templates-title": "Connector RPC Templates",
 | 
				
			||||||
            "methodFilter": "Method filter",
 | 
					            "methodFilter": "Method filter",
 | 
				
			||||||
 | 
					            "method-name": "Method name",
 | 
				
			||||||
            "requestTopicExpression": "Request topic expression",
 | 
					            "requestTopicExpression": "Request topic expression",
 | 
				
			||||||
            "responseTopicExpression": "Response topic expression",
 | 
					            "responseTopicExpression": "Response topic expression",
 | 
				
			||||||
            "responseTimeout": "Response Time",
 | 
					            "responseTimeout": "Response timeout",
 | 
				
			||||||
            "valueExpression": "Value Expression",
 | 
					            "valueExpression": "Value expression",
 | 
				
			||||||
            "tag": "Tag",
 | 
					            "tag": "Tag",
 | 
				
			||||||
            "type": "Type",
 | 
					            "type": "Type",
 | 
				
			||||||
            "functionCode": "Function Code",
 | 
					            "functionCode": "Function Code",
 | 
				
			||||||
@ -3184,6 +3185,11 @@
 | 
				
			|||||||
            "tries": "Tries",
 | 
					            "tries": "Tries",
 | 
				
			||||||
            "httpHeaders": "HTTP Headers",
 | 
					            "httpHeaders": "HTTP Headers",
 | 
				
			||||||
            "header-name": "Header name",
 | 
					            "header-name": "Header name",
 | 
				
			||||||
 | 
					            "hint": {
 | 
				
			||||||
 | 
					                "modbus-response-reading": "RPC response will return all subtracted values from all connected devices when the reading functions are selected.",
 | 
				
			||||||
 | 
					                "modbus-writing-functions": "RPC will write a filled value to all connected devices when the writing functions are selected.",
 | 
				
			||||||
 | 
					                "opc-method": "A filled method name is the OPC-UA method that will processed on the server side (make sure your node has the requested method)."
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            "security-name": "Security name",
 | 
					            "security-name": "Security name",
 | 
				
			||||||
            "value": "Value",
 | 
					            "value": "Value",
 | 
				
			||||||
            "security": "Security",
 | 
					            "security": "Security",
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user