Browse Source

connectors templates

pull/10011/head
Maksym Dudnik 3 years ago
parent
commit
6ee6ccaa36
  1. 54
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog.html
  2. 62
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog.ts
  3. 19
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html
  4. 5
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss
  5. 69
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.ts
  6. 136
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html
  7. 181
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts
  8. 7
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html
  9. 141
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts
  10. 15
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts
  11. 5
      ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
  12. 69
      ui-ngx/src/assets/locale/locale.constant-en_US.json

54
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog.html

@ -0,0 +1,54 @@
<!--
Copyright © 2016-2023 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.
-->
<mat-toolbar color="primary">
<h2 translate>gateway.rpc.save-template</h2>
<span fxFlex></span>
<button mat-icon-button
(click)="close()"
type="button">
<mat-icon class="material-icons">close</mat-icon>
</button>
</mat-toolbar>
<div mat-dialog-content style="width: 600px" class="mat-content" fxLayout="column">
<mat-form-field class="mat-block tb-value-type" style="flex-grow: 0">
<mat-label translate>gateway.rpc.template-name</mat-label>
<input matInput [formControl]="templateNameCtrl" required/>
<mat-error
*ngIf="templateNameCtrl.hasError('required')">
{{ 'gateway.rpc.template-name-required' | translate }}
</mat-error>
</mat-form-field>
<div class="mat-mdc-form-field-error"
style="margin-top: -15px; padding-left: 10px; font-size: 14px;"
*ngIf="validateDuplicateName(templateNameCtrl)">
{{ 'gateway.rpc.template-name-duplicate' | translate }}
</div>
</div>
<div mat-dialog-actions fxLayoutAlign="end center">
<button mat-button
type="button"
(click)="close()">
{{ 'action.cancel' | translate }}
</button>
<button mat-raised-button color="primary"
type="button"
[disabled]="!templateNameCtrl.valid"
(click)="save()">
{{ 'action.save' | translate }}
</button>
</div>

62
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog.ts

@ -0,0 +1,62 @@
///
/// Copyright © 2016-2023 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 { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { DialogComponent } from '@shared/components/dialog.component';
import { Router } from '@angular/router';
import { FormBuilder, FormControl, UntypedFormControl, Validators } from '@angular/forms';
import { RPCTemplate, SaveRPCTemplateData } from '@home/components/widget/lib/gateway/gateway-widget.models';
@Component({
selector: 'gateway-service-rpc-connector-template-dialog',
templateUrl: './gateway-service-rpc-connector-template-dialog.html'
})
export class GatewayServiceRPCConnectorTemplateDialogComponent extends DialogComponent<GatewayServiceRPCConnectorTemplateDialogComponent, boolean> {
config: {
[key: string]: any;
};
templates: Array<RPCTemplate>;
templateNameCtrl: FormControl;
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: SaveRPCTemplateData,
public dialogRef: MatDialogRef<GatewayServiceRPCConnectorTemplateDialogComponent, boolean>,
public fb: FormBuilder) {
super(store, router, dialogRef);
this.config = this.data.config;
this.templates = this.data.templates;
this.templateNameCtrl = this.fb.control('', [Validators.required])
}
validateDuplicateName(c: UntypedFormControl) {
const name = c.value.trim();
return !!this.templates.find((template) => template.name === name);
};
close(): void {
this.dialogRef.close();
}
save(): void {
this.dialogRef.close(this.templateNameCtrl.value);
}
}

19
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html

@ -22,13 +22,10 @@
{{template.name}} {{template.name}}
</mat-panel-title> </mat-panel-title>
<mat-panel-description> <mat-panel-description>
<button mat-icon-button matTooltip="Delete" (click)="deleteTemplate($event)"> <button mat-icon-button matTooltip="Delete" (click)="deleteTemplate($event, template)">
<mat-icon class="material-icons">delete</mat-icon> <mat-icon class="material-icons">delete</mat-icon>
</button> </button>
<button mat-icon-button matTooltip="Copy" (click)="copyTemplate($event)"> <button mat-icon-button matTooltip="Use" (click)="applyTemplate($event, template)">
<mat-icon class="material-icons">content_copy</mat-icon>
</button>
<button mat-icon-button matTooltip="Use" (click)="useTemplate($event)">
<mat-icon class="material-icons">play_arrow</mat-icon> <mat-icon class="material-icons">play_arrow</mat-icon>
</button> </button>
</mat-panel-description> </mat-panel-description>
@ -37,13 +34,15 @@
<ng-container <ng-container
*ngFor="let config of template.config | keyvalue : originalOrder" *ngFor="let config of template.config | keyvalue : originalOrder"
[ngTemplateOutlet]="RPCTemplateRef" [ngTemplateOutlet]="RPCTemplateRef"
[ngTemplateOutletContext]="{ $implicit: config, padding: false }"> [ngTemplateOutletContext]="{ $implicit: config, innerValue: false }">
</ng-container> </ng-container>
<ng-template #RPCTemplateRef let-config let-padding='padding'> <ng-template #RPCTemplateRef let-config let-innerValue='innerValue'>
<div [fxLayout]="isObject(config.value) ? 'column': 'row'" <div [fxLayout]="isObject(config.value) ? 'column': 'row'"
[fxLayoutAlign]="!isObject(config.value) ? 'space-between center' : ''" [fxLayoutAlign]="!isObject(config.value) ? 'space-between center' : ''"
[ngStyle]="{'padding-left': padding ? '16px': '0'}"> [ngStyle]="{'padding-left': innerValue ? '16px': '0'}">
<div class="template-key">{{config.key}}</div> <div class="template-key">
{{!innerValue ? ('gateway.rpc.' + config.key | translate) : config.key}}
</div>
<div *ngIf="!isObject(config.value) else RPCObjectRow" <div *ngIf="!isObject(config.value) else RPCObjectRow"
[ngClass]="{'boolean-true': config.value === true, [ngClass]="{'boolean-true': config.value === true,
'boolean-false': config.value === false }"> 'boolean-false': config.value === false }">
@ -52,7 +51,7 @@
<ng-container <ng-container
*ngFor="let subConfig of config.value | keyvalue : originalOrder" *ngFor="let subConfig of config.value | keyvalue : originalOrder"
[ngTemplateOutlet]="RPCTemplateRef" [ngTemplateOutlet]="RPCTemplateRef"
[ngTemplateOutletContext]="{ $implicit: subConfig, padding: true }"> [ngTemplateOutletContext]="{ $implicit: subConfig, innerValue: true }">
</ng-container> </ng-container>
</ng-template> </ng-template>
</div> </div>

5
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss

