RPC templates update for MQTT/MODBUS/OPC
This commit is contained in:
parent
26f9fae3e0
commit
69aca15b9b
@ -16,20 +16,9 @@
|
||||
|
||||
-->
|
||||
<ng-container [formGroup]="rpcParametersFormGroup">
|
||||
<div fxFlex fxLayout="row">
|
||||
<mat-form-field fxFlex="100">
|
||||
<mat-label>{{ 'gateway.key' | translate }}</mat-label>
|
||||
<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 class="tb-form-hint tb-primary-fill tb-flex no-padding-top hint-container">
|
||||
RPC response will return all subtracted values from all connected devices when the reading functions are selected.<br>
|
||||
RPC will write a filled value to all connected devices when the writing functions are selected.
|
||||
</div>
|
||||
<div fxFlex fxLayout="row" fxLayoutGap="10px">
|
||||
<mat-form-field fxFlex="50" class="mat-block">
|
||||
@ -45,21 +34,6 @@
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</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">
|
||||
<mat-form-field fxFlex="50">
|
||||
<mat-label>{{ 'gateway.rpc.address' | translate }}</mat-label>
|
||||
@ -88,5 +62,20 @@
|
||||
/>
|
||||
</mat-form-field>
|
||||
</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>
|
||||
|
||||
@ -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,
|
||||
ModbusFunctionCodeTranslationsMap,
|
||||
ModbusObjectCountByDataType,
|
||||
ModbusValue,
|
||||
noLeadTrailSpacesRegex,
|
||||
RPCTemplateConfigModbus,
|
||||
} from '@home/components/widget/lib/gateway/gateway-widget.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-modbus-rpc-parameters',
|
||||
templateUrl: './modbus-rpc-parameters.component.html',
|
||||
styleUrls: ['./modbus-rpc-parameters.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [
|
||||
{
|
||||
@ -80,14 +81,13 @@ export class ModbusRpcParametersComponent implements ControlValueAccessor, Valid
|
||||
private readonly readFunctionCodes = [1, 2, 3, 4];
|
||||
private readonly bitsFunctionCodes = [...this.readFunctionCodes, ...this.writeFunctionCodes];
|
||||
|
||||
private onChange: (value: ModbusValue) => void;
|
||||
private onChange: (value: RPCTemplateConfigModbus) => void;
|
||||
private onTouched: () => void;
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
this.rpcParametersFormGroup = this.fb.group({
|
||||
tag: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
type: [ModbusDataType.BYTES, [Validators.required]],
|
||||
functionCode: [this.defaultFunctionCodes[0], [Validators.required]],
|
||||
value: [{value: '', disabled: true}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
@ -106,7 +106,7 @@ export class ModbusRpcParametersComponent implements ControlValueAccessor, Valid
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: ModbusValue) => void): void {
|
||||
registerOnChange(fn: (value: RPCTemplateConfigModbus) => void): void {
|
||||
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});
|
||||
}
|
||||
|
||||
@ -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,136 @@
|
||||
///
|
||||
/// 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});
|
||||
}
|
||||
|
||||
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) => {
|
||||
const responseTopicControl = this.rpcParametersFormGroup.get('responseTopicExpression');
|
||||
const responseTimeoutControl = this.rpcParametersFormGroup.get('responseTimeout');
|
||||
if (isActive) {
|
||||
responseTopicControl.enable();
|
||||
responseTimeoutControl.enable();
|
||||
} else {
|
||||
responseTopicControl.disable();
|
||||
responseTimeoutControl.disable();
|
||||
}
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
).subscribe();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
<!--
|
||||
|
||||
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 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" fxLayoutAlign="space-between center type-container">
|
||||
<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" fxLayoutAlign="space-between center value-container">
|
||||
<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)="removeOCPUAArguments(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)="addOCPUAArguments()">
|
||||
{{ 'gateway.rpc.add-argument' | translate }}
|
||||
</button>
|
||||
</fieldset>
|
||||
</ng-container>
|
||||
|
||||
@ -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.
|
||||
*/
|
||||
:host {
|
||||
.arguments-container {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.type-container {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.value-container {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,166 @@
|
||||
///
|
||||
/// 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 { 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.clearFromArrayByName('arguments');
|
||||
params.arguments?.map(({type, value}) => ({type, [type]: value }))
|
||||
.forEach(argument => this.addOCPUAArguments(argument as OPCTypeValue));
|
||||
this.cdr.markForCheck();
|
||||
this.rpcParametersFormGroup.get('method').patchValue(params.method, {emitEvent: false});
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
removeOCPUAArguments(index: number): void {
|
||||
(this.rpcParametersFormGroup.get('arguments') as FormArray).removeAt(index);
|
||||
}
|
||||
|
||||
addOCPUAArguments(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: !value.integer}, [Validators.required, Validators.pattern(integerRegex)]],
|
||||
double: [{value: value.double ?? 0, disabled: !value.double}, [Validators.required]],
|
||||
boolean: [{value: value.boolean ?? false, disabled: !value.boolean}, [Validators.required]],
|
||||
});
|
||||
this.observeTypeChange(fromGroup);
|
||||
(this.rpcParametersFormGroup.get('arguments') as FormArray).push(fromGroup, {emitEvent: false});
|
||||
}
|
||||
|
||||
clearFromArrayByName(name: string): void {
|
||||
const formArray = this.rpcParametersFormGroup.get(name) 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"
|
||||
matTooltipClass="tb-error-tooltip"
|
||||
[matTooltip]="('gateway.value-required') | translate"
|
||||
*ngIf="keyControl.get(keyControl.get('type').value).hasError('required')
|
||||
&& keyControl.get(keyControl.get('type').value).touched"
|
||||
*ngIf="keyControl.get(keyControl.get('value').value).hasError('required')
|
||||
&& keyControl.get(keyControl.get('value').value).touched"
|
||||
class="tb-error">
|
||||
warning
|
||||
</mat-icon>
|
||||
|
||||
@ -104,7 +104,7 @@ export class TypeValuePanelComponent implements ControlValueAccessor, Validator,
|
||||
dataKeyFormGroup.disable({emitEvent: false});
|
||||
dataKeyFormGroup.get('type').enable({emitEvent: false});
|
||||
dataKeyFormGroup.get(type).enable({emitEvent: false});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
deleteKey($event: Event, index: number): void {
|
||||
|
||||
@ -44,7 +44,11 @@
|
||||
<div class="template-key">
|
||||
{{!innerValue ? ('gateway.rpc.' + config.key | translate) : config.key}}
|
||||
</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,
|
||||
'boolean-false': config.value === false }">
|
||||
<ng-container *ngIf="config.key === 'method' else value" [ngTemplateOutlet]="SNMPMethod"></ng-container>
|
||||
|
||||
@ -104,6 +104,10 @@
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.array-value {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -49,7 +49,8 @@ export class GatewayServiceRPCConnectorTemplatesComponent implements OnInit {
|
||||
rpcTemplates: Array<RPCTemplate>;
|
||||
|
||||
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;
|
||||
|
||||
constructor(private attributeService: AttributeService) {
|
||||
|
||||
@ -20,36 +20,6 @@
|
||||
class="mat-subtitle-1 title">{{ 'gateway.rpc.title' | translate: {type: gatewayConnectorDefaultTypesTranslates.get(connectorType)} }}</div>
|
||||
<ng-template [ngIf]="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">
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'gateway.rpc.methodRPC' | translate }}</mat-label>
|
||||
@ -407,31 +377,6 @@
|
||||
</button>
|
||||
</fieldset>
|
||||
</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>
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'gateway.statistics.command' | translate }}</mat-label>
|
||||
|
||||
@ -51,7 +51,6 @@ import {
|
||||
} from '@shared/components/dialog/json-object-edit-dialog.component';
|
||||
import { jsonRequired } from '@shared/components/json-object-edit.component';
|
||||
import { deepClone } from '@core/utils';
|
||||
import { takeUntil, tap } from "rxjs/operators";
|
||||
import { Subject } from "rxjs";
|
||||
|
||||
@Component({
|
||||
@ -129,7 +128,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
|
||||
this.propagateChange({...this.commandForm.value, ...value});
|
||||
}
|
||||
});
|
||||
this.observeMQTTWithResponse();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@ -141,16 +139,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
|
||||
let formGroup: FormGroup;
|
||||
|
||||
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:
|
||||
formGroup = this.fb.group({
|
||||
method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
@ -246,12 +234,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
|
||||
httpHeaders: this.fb.array([]),
|
||||
})
|
||||
break;
|
||||
case ConnectorType.OPCUA:
|
||||
formGroup = this.fb.group({
|
||||
method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||
arguments: this.fb.array([]),
|
||||
})
|
||||
break;
|
||||
default:
|
||||
formGroup = this.fb.group({
|
||||
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[];
|
||||
}
|
||||
|
||||
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) {
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
@ -368,34 +338,8 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C
|
||||
})
|
||||
delete value.httpHeaders;
|
||||
break;
|
||||
case ConnectorType.OPCUA:
|
||||
this.clearFromArrayByName("arguments");
|
||||
value.arguments.forEach(value => {
|
||||
this.addOCPUAArguments(value)
|
||||
})
|
||||
delete value.arguments;
|
||||
break;
|
||||
}
|
||||
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-template #connectorForm>
|
||||
<tb-gateway-service-rpc-connector
|
||||
*ngIf="connectorType !== ConnectorType.MODBUS else modbusParameters"
|
||||
*ngIf="!typesWithUpdatedParams.has(connectorType) else updatedParameters"
|
||||
formControlName="params"
|
||||
[connectorType]="connectorType"
|
||||
(sendCommand)="sendCommand()"
|
||||
(saveTemplate)="saveTemplate()"
|
||||
/>
|
||||
<ng-template #modbusParameters>
|
||||
<ng-template #updatedParameters>
|
||||
<div fxLayout="column" class="rpc-parameters">
|
||||
<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">
|
||||
<button mat-raised-button
|
||||
(click)="saveTemplate()"
|
||||
|
||||
@ -74,6 +74,11 @@ export class GatewayServiceRPCComponent implements OnInit {
|
||||
|
||||
readonly ConnectorType = ConnectorType;
|
||||
readonly gatewayConnectorDefaultTypesTranslates = GatewayConnectorDefaultTypesTranslatesMap;
|
||||
readonly typesWithUpdatedParams = new Set<ConnectorType>([
|
||||
ConnectorType.MQTT,
|
||||
ConnectorType.OPCUA,
|
||||
ConnectorType.MODBUS,
|
||||
]);
|
||||
|
||||
private subscription: IWidgetSubscription;
|
||||
private subscriptionOptions: WidgetSubscriptionOptions = {
|
||||
|
||||
@ -296,7 +296,7 @@ export interface LegacyTimeseries {
|
||||
|
||||
export interface RpcArgument {
|
||||
type: string;
|
||||
value: number;
|
||||
value: number | string | boolean;
|
||||
}
|
||||
|
||||
export interface RpcMethod {
|
||||
@ -541,6 +541,37 @@ export interface RPCTemplateConfig {
|
||||
[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 {
|
||||
config: RPCTemplateConfig;
|
||||
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 {
|
||||
TypeValuePanelComponent
|
||||
} 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 {
|
||||
MqttLegacyBasicConfigComponent
|
||||
@ -160,6 +157,16 @@ import {
|
||||
import {
|
||||
ModbusLegacyBasicConfigComponent
|
||||
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component';
|
||||
import {
|
||||
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';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -253,6 +260,10 @@ import {
|
||||
GatewayAdvancedConfigurationComponent,
|
||||
OpcUaLegacyBasicConfigComponent,
|
||||
ModbusLegacyBasicConfigComponent,
|
||||
MqttRpcParametersComponent,
|
||||
OpcRpcParametersComponent,
|
||||
ModbusRpcParametersComponent,
|
||||
RpcTemplateArrayViewPipe,
|
||||
],
|
||||
exports: [
|
||||
EntitiesTableWidgetComponent,
|
||||
|
||||
@ -3142,10 +3142,11 @@
|
||||
"title": "{{type}} Connector RPC parameters",
|
||||
"templates-title": "Connector RPC Templates",
|
||||
"methodFilter": "Method filter",
|
||||
"method-name": "Method name",
|
||||
"requestTopicExpression": "Request topic expression",
|
||||
"responseTopicExpression": "Response topic expression",
|
||||
"responseTimeout": "Response Time",
|
||||
"valueExpression": "Value Expression",
|
||||
"responseTimeout": "Response timeout",
|
||||
"valueExpression": "Value expression",
|
||||
"tag": "Tag",
|
||||
"type": "Type",
|
||||
"functionCode": "Function Code",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user