@ -50,6 +50,11 @@
background-color: rgba(25, 128, 56, 0.08); background-color: rgba(25, 128, 56, 0.08);
} }
mat-expansion-panel {
margin-top: 10px;
overflow: visible;
}
.mat-expansion-panel-header-description { .mat-expansion-panel-header-description {
flex-direction: row-reverse; flex-direction: row-reverse;
align-items: center; align-items: center;

69
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.ts

@ -15,9 +15,16 @@
/// ///
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { ConnectorType, RPCTemplate } from '@home/components/widget/lib/gateway/gateway-widget.models'; import {
import { TranslateService } from '@ngx-translate/core'; ConnectorType,
RPCTemplate
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { KeyValue } from '@angular/common'; import { KeyValue } from '@angular/common';
import { EntityType } from '@shared/models/entity-type.models';
import { AttributeScope } from '@shared/models/telemetry/telemetry.models';
import { AttributeService } from '@core/http/attribute.service';
import { WidgetContext } from '@home/models/widget-component.models';
import { MatDialog } from '@angular/material/dialog';
@Component({ @Component({
selector: 'tb-gateway-service-rpc-connector-templates', selector: 'tb-gateway-service-rpc-connector-templates',
@ -29,52 +36,52 @@ export class GatewayServiceRPCConnectorTemplatesComponent implements OnInit {
@Input() @Input()
connectorType: ConnectorType; connectorType: ConnectorType;
@Input()
ctx: WidgetContext;
@Output() @Output()
saveTemplate: EventEmitter<any> = new EventEmitter(); saveTemplate: EventEmitter<any> = new EventEmitter();
rpcTemplates: Array<RPCTemplate> = []; @Output()
useTemplate: EventEmitter<any> = new EventEmitter();
constructor(private translate: TranslateService) {
this.rpcTemplates.push(
{
name: 'Test Template',
config: {
fieldString: 'string',
fieldNumber: 666,
fieldBool: true,
fieldArray: [111, 222, 333, "String", "444"],
fieldObj: {
subKey1: 'dasd',
subKey2: 666,
}
}
}
)
}
ngOnInit() { @Input()
rpcTemplates: Array<RPCTemplate>;
constructor(private attributeService: AttributeService) {
} }
public useTemplate($event: Event): void { ngOnInit() {
$event.stopPropagation();
console.log("useTemplate")
} }
public copyTemplate($event: Event): void { public applyTemplate($event: Event, template: RPCTemplate): void {
$event.stopPropagation(); $event.stopPropagation();
console.log("copyTemplate") this.useTemplate.emit(template);
} }
public deleteTemplate($event: Event): void { public deleteTemplate($event: Event, template: RPCTemplate): void {
$event.stopPropagation(); $event.stopPropagation();
console.log("deleteTemplate") const index = this.rpcTemplates.findIndex(data => {
return data.name == template.name;
})
this.rpcTemplates.splice(index, 1);
const key = `${this.connectorType}_template`;
this.attributeService.saveEntityAttributes(
{
id: this.ctx.defaultSubscription.targetDeviceId,
entityType: EntityType.DEVICE
}
, AttributeScope.SERVER_SCOPE, [{
key,
value: this.rpcTemplates
}]).subscribe(() => {
})
} }
public originalOrder = (a: KeyValue<string, any>, b: KeyValue<string, any>): number => { public originalOrder = (a, b): number => {
return 0; return 0;
} }
public isObject(value: any) { public isObject(value: any) {
return value !== null && typeof value === 'object' && !Array.isArray(value); return value !== null && typeof value === 'object' && !Array.isArray(value);
} }

136
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html

@ -16,7 +16,8 @@
--> -->
<div fxLayout="column" class="command-form" [formGroup]="commandForm"> <div fxLayout="column" class="command-form" [formGroup]="commandForm">
<div class="mat-subtitle-1 title">{{ 'gateway.rpc.title' | translate: {type: gatewayConnectorDefaultTypesTranslates.get(connectorType)} }}</div> <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"> <ng-template [ngSwitchCase]="ConnectorType.MQTT">
@ -26,27 +27,26 @@
placeholder="echo"/> placeholder="echo"/>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.request-topic-expression' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.requestTopicExpression' | translate }}</mat-label>
<input matInput formControlName="requestTopicExpression" <input matInput formControlName="requestTopicExpression"
placeholder="sensor/${deviceName}/request/${methodName}/${requestId}"/> placeholder="sensor/${deviceName}/request/${methodName}/${requestId}"/>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.response-topic-expression' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.responseTopicExpression' | translate }}</mat-label>
<input matInput formControlName="responseTopicExpression" <input matInput formControlName="responseTopicExpression"
placeholder="sensor/${deviceName}/response/${methodName}/${requestId}"/> placeholder="sensor/${deviceName}/response/${methodName}/${requestId}"/>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.response-timeout' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.responseTimeout' | translate }}</mat-label>
<input matInput formControlName="responseTimeout" type="number" <input matInput formControlName="responseTimeout" type="number"
placeholder="10000" min="10" step="1"/> placeholder="10000" min="10" step="1"/>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.value-expression' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.valueExpression' | translate }}</mat-label>
<input matInput formControlName="valueExpression" <input matInput formControlName="valueExpression"
placeholder="${params}"/> placeholder="${params}"/>
</mat-form-field> </mat-form-field>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="ConnectorType.MODBUS"> <ng-template [ngSwitchCase]="ConnectorType.MODBUS">
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.tag' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.tag' | translate }}</mat-label>
@ -62,7 +62,7 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field fxFlex="50" class="mat-block"> <mat-form-field fxFlex="50" class="mat-block">
<mat-label>{{ 'gateway.rpc.function-code' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.functionCode' | translate }}</mat-label>
<mat-select formControlName="functionCode"> <mat-select formControlName="functionCode">
<mat-option *ngFor="let code of codesArray" [value]="code"> <mat-option *ngFor="let code of codesArray" [value]="code">
{{ code }} {{ code }}
@ -77,20 +77,19 @@
placeholder="1" step="1"/> placeholder="1" step="1"/>
</mat-form-field> </mat-form-field>
<mat-form-field fxFlex="50"> <mat-form-field fxFlex="50">
<mat-label>{{ 'gateway.rpc.objects-count' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.objectsCount' | translate }}</mat-label>
<input matInput formControlName="objectsCount" type="number" min="0" <input matInput formControlName="objectsCount" type="number" min="0"
placeholder="31" step="1"/> placeholder="31" step="1"/>
</mat-form-field> </mat-form-field>
</div> </div>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="ConnectorType.BACNET"> <ng-template [ngSwitchCase]="ConnectorType.BACNET">
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.method-rpc' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.methodRPC' | translate }}</mat-label>
<input matInput formControlName="method" placeholder="set_state"/> <input matInput formControlName="method" placeholder="set_state"/>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label>{{ 'gateway.rpc.request-type' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.requestType' | translate }}</mat-label>
<mat-select formControlName="requestType"> <mat-select formControlName="requestType">
<mat-option *ngFor="let type of bACnetRequestTypes" [value]="type"> <mat-option *ngFor="let type of bACnetRequestTypes" [value]="type">
{{bACnetRequestTypesTranslates.get(type) | translate}} {{bACnetRequestTypesTranslates.get(type) | translate}}
@ -98,13 +97,13 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.request-timeout' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.requestTimeout' | translate }}</mat-label>
<input matInput formControlName="requestTimeout" type="number" <input matInput formControlName="requestTimeout" type="number"
min="10" step="1" placeholder="1000"/> min="10" step="1" placeholder="1000"/>
</mat-form-field> </mat-form-field>
<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">
<mat-label>{{ 'gateway.rpc.object-type' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.objectType' | translate }}</mat-label>
<mat-select formControlName="objectType"> <mat-select formControlName="objectType">
<mat-option *ngFor="let type of bACnetObjectTypes" [value]="type"> <mat-option *ngFor="let type of bACnetObjectTypes" [value]="type">
{{bACnetObjectTypesTranslates.get(type) | translate}} {{bACnetObjectTypesTranslates.get(type) | translate}}
@ -118,22 +117,21 @@
</mat-form-field> </mat-form-field>
</div> </div>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label>{{ 'gateway.rpc.property-id' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.propertyId' | translate }}</mat-label>
<input matInput formControlName="propertyId" placeholder="presentValue"/> <input matInput formControlName="propertyId" placeholder="presentValue"/>
</mat-form-field> </mat-form-field>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="ConnectorType.BLE"> <ng-template [ngSwitchCase]="ConnectorType.BLE">
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.method-rpc' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.methodRPC' | translate }}</mat-label>
<input matInput formControlName="methodRPC" placeholder="rpcMethod1"/> <input matInput formControlName="methodRPC" placeholder="rpcMethod1"/>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.characteristic-uuid' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.characteristicUUID' | translate }}</mat-label>
<input matInput formControlName="characteristicUUID" placeholder="00002A00-0000-1000-8000-00805F9B34FB"/> <input matInput formControlName="characteristicUUID" placeholder="00002A00-0000-1000-8000-00805F9B34FB"/>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label>{{ 'gateway.rpc.method-processing' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.methodProcessing' | translate }}</mat-label>
<mat-select formControlName="methodProcessing"> <mat-select formControlName="methodProcessing">
<mat-option *ngFor="let type of bLEMethods" [value]="type"> <mat-option *ngFor="let type of bLEMethods" [value]="type">
{{bLEMethodsTranslates.get(type) | translate}} {{bLEMethodsTranslates.get(type) | translate}}
@ -141,35 +139,34 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-slide-toggle class="mat-slide" formControlName="withResponse"> <mat-slide-toggle class="mat-slide" formControlName="withResponse">
{{ 'gateway.rpc.with-response' | translate }} {{ 'gateway.rpc.withResponse' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="ConnectorType.CAN"> <ng-template [ngSwitchCase]="ConnectorType.CAN">
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.method-rpc' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.methodRPC' | translate }}</mat-label>
<input matInput formControlName="method" placeholder="sendSameData"/> <input matInput formControlName="method" placeholder="sendSameData"/>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.node-id' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.nodeID' | translate }}</mat-label>
<input matInput formControlName="nodeID" type="number" placeholder="4" min="0" step="1"/> <input matInput formControlName="nodeID" type="number" placeholder="4" min="0" step="1"/>
</mat-form-field> </mat-form-field>
<mat-slide-toggle class="mat-slide margin" formControlName="isExtendedID"> <mat-slide-toggle class="mat-slide margin" formControlName="isExtendedID">
{{ 'gateway.rpc.is-extended-id' | translate }} {{ 'gateway.rpc.isExtendedID' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<mat-slide-toggle class="mat-slide margin" formControlName="isFD"> <mat-slide-toggle class="mat-slide margin" formControlName="isFD">
{{ 'gateway.rpc.is-fd' | translate }} {{ 'gateway.rpc.isFD' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<mat-slide-toggle class="mat-slide margin" formControlName="bitrateSwitch"> <mat-slide-toggle class="mat-slide margin" formControlName="bitrateSwitch">
{{ 'gateway.rpc.bitrate-switch' | translate }} {{ 'gateway.rpc.bitrateSwitch' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<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.data-length' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.dataLength' | translate }}</mat-label>
<input matInput formControlName="dataLength" type="number" placeholder="2" min="1" step="1"/> <input matInput formControlName="dataLength" type="number" placeholder="2" min="1" step="1"/>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block" fxFlex="50"> <mat-form-field class="mat-block" fxFlex="50">
<mat-label>{{ 'gateway.rpc.data-byte-order' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.dataByteorder' | translate }}</mat-label>
<mat-select formControlName="dataByteorder"> <mat-select formControlName="dataByteorder">
<mat-option *ngFor="let order of cANByteOrders" [value]="order"> <mat-option *ngFor="let order of cANByteOrders" [value]="order">
{{ order | translate }} {{ order | translate }}
@ -179,59 +176,55 @@
</div> </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.data-before' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.dataBefore' | translate }}</mat-label>
<input matInput formControlName="dataBefore" placeholder="00AA"/> <input matInput formControlName="dataBefore" placeholder="00AA"/>
</mat-form-field> </mat-form-field>
<mat-form-field fxFlex="50"> <mat-form-field fxFlex="50">
<mat-label>{{ 'gateway.rpc.data-after' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.dataAfter' | translate }}</mat-label>
<input matInput formControlName="dataAfter" placeholder="0102"/> <input matInput formControlName="dataAfter" placeholder="0102"/>
</mat-form-field> </mat-form-field>
</div> </div>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.data-in-hex' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.dataInHEX' | translate }}</mat-label>
<input matInput formControlName="dataInHEX" <input matInput formControlName="dataInHEX"
placeholder="aa bb cc dd ee ff aa bb aa bb cc d ee ff"/> placeholder="aa bb cc dd ee ff aa bb aa bb cc d ee ff"/>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.data-expression' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.dataExpression' | translate }}</mat-label>
<input matInput formControlName="dataExpression" <input matInput formControlName="dataExpression"
placeholder="userSpeed if maxAllowedSpeed > userSpeed else maxAllowedSpeed"/> placeholder="userSpeed if maxAllowedSpeed > userSpeed else maxAllowedSpeed"/>
</mat-form-field> </mat-form-field>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="ConnectorType.FTP"> <ng-template [ngSwitchCase]="ConnectorType.FTP">
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.method-filter' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.methodFilter' | translate }}</mat-label>
<input matInput formControlName="methodFilter" placeholder="read"/> <input matInput formControlName="methodFilter" placeholder="read"/>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.value-expression' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.valueExpression' | translate }}</mat-label>
<input matInput formControlName="valueExpression" placeholder="${params}"/> <input matInput formControlName="valueExpression" placeholder="${params}"/>
</mat-form-field> </mat-form-field>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="ConnectorType.OCPP"> <ng-template [ngSwitchCase]="ConnectorType.OCPP">
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.method-rpc' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.methodRPC' | translate }}</mat-label>
<input matInput formControlName="methodRPC" placeholder="rpc1"/> <input matInput formControlName="methodRPC" placeholder="rpc1"/>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.value-expression' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.valueExpression' | translate }}</mat-label>
<input matInput formControlName="valueExpression" placeholder="${params}"/> <input matInput formControlName="valueExpression" placeholder="${params}"/>
</mat-form-field> </mat-form-field>
<mat-slide-toggle class="mat-slide margin" formControlName="withResponse"> <mat-slide-toggle class="mat-slide margin" formControlName="withResponse">
{{ 'gateway.rpc.with-response' | translate }} {{ 'gateway.rpc.withResponse' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="ConnectorType.SOCKET"> <ng-template [ngSwitchCase]="ConnectorType.SOCKET">
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.method-rpc' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.methodRPC' | translate }}</mat-label>
<input matInput formControlName="methodRPC" placeholder="rpcMethod1"/> <input matInput formControlName="methodRPC" placeholder="rpcMethod1"/>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label>{{ 'gateway.rpc.method-processing' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.methodProcessing' | translate }}</mat-label>
<mat-select formControlName="methodProcessing"> <mat-select formControlName="methodProcessing">
<mat-option *ngFor="let method of socketMethodProcessings" [value]="method"> <mat-option *ngFor="let method of socketMethodProcessings" [value]="method">
{{ SocketMethodProcessingsTranslates.get(method) | translate }} {{ SocketMethodProcessingsTranslates.get(method) | translate }}
@ -247,27 +240,25 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-slide-toggle class="mat-slide margin" formControlName="withResponse"> <mat-slide-toggle class="mat-slide margin" formControlName="withResponse">
{{ 'gateway.rpc.with-response' | translate }} {{ 'gateway.rpc.withResponse' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="ConnectorType.XMPP"> <ng-template [ngSwitchCase]="ConnectorType.XMPP">
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.method-rpc' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.methodRPC' | translate }}</mat-label>
<input matInput formControlName="methodRPC" placeholder="rpc1"/> <input matInput formControlName="methodRPC" placeholder="rpc1"/>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.value-expression' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.valueExpression' | translate }}</mat-label>
<input matInput formControlName="valueExpression" placeholder="${params}"/> <input matInput formControlName="valueExpression" placeholder="${params}"/>
</mat-form-field> </mat-form-field>
<mat-slide-toggle class="mat-slide margin" formControlName="withResponse"> <mat-slide-toggle class="mat-slide margin" formControlName="withResponse">
{{ 'gateway.rpc.with-response' | translate }} {{ 'gateway.rpc.withResponse' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="ConnectorType.SNMP"> <ng-template [ngSwitchCase]="ConnectorType.SNMP">
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.request-filter' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.requestFilter' | translate }}</mat-label>
<input matInput formControlName="requestFilter" placeholder="setData"/> <input matInput formControlName="requestFilter" placeholder="setData"/>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
@ -298,15 +289,14 @@
</button> </button>
</fieldset> </fieldset>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="ConnectorType.REST"> <ng-template [ngSwitchCase]="ConnectorType.REST">
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.method-filter' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.methodFilter' | translate }}</mat-label>
<input matInput formControlName="methodFilter" placeholder="post_attributes"/> <input matInput formControlName="methodFilter" placeholder="post_attributes"/>
</mat-form-field> </mat-form-field>
<div fxFlex fxLayout="row" fxLayoutGap="10px"> <div fxFlex fxLayout="row" fxLayoutGap="10px">
<mat-form-field class="mat-block" fxFlex="33"> <mat-form-field class="mat-block" fxFlex="33">
<mat-label>{{ 'gateway.rpc.http-method' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.HTTPMethod' | translate }}</mat-label>
<mat-select formControlName="HTTPMethod"> <mat-select formControlName="HTTPMethod">
<mat-option *ngFor="let method of hTTPMethods" [value]="method"> <mat-option *ngFor="let method of hTTPMethods" [value]="method">
{{ method }} {{ method }}
@ -314,14 +304,14 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label>{{ 'gateway.rpc.request-url' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.requestUrlExpression' | translate }}</mat-label>
<input matInput formControlName="requestUrlExpression" <input matInput formControlName="requestUrlExpression"
placeholder="http://127.0.0.1:5000/my_devices"/> placeholder="http://127.0.0.1:5000/my_devices"/>
</mat-form-field> </mat-form-field>
</div> </div>
<div fxFlex fxLayout="row" fxLayoutGap="10px"> <div fxFlex fxLayout="row" fxLayoutGap="10px">
<mat-form-field fxFlex="33"> <mat-form-field fxFlex="33">
<mat-label>{{ 'gateway.rpc.response-timeout' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.responseTimeout' | translate }}</mat-label>
<input matInput formControlName="responseTimeout" type="number" <input matInput formControlName="responseTimeout" type="number"
step="1" min="10" placeholder="10"/> step="1" min="10" placeholder="10"/>
</mat-form-field> </mat-form-field>
@ -337,11 +327,11 @@
</mat-form-field> </mat-form-field>
</div> </div>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.value-expression' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.valueExpression' | translate }}</mat-label>
<input matInput formControlName="valueExpression" placeholder="${params}"/> <input matInput formControlName="valueExpression" placeholder="${params}"/>
</mat-form-field> </mat-form-field>
<fieldset class="fields border" fxLayout="column" fxLayoutGap="10px" formArrayName="httpHeaders"> <fieldset class="fields border" fxLayout="column" fxLayoutGap="10px" formArrayName="httpHeaders">
<span class="fields-label">{{ 'gateway.rpc.http-headers' | translate }}</span> <span class="fields-label">{{ 'gateway.rpc.httpHeaders' | translate }}</span>
<div class="border" fxLayout="column" fxLayoutGap="10px" *ngIf="getFormArrayControls('httpHeaders').length"> <div class="border" fxLayout="column" fxLayoutGap="10px" *ngIf="getFormArrayControls('httpHeaders').length">
<div fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="center center"> <div fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="center center">
<span fxFlex class="title">{{ 'gateway.rpc.header-name' | translate }}</span> <span fxFlex class="title">{{ 'gateway.rpc.header-name' | translate }}</span>
@ -405,15 +395,14 @@
</button> </button>
</fieldset> </fieldset>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="ConnectorType.REQUEST"> <ng-template [ngSwitchCase]="ConnectorType.REQUEST">
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.method-filter' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.methodFilter' | translate }}</mat-label>
<input matInput formControlName="methodFilter" placeholder="echo"/> <input matInput formControlName="methodFilter" placeholder="echo"/>
</mat-form-field> </mat-form-field>
<div fxFlex fxLayout="row" fxLayoutGap="10px"> <div fxFlex fxLayout="row" fxLayoutGap="10px">
<mat-form-field class="mat-block" fxFlex="33"> <mat-form-field class="mat-block" fxFlex="33">
<mat-label>{{ 'gateway.rpc.method' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.httpMethod' | translate }}</mat-label>
<mat-select formControlName="httpMethod"> <mat-select formControlName="httpMethod">
<mat-option *ngFor="let method of hTTPMethods" [value]="method"> <mat-option *ngFor="let method of hTTPMethods" [value]="method">
{{ method }} {{ method }}
@ -421,13 +410,13 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label>{{ 'gateway.rpc.request-url' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.requestUrlExpression' | translate }}</mat-label>
<input matInput formControlName="requestUrlExpression" placeholder="http://127.0.0.1:5000/my_devices"/> <input matInput formControlName="requestUrlExpression" placeholder="http://127.0.0.1:5000/my_devices"/>
</mat-form-field> </mat-form-field>
</div> </div>
<div fxFlex fxLayout="row" fxLayoutGap="10px"> <div fxFlex fxLayout="row" fxLayoutGap="10px">
<mat-form-field fxFlex="33"> <mat-form-field fxFlex="33">
<mat-label>{{ 'gateway.rpc.response-timeout' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.responseTimeout' | translate }}</mat-label>
<input matInput formControlName="responseTimeout" type="number" <input matInput formControlName="responseTimeout" type="number"
step="1" min="10" placeholder="10"/> step="1" min="10" placeholder="10"/>
</mat-form-field> </mat-form-field>
@ -443,15 +432,15 @@
</mat-form-field> </mat-form-field>
</div> </div>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.request-value-expression' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.requestValueExpression' | translate }}</mat-label>
<input matInput formControlName="requestValueExpression" placeholder="${params}"/> <input matInput formControlName="requestValueExpression" placeholder="${params}"/>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{ 'gateway.rpc.response-value-expression' | translate }}</mat-label> <mat-label>{{ 'gateway.rpc.responseValueExpression' | translate }}</mat-label>
<input matInput formControlName="responseValueExpression" placeholder="${temp}"/> <input matInput formControlName="responseValueExpression" placeholder="${temp}"/>
</mat-form-field> </mat-form-field>
<fieldset class="fields border" fxLayout="column" fxLayoutGap="10px" formArrayName="httpHeaders"> <fieldset class="fields border" fxLayout="column" fxLayoutGap="10px" formArrayName="httpHeaders">
<span class="fields-label">{{ 'gateway.rpc.http-headers' | translate }}</span> <span class="fields-label">{{ 'gateway.rpc.httpHeaders' | translate }}</span>
<div class="border" fxLayout="column" fxLayoutGap="10px" *ngIf="getFormArrayControls('httpHeaders').length"> <div class="border" fxLayout="column" fxLayoutGap="10px" *ngIf="getFormArrayControls('httpHeaders').length">
<div fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="center center"> <div fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="center center">
<span fxFlex class="title">{{ 'gateway.rpc.header-name' | translate }}</span> <span fxFlex class="title">{{ 'gateway.rpc.header-name' | translate }}</span>
@ -483,7 +472,6 @@
</button> </button>
</fieldset> </fieldset>
</ng-template> </ng-template>
<ng-template [ngSwitchCase]="ConnectorType.OPCUA_ASYNCIO"> <ng-template [ngSwitchCase]="ConnectorType.OPCUA_ASYNCIO">
<ng-container *ngTemplateOutlet="OPCUAForm"></ng-container> <ng-container *ngTemplateOutlet="OPCUAForm"></ng-container>
</ng-template> </ng-template>
@ -513,11 +501,27 @@
</button> </button>
</fieldset> </fieldset>
</ng-template> </ng-template>
<ng-template ngSwitchDefault>
{{'dsadas'}}
<mat-form-field>
<mat-label>{{ 'gateway.statistics.command' | translate }}</mat-label>
<input matInput formControlName="command"/>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'widget-config.datasource-parameters' | translate }}</mat-label>
<input matInput formControlName="params" type="JSON"/>
<mat-icon class="material-icons-outlined" aria-hidden="false" aria-label="help-icon"
matIconSuffix style="cursor:pointer;"
(click)="openEditJSONDialog($event)"
matTooltip="{{ 'gateway.rpc-command-edit-params' | translate }}">edit
</mat-icon>
</mat-form-field>
</ng-template>
</ng-container> </ng-container>
</ng-template> </ng-template>
<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)="sendCommand.emit()" (click)="save()"
[disabled]="commandForm.invalid"> [disabled]="commandForm.invalid">
{{ 'gateway.rpc-command-save-template' | translate }} {{ 'gateway.rpc-command-save-template' | translate }}
</button> </button>

181
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts

@ -14,27 +14,57 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';
import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { import {
BACnetObjectTypes, BACnetObjectTypesTranslates, ControlValueAccessor,
BACnetRequestTypes, BACnetRequestTypesTranslates, BLEMethods, BLEMethodsTranslates, FormArray,
FormBuilder,
FormControl,
FormGroup,
NG_VALUE_ACCESSOR,
Validators
} from '@angular/forms';
import {
BACnetObjectTypes,
BACnetObjectTypesTranslates,
BACnetRequestTypes,
BACnetRequestTypesTranslates,
BLEMethods,
BLEMethodsTranslates,
CANByteOrders, CANByteOrders,
ConnectorType, GatewayConnectorDefaultTypesTranslates, HTTPMethods, ConnectorType,
GatewayConnectorDefaultTypesTranslates,
HTTPMethods,
ModbusCommandTypes, ModbusCommandTypes,
RPCCommand, RPCCommand,
SNMPMethods, SNMPMethodsTranslations, RPCTemplateConfig,
SNMPMethods,
SNMPMethodsTranslations,
SocketEncodings, SocketEncodings,
SocketMethodProcessings, SocketMethodProcessingsTranslates SocketMethodProcessings,
SocketMethodProcessingsTranslates
} from '@home/components/widget/lib/gateway/gateway-widget.models'; } from '@home/components/widget/lib/gateway/gateway-widget.models';
import { TranslateService } from "@ngx-translate/core"; import { MatDialog } from '@angular/material/dialog';
import {
JsonObjectEditDialogComponent,
JsonObjectEditDialogData
} from '@shared/components/dialog/json-object-edit-dialog.component';
import { jsonRequired } from '@shared/components/json-object-edit.component';
import { deepClone } from '@core/utils';
@Component({ @Component({
selector: 'tb-gateway-service-rpc-connector', selector: 'tb-gateway-service-rpc-connector',
templateUrl: './gateway-service-rpc-connector.component.html', templateUrl: './gateway-service-rpc-connector.component.html',
styleUrls: ['./gateway-service-rpc-connector.component.scss'] styleUrls: ['./gateway-service-rpc-connector.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => GatewayServiceRPCConnectorComponent),
multi: true
}
]
}) })
export class GatewayServiceRPCConnectorComponent implements OnInit { export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValueAccessor {
@Input() @Input()
connectorType: ConnectorType; connectorType: ConnectorType;
@ -42,6 +72,9 @@ export class GatewayServiceRPCConnectorComponent implements OnInit {
@Output() @Output()
sendCommand: EventEmitter<RPCCommand> = new EventEmitter(); sendCommand: EventEmitter<RPCCommand> = new EventEmitter();
@Output()
saveTemplate: EventEmitter<RPCTemplateConfig> = new EventEmitter();
commandForm: FormGroup; commandForm: FormGroup;
codesArray: Array<number> = [1, 2, 3, 4, 5, 6, 15, 16]; codesArray: Array<number> = [1, 2, 3, 4, 5, 6, 15, 16];
@ -74,14 +107,41 @@ export class GatewayServiceRPCConnectorComponent implements OnInit {
'(\\#[-a-z\\d_]*)?$', // fragment locator '(\\#[-a-z\\d_]*)?$', // fragment locator
'i' 'i'
); );
private propagateChange = (v: any) => {
}
constructor(private fb: FormBuilder, constructor(private fb: FormBuilder,
private translate: TranslateService) { private dialog: MatDialog,) {
} }
ngOnInit() { ngOnInit() {
this.commandForm = this.connectorParamsFormGroupByType(this.connectorType) this.commandForm = this.connectorParamsFormGroupByType(this.connectorType);
this.commandForm.valueChanges.subscribe(value => {
const httpHeaders = {};
const security = {};
switch (this.connectorType) {
case ConnectorType.REST:
value.httpHeaders.forEach(data => {
httpHeaders[data.headerName] = data.value;
})
value.httpHeaders = httpHeaders;
value.security.forEach(data => {
security[data.securityName] = data.value;
})
value.security = security;
break;
case ConnectorType.REQUEST:
value.httpHeaders.forEach(data => {
httpHeaders[data.headerName] = data.value;
})
value.httpHeaders = httpHeaders;
break;
}
if (this.commandForm.valid) {
this.propagateChange({...this.commandForm.value,...value});
}
})
} }
connectorParamsFormGroupByType(type: ConnectorType): FormGroup { connectorParamsFormGroupByType(type: ConnectorType): FormGroup {
@ -206,6 +266,13 @@ export class GatewayServiceRPCConnectorComponent implements OnInit {
method: [null, [Validators.required]], method: [null, [Validators.required]],
arguments: this.fb.array([]), arguments: this.fb.array([]),
}) })
break;
default:
formGroup = this.fb.group({
command: [null, [Validators.required]],
params: ['{}', [jsonRequired]],
})
} }
return formGroup; return formGroup;
} }
@ -213,7 +280,7 @@ export class GatewayServiceRPCConnectorComponent implements OnInit {
addSNMPoid(value: string = null) { addSNMPoid(value: string = null) {
const oidsFA = this.commandForm.get('oid') as FormArray; const oidsFA = this.commandForm.get('oid') as FormArray;
if (oidsFA) { if (oidsFA) {
oidsFA.push(this.fb.control(value, [Validators.required])); oidsFA.push(this.fb.control(value, [Validators.required]), {emitEvent: false});
} }
} }
@ -229,7 +296,7 @@ export class GatewayServiceRPCConnectorComponent implements OnInit {
value: [value.value, [Validators.required]] value: [value.value, [Validators.required]]
}) })
if (headerFA) { if (headerFA) {
headerFA.push(formGroup); headerFA.push(formGroup, {emitEvent: false});
} }
} }
@ -245,7 +312,7 @@ export class GatewayServiceRPCConnectorComponent implements OnInit {
value: [value.value, [Validators.required]] value: [value.value, [Validators.required]]
}) })
if (securityFA) { if (securityFA) {
securityFA.push(formGroup); securityFA.push(formGroup, {emitEvent: false});
} }
} }
@ -261,7 +328,7 @@ export class GatewayServiceRPCConnectorComponent implements OnInit {
addOCPUAArguments(value: string = null) { addOCPUAArguments(value: string = null) {
const oidsFA = this.commandForm.get('arguments') as FormArray; const oidsFA = this.commandForm.get('arguments') as FormArray;
if (oidsFA) { if (oidsFA) {
oidsFA.push(this.fb.control(value)); oidsFA.push(this.fb.control(value), {emitEvent: false});
} }
} }
@ -269,4 +336,86 @@ export class GatewayServiceRPCConnectorComponent implements OnInit {
const oidsFA = this.commandForm.get('arguments') as FormArray; const oidsFA = this.commandForm.get('arguments') as FormArray;
oidsFA.removeAt(index); oidsFA.removeAt(index);
} }
openEditJSONDialog($event: Event) {
if ($event) {
$event.stopPropagation();
}
this.dialog.open<JsonObjectEditDialogComponent, JsonObjectEditDialogData, object>(JsonObjectEditDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
jsonValue: JSON.parse(this.commandForm.get('params').value),
required: true
}
}).afterClosed().subscribe(
(res) => {
if (res) {
this.commandForm.get('params').setValue(JSON.stringify(res));
}
}
);
}
save() {
this.saveTemplate.emit();
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
clearFromArrayByName(name: string) {
const formArray = this.commandForm.get(name) as FormArray;
while (formArray.length !== 0) {
formArray.removeAt(0)
}
}
writeValue(value: RPCTemplateConfig): void {
if (typeof value == "object") {
value = deepClone(value);
switch (this.connectorType) {
case ConnectorType.SNMP:
this.clearFromArrayByName("oids");
value.oids.forEach(value => {
this.addSNMPoid(value)
})
delete value.oids;
break;
case ConnectorType.REQUEST:
this.clearFromArrayByName("httpHeaders");
value.httpHeaders && Object.entries(value.httpHeaders).forEach(httpHeader => {
this.addHTTPHeader({headerName: httpHeader[0], value: httpHeader[1] as string})
})
delete value.httpHeaders;
break;
case ConnectorType.REST:
this.clearFromArrayByName("httpHeaders");
this.clearFromArrayByName("security");
value.security && Object.entries(value.security).forEach(securityHeader => {
this.addHTTPSecurity({securityName: securityHeader[0], value: securityHeader[1] as string})
})
delete value.security;
value.httpHeaders && Object.entries(value.httpHeaders).forEach(httpHeader => {
this.addHTTPHeader({headerName: httpHeader[0], value: httpHeader[1] as string})
})
delete value.httpHeaders;
break;
case ConnectorType.OPCUA:
case ConnectorType.OPCUA_ASYNCIO:
this.clearFromArrayByName("arguments");
value.arguments.forEach(value => {
this.addOCPUAArguments(value)
})
delete value.arguments;
break;
}
this.commandForm.patchValue(value, {onlySelf: false});
}
}
} }

7
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html

@ -41,7 +41,8 @@
</button> </button>
</ng-container> </ng-container>
<ng-template #connectorForm> <ng-template #connectorForm>
<tb-gateway-service-rpc-connector [connectorType]="connectorType" (sendCommand)="sendCommand()"/> <tb-gateway-service-rpc-connector formControlName="params" [connectorType]="connectorType"
(sendCommand)="sendCommand()" (saveTemplate)="saveTemplate()"/>
</ng-template> </ng-template>
</div> </div>
<section class="result-block" [formGroup]="commandForm"> <section class="result-block" [formGroup]="commandForm">
@ -52,4 +53,6 @@
<tb-json-content [contentType]="contentTypes.JSON" readonly="true" formControlName="result"></tb-json-content> <tb-json-content [contentType]="contentTypes.JSON" readonly="true" formControlName="result"></tb-json-content>
</section> </section>
</div> </div>
<tb-gateway-service-rpc-connector-templates fxFlex="30" *ngIf="isConnector" class="border"></tb-gateway-service-rpc-connector-templates> <tb-gateway-service-rpc-connector-templates fxFlex="30" *ngIf="isConnector" class="border" [rpcTemplates]="templates"
[ctx]="ctx" [connectorType]="connectorType" (useTemplate)="useTemplate($event)">
</tb-gateway-service-rpc-connector-templates>

141
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts

@ -14,17 +14,28 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { OnInit, Component, Input } from '@angular/core'; import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { WidgetContext } from '@home/models/widget-component.models'; import { WidgetContext } from '@home/models/widget-component.models';
import { ContentType } from '@shared/models/constants'; import { ContentType } from '@shared/models/constants';
import {
JsonObjectEditDialogComponent,
JsonObjectEditDialogData
} 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 { ConnectorType, RPCCommand } from '@home/components/widget/lib/gateway/gateway-widget.models'; import {
ConnectorType,
RPCCommand,
RPCTemplate,
RPCTemplateConfig,
SaveRPCTemplateData
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import {
GatewayServiceRPCConnectorTemplateDialogComponent
} from '@home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog';
import { AttributeService } from '@core/http/attribute.service';
import { AttributeScope } from '@shared/models/telemetry/telemetry.models';
import { EntityType } from '@shared/models/entity-type.models';
import { DatasourceType, widgetType } from '@shared/models/widget.models';
import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models';
import { UtilsService } from '@core/services/utils.service';
@Component({ @Component({
selector: 'tb-gateway-service-rpc', selector: 'tb-gateway-service-rpc',
@ -58,9 +69,27 @@ export class GatewayServiceRPCComponent implements OnInit {
]; ];
public connectorType: ConnectorType; public connectorType: ConnectorType;
public templates: Array<RPCTemplate> = [];
private subscription: IWidgetSubscription;
private subscriptionOptions: WidgetSubscriptionOptions = {
callbacks: {
onDataUpdated: () => this.ctx.ngZone.run(() => {
this.updateTemplates()
}),
onDataUpdateError: (subscription, e) => this.ctx.ngZone.run(() => {
this.onDataUpdateError(e);
}),
dataLoading: () => {
}
}
};
constructor(private fb: FormBuilder, constructor(private fb: FormBuilder,
private dialog: MatDialog) { private dialog: MatDialog,
private utils: UtilsService,
private cd: ChangeDetectorRef,
private attributeService: AttributeService) {
this.commandForm = this.fb.group({ this.commandForm = this.fb.group({
command: [null, [Validators.required]], command: [null, [Validators.required]],
time: [60, [Validators.required, Validators.min(1)]], time: [60, [Validators.required, Validators.min(1)]],
@ -75,6 +104,17 @@ export class GatewayServiceRPCComponent implements OnInit {
this.commandForm.get('command').setValue(this.RPCCommands[0]); this.commandForm.get('command').setValue(this.RPCCommands[0]);
} else { } else {
this.connectorType = this.ctx.stateController.getStateParams().connector_rpc.value.type; this.connectorType = this.ctx.stateController.getStateParams().connector_rpc.value.type;
const subscriptionInfo = [{
type: DatasourceType.entity,
entityType: EntityType.DEVICE,
entityId: this.ctx.defaultSubscription.targetDeviceId,
entityName: 'Connector',
attributes: [{name: `${this.connectorType}_template`}]
}];
this.ctx.subscriptionApi.createSubscriptionFromInfo(widgetType.latest, subscriptionInfo,
this.subscriptionOptions, false, true).subscribe(subscription => {
this.subscription = subscription;
})
} }
} }
@ -82,7 +122,9 @@ export class GatewayServiceRPCComponent implements OnInit {
this.resultTime = null; this.resultTime = null;
const formValues = value || this.commandForm.value; const formValues = value || this.commandForm.value;
const commandPrefix = this.isConnector ? `${this.connectorType}_` : 'gateway_'; const commandPrefix = this.isConnector ? `${this.connectorType}_` : 'gateway_';
this.ctx.controlApi.sendTwoWayCommand(commandPrefix + formValues.command.toLowerCase(), formValues.params, formValues.time).subscribe({ const command = !this.isConnector ? formValues.command.toLowerCase() : this.getCommandFromParamsByType(formValues.params);
const params = formValues.params;
this.ctx.controlApi.sendTwoWayCommand(commandPrefix + command, params, formValues.time).subscribe({
next: resp => { next: resp => {
this.resultTime = new Date().getTime(); this.resultTime = new Date().getTime();
this.commandForm.get('result').setValue(JSON.stringify(resp)) this.commandForm.get('result').setValue(JSON.stringify(resp))
@ -95,23 +137,86 @@ export class GatewayServiceRPCComponent implements OnInit {
}); });
} }
openEditJSONDialog($event: Event) { getCommandFromParamsByType(params: RPCTemplateConfig) {
if ($event) { switch (this.connectorType) {
$event.stopPropagation(); case ConnectorType.MQTT:
case ConnectorType.FTP:
case ConnectorType.SNMP:
case ConnectorType.REST:
case ConnectorType.REQUEST:
return params.methodFilter;
case ConnectorType.MODBUS:
return params.tag;
case ConnectorType.BACNET:
case ConnectorType.CAN:
case ConnectorType.OPCUA:
case ConnectorType.OPCUA_ASYNCIO:
return params.method;
case ConnectorType.BLE:
case ConnectorType.OCPP:
case ConnectorType.SOCKET:
case ConnectorType.XMPP:
return params.methodRPC;
default:
return params.command;
} }
this.dialog.open<JsonObjectEditDialogComponent, JsonObjectEditDialogData, object>(JsonObjectEditDialogComponent, { }
saveTemplate() {
this.dialog.open<GatewayServiceRPCConnectorTemplateDialogComponent, SaveRPCTemplateData>
(GatewayServiceRPCConnectorTemplateDialogComponent, {
disableClose: true, disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: { data: {config: this.commandForm.value.params, templates: this.templates}
jsonValue: JSON.parse(this.commandForm.get('params').value),
required: true
}
}).afterClosed().subscribe( }).afterClosed().subscribe(
(res) => { (res) => {
if (res) { const templateAttribute: RPCTemplate = {
this.commandForm.get('params').setValue(JSON.stringify(res)); name: res,
config: this.commandForm.value.params
}
const templatesArray = this.templates;
const existingIndex = templatesArray.findIndex(template=>{
return template.name == templateAttribute.name;
})
if (existingIndex > -1 ){
templatesArray.splice(existingIndex, 1)
} }
templatesArray.push(templateAttribute)
const key = `${this.connectorType}_template`;
this.attributeService.saveEntityAttributes(
{
id: this.ctx.defaultSubscription.targetDeviceId,
entityType: EntityType.DEVICE
}
, AttributeScope.SERVER_SCOPE, [{
key,
value: templatesArray
}]).subscribe(() => {
})
} }
); );
} }
useTemplate($event) {
this.commandForm.get('params').patchValue($event.config);
}
private updateTemplates() {
this.templates = this.subscription.data[0].data[0][1].length ?
JSON.parse(this.subscription.data[0].data[0][1]) : [];
if (this.templates.length && this.commandForm.get('params').value == "{}") {
this.commandForm.get('params').patchValue(this.templates[0].config);
}
this.cd.detectChanges();
}
private onDataUpdateError(e: any) {
const exceptionData = this.utils.parseException(e);
let errorText = exceptionData.name;
if (exceptionData.message) {
errorText += ': ' + exceptionData.message;
}
console.error(errorText);
}
} }

15
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts

@ -261,10 +261,17 @@ export enum SocketEncodings {
} }
export interface RPCTemplate { export interface RPCTemplate {
name: string; name?: string;
config: { config: RPCTemplateConfig;
[key: string]: any; }
};
export interface RPCTemplateConfig {
[key: string]: any;
}
export interface SaveRPCTemplateData {
config: RPCTemplateConfig,
templates: Array<RPCTemplate>
} }
export interface LogLink { export interface LogLink {

5
ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts

@ -74,6 +74,9 @@ import { ValueChartCardWidgetComponent } from '@home/components/widget/lib/cards
import { ProgressBarWidgetComponent } from '@home/components/widget/lib/cards/progress-bar-widget.component'; import { ProgressBarWidgetComponent } from '@home/components/widget/lib/cards/progress-bar-widget.component';
import { LiquidLevelWidgetComponent } from '@home/components/widget/lib/indicator/liquid-level-widget.component'; import { LiquidLevelWidgetComponent } from '@home/components/widget/lib/indicator/liquid-level-widget.component';
import { DoughnutWidgetComponent } from '@home/components/widget/lib/chart/doughnut-widget.component'; import { DoughnutWidgetComponent } from '@home/components/widget/lib/chart/doughnut-widget.component';
import {
GatewayServiceRPCConnectorTemplateDialogComponent
} from '@home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog';
@NgModule({ @NgModule({
declarations: declarations:
@ -107,6 +110,7 @@ import { DoughnutWidgetComponent } from '@home/components/widget/lib/chart/dough
DeviceGatewayCommandComponent, DeviceGatewayCommandComponent,
GatewayConfigurationComponent, GatewayConfigurationComponent,
GatewayRemoteConfigurationDialogComponent, GatewayRemoteConfigurationDialogComponent,
GatewayServiceRPCConnectorTemplateDialogComponent,
ValueCardWidgetComponent, ValueCardWidgetComponent,
AggregatedValueCardWidgetComponent, AggregatedValueCardWidgetComponent,
CountWidgetComponent, CountWidgetComponent,
@ -154,6 +158,7 @@ import { DoughnutWidgetComponent } from '@home/components/widget/lib/chart/dough
DeviceGatewayCommandComponent, DeviceGatewayCommandComponent,
GatewayConfigurationComponent, GatewayConfigurationComponent,
GatewayRemoteConfigurationDialogComponent, GatewayRemoteConfigurationDialogComponent,
GatewayServiceRPCConnectorTemplateDialogComponent,
ValueCardWidgetComponent, ValueCardWidgetComponent,
AggregatedValueCardWidgetComponent, AggregatedValueCardWidgetComponent,
CountWidgetComponent, CountWidgetComponent,

69
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -2776,54 +2776,54 @@
"rpc": { "rpc": {
"title": "{{type}} Connector RPC parameters", "title": "{{type}} Connector RPC parameters",
"templates-title": "Connector RPC Templates", "templates-title": "Connector RPC Templates",
"method-filter": "Method filter", "methodFilter": "Method filter",
"request-topic-expression": "Request topic expression", "requestTopicExpression": "Request topic expression",
"response-topic-expression": "Response topic expression", "responseTopicExpression": "Response topic expression",
"response-timeout": "Response Time", "responseTimeout": "Response Time",
"value-expression": "Value Expression", "valueExpression": "Value Expression",
"tag": "Tag", "tag": "Tag",
"type": "Type", "type": "Type",
"function-code": "Function Code", "functionCode": "Function Code",
"objects-count": "Objects Count", "objectsCount": "Objects Count",
"address": "Address", "address": "Address",
"method": "Method", "method": "Method",
"request-type": "Request Type", "requestType": "Request Type",
"request-timeout": "Request Timeout", "requestTimeout": "Request Timeout",
"object-type": "Object type", "objectType": "Object type",
"identifier": "Identifier", "identifier": "Identifier",
"property-id": "Property ID", "propertyId": "Property ID",
"method-rpc": "Method RPC name", "methodRPC": "Method RPC name",
"with-response": "With Response", "withResponse": "With Response",
"characteristic-uuid": "Characteristic UUID", "characteristicUUID": "Characteristic UUID",
"method-processing": "Method Processing", "methodProcessing": "Method Processing",
"node-id": "Node ID", "nodeID": "Node ID",
"is-extended-id": "Is Extended ID", "isExtendedID": "Is Extended ID",
"is-fd": "Is FD", "isFD": "Is FD",
"bitrate-switch": "Bitrate Switch", "bitrateSwitch": "Bitrate Switch",
"data-in-hex": "Data In HEX", "dataInHEX": "Data In HEX",
"data-length": "Data Length", "dataLength": "Data Length",
"data-byte-order": "Data Byte Order", "dataByteorder": "Data Byte Order",
"data-before": "Data Before", "dataBefore": "Data Before",
"data-after": "Data After", "dataAfter": "Data After",
"data-expression": "Data Expression", "dataExpression": "Data Expression",
"encoding": "Encoding", "encoding": "Encoding",
"oid": "OID", "oid": "OID",
"add-oid": "Add OID", "add-oid": "Add OID",
"add-header": "Add header", "add-header": "Add header",
"add-security": "Add security", "add-security": "Add security",
"remove": "Remove", "remove": "Remove",
"request-filter": "Request Filter", "requestFilter": "Request Filter",
"request-url": "Request URL Expression", "requestUrlExpression": "Request URL Expression",
"http-method": "HTTP Method", "HTTPMethod": "HTTP Method",
"timeout": "Timeout", "timeout": "Timeout",
"tries": "Tries", "tries": "Tries",
"http-headers": "HTTP Headers", "httpHeaders": "HTTP Headers",
"header-name": "Header name", "header-name": "Header name",
"security-name": "Security name", "security-name": "Security name",
"value": "Value", "value": "Value",
"security": "Security", "security": "Security",
"response-value-expression": "Response Value Expression", "responseValueExpression": "Response Value Expression",
"request-value-expression": "Request Value Expression", "requestValueExpression": "Request Value Expression",
"arguments": "Arguments", "arguments": "Arguments",
"add-argument": "Add argument", "add-argument": "Add argument",
"write-property": "Write property", "write-property": "Write property",
@ -2843,7 +2843,12 @@
"multi-get": "Multiget", "multi-get": "Multiget",
"get-next": "Get next", "get-next": "Get next",
"bulk-get": "Bulk get", "bulk-get": "Bulk get",
"walk": "Walk" "walk": "Walk",
"save-template": "Save template",
"template-name": "Template name",
"template-name-required": "Template name is required.",
"template-name-duplicate": "The name is already used."
}, },
"other": "Other", "other": "Other",

Loading…
Cancel
Save