diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts
index 9fa1f7b5f2..18b2c2d862 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts
@@ -40,6 +40,7 @@ import { DatasourceType, widgetType } from '@shared/models/widget.models';
import { UtilsService } from '@core/services/utils.service';
import { EntityType } from '@shared/models/entity-type.models';
import {
+ ConnectorType,
GatewayConnector,
GatewayConnectorDefaultTypesTranslates,
GatewayLogLevel
@@ -198,10 +199,10 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
saveConnector(): void {
const value = this.connectorForm.value;
value.configuration = camelCase(value.name) + '.json';
- if (value.type !== 'grpc') {
+ if (value.type !== ConnectorType.GRPC) {
delete value.key;
}
- if (value.type !== 'custom') {
+ if (value.type !== ConnectorType.CUSTOM) {
delete value.class;
}
value.ts = new Date().getTime();
@@ -309,7 +310,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
private clearOutConnectorForm(): void {
this.connectorForm.setValue({
name: '',
- type: 'mqtt',
+ type: ConnectorType.MQTT,
logLevel: GatewayLogLevel.INFO,
key: 'auto',
class: '',
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html
new file mode 100644
index 0000000000..3c6b410ac2
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html
@@ -0,0 +1,60 @@
+
+
{{ 'gateway.rpc.templates-title' | translate }}
+
+
+
+ {{template.name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
{{config.key}}
+
+ {{config.value}}
+
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss
new file mode 100644
index 0000000000..33ce5fa6d8
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss
@@ -0,0 +1,79 @@
+/**
+ * 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.
+ */
+:host {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ padding: 0;
+
+ .title {
+ font-weight: 500;
+ }
+
+ .template-key {
+ color: rgba(0, 0, 0, 0.38);
+ height: 32px;
+ line-height: 32px;
+ }
+
+ .boolean-true, .boolean-false {
+ border-radius: 3px;
+ height: 32px;
+ line-height: 32px;
+ padding: 0 12px;
+ width: fit-content;
+ font-size: 14px;
+ text-transform: capitalize;
+ }
+
+ .boolean-false {
+ color: #d12730;
+ background-color: rgba(209, 39, 48, 0.08);
+ }
+
+ .boolean-true {
+ color: #198038;
+ background-color: rgba(25, 128, 56, 0.08);
+ }
+
+ .mat-expansion-panel-header-description {
+ flex-direction: row-reverse;
+ align-items: center;
+ margin-right: 0;
+
+ & > mat-icon {
+ margin-left: 15px;
+ color: rgba(0, 0, 0, 0.38);
+ }
+ }
+
+ .mat-expansion-panel-header {
+ padding: 0 0 0 12px;
+
+ &.mat-expansion-panel-header.mat-expanded {
+ height: 48px;
+ }
+
+ .mat-content.mat-content-hide-toggle {
+ margin-right: 0;
+ }
+ }
+
+
+}
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.ts
new file mode 100644
index 0000000000..26cdf24dd1
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.ts
@@ -0,0 +1,81 @@
+///
+/// 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, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { ConnectorType, RPCTemplate } from '@home/components/widget/lib/gateway/gateway-widget.models';
+import { TranslateService } from '@ngx-translate/core';
+import { KeyValue } from '@angular/common';
+
+@Component({
+ selector: 'tb-gateway-service-rpc-connector-templates',
+ templateUrl: './gateway-service-rpc-connector-templates.component.html',
+ styleUrls: ['./gateway-service-rpc-connector-templates.component.scss']
+})
+export class GatewayServiceRPCConnectorTemplatesComponent implements OnInit {
+
+ @Input()
+ connectorType: ConnectorType;
+
+ @Output()
+ saveTemplate: EventEmitter = new EventEmitter();
+
+ rpcTemplates: Array = [];
+
+ 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() {
+
+ }
+
+ public useTemplate($event: Event): void {
+ $event.stopPropagation();
+ console.log("useTemplate")
+ }
+
+ public copyTemplate($event: Event): void {
+ $event.stopPropagation();
+ console.log("copyTemplate")
+ }
+
+ public deleteTemplate($event: Event): void {
+ $event.stopPropagation();
+ console.log("deleteTemplate")
+ }
+
+ public originalOrder = (a: KeyValue, b: KeyValue): number => {
+ return 0;
+ }
+ public isObject(value: any) {
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html
new file mode 100644
index 0000000000..9bab9508a5
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html
@@ -0,0 +1,531 @@
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.scss
new file mode 100644
index 0000000000..65abdb50b3
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.scss
@@ -0,0 +1,85 @@
+/**
+ * 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.
+ */
+:host {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ padding: 0;
+
+ .title {
+ font-weight: 500;
+ }
+
+ .command-form {
+ flex-wrap: nowrap;
+
+ & > button {
+ margin-top: 10px;
+ }
+ }
+
+ .mat-mdc-slide-toggle.margin {
+ margin-bottom: 10px;
+ margin-left: 10px;
+ }
+
+ .fields {
+
+ ::ng-deep .mat-mdc-text-field-wrapper.mdc-text-field--outlined .mat-mdc-form-field-infix {
+ padding-top: 6px;
+ padding-bottom: 6px;
+ min-height: auto;
+ }
+
+ ::ng-deep .mat-mdc-form-field-subscript-wrapper {
+ display: none;
+ }
+ ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,
+ ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,
+ ::ng-deep .mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing {
+ border-color: #e0e0e0;
+ }
+
+ .fields-label {
+ font-weight: 500;
+ }
+ }
+
+ .border {
+ padding: 16px;
+ margin-bottom: 10px;
+ box-shadow: 0 0 0 0 rgb(0 0 0 / 20%), 0 0 0 0 rgb(0 0 0 / 14%), 0 0 0 0 rgb(0 0 0 / 12%);
+ border: solid 1px #e0e0e0;
+ border-radius: 4px;
+
+ .title {
+ color: rgba(0, 0, 0, .54);
+ }
+
+ .mat-icon {
+ color: rgba(0, 0, 0, .38);
+ }
+
+ .mat-divider {
+ margin-left: -16px;
+ margin-right: -16px;
+ margin-bottom: 16px;
+ }
+ }
+
+}
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts
new file mode 100644
index 0000000000..ce2c850741
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts
@@ -0,0 +1,272 @@
+///
+/// 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, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
+import {
+ BACnetObjectTypes, BACnetObjectTypesTranslates,
+ BACnetRequestTypes, BACnetRequestTypesTranslates, BLEMethods, BLEMethodsTranslates,
+ CANByteOrders,
+ ConnectorType, GatewayConnectorDefaultTypesTranslates, HTTPMethods,
+ ModbusCommandTypes,
+ RPCCommand,
+ SNMPMethods, SNMPMethodsTranslations,
+ SocketEncodings,
+ SocketMethodProcessings, SocketMethodProcessingsTranslates
+} from '@home/components/widget/lib/gateway/gateway-widget.models';
+import { TranslateService } from "@ngx-translate/core";
+
+@Component({
+ selector: 'tb-gateway-service-rpc-connector',
+ templateUrl: './gateway-service-rpc-connector.component.html',
+ styleUrls: ['./gateway-service-rpc-connector.component.scss']
+})
+export class GatewayServiceRPCConnectorComponent implements OnInit {
+
+ @Input()
+ connectorType: ConnectorType;
+
+ @Output()
+ sendCommand: EventEmitter = new EventEmitter();
+
+ commandForm: FormGroup;
+
+ codesArray: Array = [1, 2, 3, 4, 5, 6, 15, 16];
+
+ readonly ConnectorType = ConnectorType;
+
+ modbusCommandTypes = Object.values(ModbusCommandTypes) as ModbusCommandTypes[];
+ bACnetRequestTypes = Object.values(BACnetRequestTypes) as BACnetRequestTypes[];
+ bACnetObjectTypes = Object.values(BACnetObjectTypes) as BACnetObjectTypes[];
+ bLEMethods = Object.values(BLEMethods) as BLEMethods[];
+ cANByteOrders = Object.values(CANByteOrders) as CANByteOrders[];
+ socketMethodProcessings = Object.values(SocketMethodProcessings) as SocketMethodProcessings[];
+ socketEncodings = Object.values(SocketEncodings) as SocketEncodings[];
+ sNMPMethods = Object.values(SNMPMethods) as SNMPMethods[];
+ hTTPMethods = Object.values(HTTPMethods) as HTTPMethods[];
+
+ bACnetRequestTypesTranslates = BACnetRequestTypesTranslates;
+ bACnetObjectTypesTranslates = BACnetObjectTypesTranslates;
+ bLEMethodsTranslates = BLEMethodsTranslates;
+ SocketMethodProcessingsTranslates = SocketMethodProcessingsTranslates;
+ SNMPMethodsTranslations = SNMPMethodsTranslations;
+ gatewayConnectorDefaultTypesTranslates = GatewayConnectorDefaultTypesTranslates;
+
+ urlPattern = new RegExp(
+ '^(https?:\\/\\/)?' + // protocol
+ '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
+ '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR IP (v4) address
+ '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
+ '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
+ '(\\#[-a-z\\d_]*)?$', // fragment locator
+ 'i'
+ );
+
+ constructor(private fb: FormBuilder,
+ private translate: TranslateService) {
+ }
+
+
+ ngOnInit() {
+ this.commandForm = this.connectorParamsFormGroupByType(this.connectorType)
+ }
+
+ connectorParamsFormGroupByType(type: ConnectorType): FormGroup {
+ let formGroup: FormGroup;
+
+ switch (type) {
+ case ConnectorType.MQTT:
+ formGroup = this.fb.group({
+ methodFilter: [null, [Validators.required]],
+ requestTopicExpression: [null, [Validators.required]],
+ responseTopicExpression: [null, []],
+ responseTimeout: [null, [Validators.min(10), Validators.pattern("^[0-9]*$")]],
+ valueExpression: [null, [Validators.required]],
+ })
+ break;
+ case ConnectorType.MODBUS:
+ formGroup = this.fb.group({
+ tag: [null, [Validators.required]],
+ type: [null, [Validators.required]],
+ functionCode: [null, [Validators.required]],
+ address: [null, [Validators.required, Validators.min(0), Validators.pattern("^[0-9]*$")]],
+ objectsCount: [null, [Validators.required, Validators.min(0), Validators.pattern("^[0-9]*$")]]
+ })
+ break;
+ case ConnectorType.BACNET:
+ formGroup = this.fb.group({
+ method: [null, [Validators.required]],
+ requestType: [null, [Validators.required]],
+ requestTimeout: [null, [Validators.required, Validators.min(10), Validators.pattern("^[0-9]*$")]],
+ objectType: [null, []],
+ identifier: [null, [Validators.required, Validators.min(1), Validators.pattern("^[0-9]*$")]],
+ propertyId: [null, [Validators.required]]
+ })
+ break;
+ case ConnectorType.BLE:
+ formGroup = this.fb.group({
+ methodRPC: [null, [Validators.required]],
+ characteristicUUID: [null, [Validators.required]],
+ methodProcessing: [null, [Validators.required]],
+ withResponse: [false, []]
+ })
+ break;
+ case ConnectorType.CAN:
+ formGroup = this.fb.group({
+ method: [null, [Validators.required]],
+ nodeID: [null, [Validators.required, Validators.min(0), Validators.pattern("^[0-9]*$")]],
+ isExtendedID: [false, []],
+ isFD: [false, []],
+ bitrateSwitch: [false, []],
+ dataLength: [null, [Validators.min(1), Validators.pattern("^[0-9]*$")]],
+ dataByteorder: [null, []],
+ dataBefore: [null, []],
+ dataAfter: [null, []],
+ dataInHEX: [null, []],
+ dataExpression: [null, []]
+ })
+ break;
+ case ConnectorType.FTP:
+ formGroup = this.fb.group({
+ methodFilter: [null, [Validators.required]],
+ valueExpression: [null, [Validators.required]]
+ })
+ break;
+ case ConnectorType.OCPP:
+ formGroup = this.fb.group({
+ methodRPC: [null, [Validators.required]],
+ valueExpression: [null, [Validators.required]],
+ withResponse: [false, []]
+ })
+ break;
+ case ConnectorType.SOCKET:
+ formGroup = this.fb.group({
+ methodRPC: [null, [Validators.required]],
+ methodProcessing: [null, [Validators.required]],
+ encoding: [null, [Validators.required]],
+ withResponse: [false, []]
+ })
+ break;
+ case ConnectorType.XMPP:
+ formGroup = this.fb.group({
+ methodRPC: [null, [Validators.required]],
+ valueExpression: [null, [Validators.required]],
+ withResponse: [false, []]
+ })
+ break;
+ case ConnectorType.SNMP:
+ formGroup = this.fb.group({
+ requestFilter: [null, [Validators.required]],
+ method: [null, [Validators.required]],
+ oid: this.fb.array([], [Validators.required])
+ })
+ break;
+ case ConnectorType.REST:
+ formGroup = this.fb.group({
+ methodFilter: [null, [Validators.required]],
+ HTTPMethod: [null, [Validators.required]],
+ requestUrlExpression: [null, [Validators.required, Validators.pattern(this.urlPattern)]],
+ responseTimeout: [null, [Validators.required, Validators.min(10), Validators.pattern("^[0-9]*$")]],
+ timeout: [null, [Validators.required, Validators.min(10), Validators.pattern("^[0-9]*$")]],
+ tries: [null, [Validators.required, Validators.min(1), Validators.pattern("^[0-9]*$")]],
+ valueExpression: [null, [Validators.required]],
+ httpHeaders: this.fb.array([]),
+ security: this.fb.array([])
+ })
+ break;
+ case ConnectorType.REQUEST:
+ formGroup = this.fb.group({
+ methodFilter: [null, [Validators.required]],
+ httpMethod: [null, [Validators.required]],
+ requestUrlExpression: [null, [Validators.required, Validators.pattern(this.urlPattern)]],
+ responseTimeout: [null, [Validators.required, Validators.min(10), Validators.pattern("^[0-9]*$")]],
+ timeout: [null, [Validators.required, Validators.min(10), Validators.pattern("^[0-9]*$")]],
+ tries: [null, [Validators.required, Validators.min(1), Validators.pattern("^[0-9]*$")]],
+ requestValueExpression: [null, [Validators.required]],
+ responseValueExpression: [null, []],
+ httpHeaders: this.fb.array([]),
+ })
+ break;
+ case ConnectorType.OPCUA:
+ case ConnectorType.OPCUA_ASYNCIO:
+ formGroup = this.fb.group({
+ method: [null, [Validators.required]],
+ arguments: this.fb.array([]),
+ })
+ }
+ return formGroup;
+ }
+
+ addSNMPoid(value: string = null) {
+ const oidsFA = this.commandForm.get('oid') as FormArray;
+ if (oidsFA) {
+ oidsFA.push(this.fb.control(value, [Validators.required]));
+ }
+ }
+
+ removeSNMPoid(index: number) {
+ const oidsFA = this.commandForm.get('oid') as FormArray;
+ oidsFA.removeAt(index);
+ }
+
+ addHTTPHeader(value: { headerName: string, value: string } = {headerName: null, value: null}) {
+ const headerFA = this.commandForm.get('httpHeaders') as FormArray;
+ const formGroup = this.fb.group({
+ headerName: [value.headerName, [Validators.required]],
+ value: [value.value, [Validators.required]]
+ })
+ if (headerFA) {
+ headerFA.push(formGroup);
+ }
+ }
+
+ removeHTTPHeader(index: number) {
+ const oidsFA = this.commandForm.get('httpHeaders') as FormArray;
+ oidsFA.removeAt(index);
+ }
+
+ addHTTPSecurity(value: { securityName: string, value: string } = {securityName: null, value: null}) {
+ const securityFA = this.commandForm.get('security') as FormArray;
+ const formGroup = this.fb.group({
+ securityName: [value.securityName, [Validators.required]],
+ value: [value.value, [Validators.required]]
+ })
+ if (securityFA) {
+ securityFA.push(formGroup);
+ }
+ }
+
+ removeHTTPSecurity(index: number) {
+ const oidsFA = this.commandForm.get('security') as FormArray;
+ oidsFA.removeAt(index);
+ }
+
+ getFormArrayControls(path: string) {
+ 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));
+ }
+ }
+
+ removeOCPUAArguments(index: number) {
+ const oidsFA = this.commandForm.get('arguments') as FormArray;
+ oidsFA.removeAt(index);
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html
index 8a96f1849b..2397d1bcdd 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html
@@ -15,51 +15,41 @@
limitations under the License.
-->
-
-
-
- {{ 'gateway.statistics.command' | translate }}
-
-
- {{ command }}
-
-
-
-
- {{ 'gateway.statistics.timeout-ms' | translate }}
-
-
- {{ 'gateway.statistics.timeout-min' | translate }}
-
-
-
-
-
- {{ 'gateway.statistics.command' | translate }}
-
-
-
- {{ 'widget-config.datasource-parameters' | translate }}
-
- edit
-
-
-
-
-
-
+
+
+
+
+ {{ 'gateway.statistics.command' | translate }}
+
+
+ {{ command }}
+
+
+
+
+ {{ 'gateway.statistics.timeout-ms' | translate }}
+
+
+ {{ 'gateway.statistics.timeout-min' | translate }}
+
+
+
+
+
+
+
+
+
{{ 'gateway.rpc-command-result' | translate }}
schedule
{{ resultTime | date: 'yyyy/MM/dd HH:mm:ss' }}
-
-
-
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss
index 5dab1d91c1..00e9e8df63 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss
@@ -16,10 +16,20 @@
:host {
width: 100%;
height: 100%;
- overflow-x: auto;
+ overflow: auto;
display: flex;
- flex-direction: column;
- padding: 0;
+ flex-direction: row;
+ padding: 0 5px;
+
+ & > * {
+ height: 100%;
+ overflow: auto;
+ }
+
+ & > tb-gateway-service-rpc-connector-templates:last-child {
+ margin-left: 10px;
+ }
+
.command-form {
flex-wrap: nowrap;
@@ -63,6 +73,13 @@
flex: 1;
}
}
+
+ .border {
+ padding: 16px;
+ box-shadow: 0 0 0 0 rgb(0 0 0 / 20%), 0 0 0 0 rgb(0 0 0 / 14%), 0 0 0 0 rgb(0 0 0 / 12%);
+ border: solid 1px #e0e0e0;
+ border-radius: 4px;
+ }
}
:host ::ng-deep {
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts
index d19624e257..a3c96f7d49 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts
@@ -14,7 +14,7 @@
/// limitations under the License.
///
-import { AfterViewInit, Component, Input } from '@angular/core';
+import { OnInit, Component, Input } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { WidgetContext } from '@home/models/widget-component.models';
@@ -24,13 +24,14 @@ import {
JsonObjectEditDialogData
} from '@shared/components/dialog/json-object-edit-dialog.component';
import { jsonRequired } from '@shared/components/json-object-edit.component';
+import { ConnectorType, RPCCommand } from '@home/components/widget/lib/gateway/gateway-widget.models';
@Component({
selector: 'tb-gateway-service-rpc',
templateUrl: './gateway-service-rpc.component.html',
styleUrls: ['./gateway-service-rpc.component.scss']
})
-export class GatewayServiceRPCComponent implements AfterViewInit {
+export class GatewayServiceRPCComponent implements OnInit {
@Input()
ctx: WidgetContext;
@@ -56,19 +57,19 @@ export class GatewayServiceRPCComponent implements AfterViewInit {
'Reboot'
];
- private connectorType: string;
+ public connectorType: ConnectorType;
constructor(private fb: FormBuilder,
private dialog: MatDialog) {
this.commandForm = this.fb.group({
- command: [null,[Validators.required]],
+ command: [null, [Validators.required]],
time: [60, [Validators.required, Validators.min(1)]],
params: ['{}', [jsonRequired]],
result: [null]
});
}
- ngAfterViewInit() {
+ ngOnInit() {
this.isConnector = this.ctx.settings.isConnector;
if (!this.isConnector) {
this.commandForm.get('command').setValue(this.RPCCommands[0]);
@@ -77,17 +78,17 @@ export class GatewayServiceRPCComponent implements AfterViewInit {
}
}
- sendCommand() {
+ sendCommand(value?: RPCCommand) {
this.resultTime = null;
- const formValues = this.commandForm.value;
+ const formValues = value || this.commandForm.value;
const commandPrefix = this.isConnector ? `${this.connectorType}_` : 'gateway_';
- this.ctx.controlApi.sendTwoWayCommand(commandPrefix+formValues.command.toLowerCase(), formValues.params,formValues.time).subscribe({
+ this.ctx.controlApi.sendTwoWayCommand(commandPrefix + formValues.command.toLowerCase(), formValues.params, formValues.time).subscribe({
next: resp => {
- this.resultTime = new Date().getTime();
+ this.resultTime = new Date().getTime();
this.commandForm.get('result').setValue(JSON.stringify(resp))
},
error: error => {
- this.resultTime = new Date().getTime();
+ this.resultTime = new Date().getTime();
console.error(error);
this.commandForm.get('result').setValue(JSON.stringify(error.error));
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts
index d58c33079a..4b09667a6b 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts
@@ -107,27 +107,166 @@ export interface GatewayConnector {
key?: string;
}
+export enum ConnectorType {
+ MQTT = 'mqtt',
+ MODBUS = 'modbus',
+ GRPC = 'grpc',
+ OPCUA = 'opcua',
+ OPCUA_ASYNCIO = 'opcua_asyncio',
+ BLE = 'ble',
+ REQUEST = 'request',
+ CAN = 'can',
+ BACNET = 'bacnet',
+ ODBC = 'odbc',
+ REST = 'rest',
+ SNMP = 'snmp',
+ FTP = 'ftp',
+ SOCKET = 'socket',
+ XMPP = 'xmpp',
+ OCPP = 'ocpp',
+ CUSTOM = 'custom'
+}
-export const GatewayConnectorDefaultTypesTranslates = new Map([
- ['mqtt', 'MQTT'],
- ['modbus', 'MODBUS'],
- ['grpc', 'GRPC'],
- ['opcua', 'OPCUA'],
- ['opcua_asyncio', 'OPCUA ASYNCIO'],
- ['ble', 'BLE'],
- ['request', 'REQUEST'],
- ['can', 'CAN'],
- ['bacnet', 'BACNET'],
- ['odbc', 'ODBC'],
- ['rest', 'REST'],
- ['snmp', 'SNMP'],
- ['ftp', 'FTP'],
- ['socket', 'SOCKET'],
- ['xmpp', 'XMPP'],
- ['ocpp', 'OCPP'],
- ['custom', 'CUSTOM']
+export const GatewayConnectorDefaultTypesTranslates = new Map([
+ [ConnectorType.MQTT, 'MQTT'],
+ [ConnectorType.MODBUS, 'MODBUS'],
+ [ConnectorType.GRPC, 'GRPC'],
+ [ConnectorType.OPCUA, 'OPCUA'],
+ [ConnectorType.OPCUA_ASYNCIO, 'OPCUA ASYNCIO'],
+ [ConnectorType.BLE, 'BLE'],
+ [ConnectorType.REQUEST, 'REQUEST'],
+ [ConnectorType.CAN, 'CAN'],
+ [ConnectorType.BACNET, 'BACNET'],
+ [ConnectorType.ODBC, 'ODBC'],
+ [ConnectorType.REST, 'REST'],
+ [ConnectorType.SNMP, 'SNMP'],
+ [ConnectorType.FTP, 'FTP'],
+ [ConnectorType.SOCKET, 'SOCKET'],
+ [ConnectorType.XMPP, 'XMPP'],
+ [ConnectorType.OCPP, 'OCPP'],
+ [ConnectorType.CUSTOM, 'CUSTOM']
]);
+export interface RPCCommand {
+ command: string,
+ params: any,
+ time: number
+}
+
+
+export enum ModbusCommandTypes {
+ Bits = 'bits',
+ Bit = 'bit',
+ String = 'string',
+ Bytes = 'bytes',
+ Int8 = '8int',
+ Uint8 = '8uint',
+ Int16 = '16int',
+ Uint16 = '16uint',
+ Float16 = '16float',
+ Int32 = '32int',
+ Uint32 = '32uint',
+ Float32 = '32float',
+ Int64 = '64int',
+ Uint64 = '64uint',
+ Float64 = '64float'
+}
+
+export enum BACnetRequestTypes {
+ WriteProperty = 'writeProperty',
+ ReadProperty = 'readProperty'
+}
+
+export const BACnetRequestTypesTranslates = new Map([
+ [BACnetRequestTypes.WriteProperty, 'gateway.rpc.write-property'],
+ [BACnetRequestTypes.ReadProperty, "gateway.rpc.read-property"]
+])
+
+export enum BACnetObjectTypes {
+ BinaryOutput = 'binaryOutput',
+ AnalogOutput = 'analogOutput',
+ BinaryValue = 'binaryValue'
+}
+
+export const BACnetObjectTypesTranslates = new Map([
+ [BACnetObjectTypes.AnalogOutput, 'gateway.rpc.analog-output'],
+ [BACnetObjectTypes.BinaryOutput, 'gateway.rpc.binary-output'],
+ [BACnetObjectTypes.BinaryValue, 'gateway.rpc.binary-value'],
+])
+
+export enum BLEMethods {
+ WRITE = 'write',
+ READ = 'read',
+ SCAN = 'scan'
+}
+
+export const BLEMethodsTranslates = new Map([
+ [BLEMethods.WRITE, 'gateway.rpc.write'],
+ [BLEMethods.READ, 'gateway.rpc.read'],
+ [BLEMethods.SCAN, 'gateway.rpc.scan'],
+])
+
+export enum CANByteOrders {
+ LITTLE = 'LITTLE',
+ BIG = 'BIG'
+}
+
+export enum SocketMethodProcessings {
+ WRITE = 'write'
+}
+
+export const SocketMethodProcessingsTranslates = new Map([
+ [SocketMethodProcessings.WRITE, 'gateway.rpc.write']
+])
+
+export enum SNMPMethods {
+ SET = 'set',
+ MULTISET = "multiset",
+ GET = "get",
+ BULKWALK = "bulkwalk",
+ TABLE = "table",
+ MULTIGET = "multiget",
+ GETNEXT = "getnext",
+ BULKGET = "bulkget",
+ WALKS = "walk"
+}
+
+export const SNMPMethodsTranslations = new Map([
+ [SNMPMethods.SET, 'gateway.rpc.set'],
+ [SNMPMethods.MULTISET, 'gateway.rpc.multiset'],
+ [SNMPMethods.GET, 'gateway.rpc.get'],
+ [SNMPMethods.BULKWALK, 'gateway.rpc.bulk-walk'],
+ [SNMPMethods.TABLE, 'gateway.rpc.table'],
+ [SNMPMethods.MULTIGET, 'gateway.rpc.multi-get'],
+ [SNMPMethods.GETNEXT, 'gateway.rpc.get-next'],
+ [SNMPMethods.BULKGET, 'gateway.rpc.bul-kget'],
+ [SNMPMethods.WALKS, 'gateway.rpc.walk']
+])
+
+export enum HTTPMethods {
+ CONNECT = 'CONNECT',
+ DELETE = 'DELETE',
+ GET = 'GET',
+ HEAD = 'HEAD',
+ OPTIONS = 'OPTIONS',
+ PATCH = 'PATCH',
+ POST = 'POST',
+ PUT = 'PUT',
+ TRACE = 'TRACE'
+
+}
+
+export enum SocketEncodings {
+ UTF_8 = 'utf-8'
+}
+
+export interface RPCTemplate {
+ name: string;
+ config: {
+ [key: string]: any;
+ };
+}
+
export interface LogLink {
name: string;
key: string;
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
index 858856fb4a..b4b688848e 100644
--- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
@@ -22,7 +22,9 @@ import { DisplayColumnsPanelComponent } from '@home/components/widget/lib/displa
import { AlarmsTableWidgetComponent } from '@home/components/widget/lib/alarm/alarms-table-widget.component';
import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module';
import { TimeseriesTableWidgetComponent } from '@home/components/widget/lib/timeseries-table-widget.component';
-import { EntitiesHierarchyWidgetComponent } from '@home/components/widget/lib/entity/entities-hierarchy-widget.component';
+import {
+ EntitiesHierarchyWidgetComponent
+} from '@home/components/widget/lib/entity/entities-hierarchy-widget.component';
import { RpcWidgetsModule } from '@home/components/widget/lib/rpc/rpc-widgets.module';
import {
DateRangeNavigatorPanelComponent,
@@ -47,6 +49,12 @@ import { GatewayConnectorComponent } from '@home/components/widget/lib/gateway/g
import { GatewayLogsComponent } from '@home/components/widget/lib/gateway/gateway-logs.component';
import { GatewayStatisticsComponent } from '@home/components/widget/lib/gateway/gateway-statistics.component';
import { GatewayServiceRPCComponent } from '@home/components/widget/lib/gateway/gateway-service-rpc.component';
+import {
+ GatewayServiceRPCConnectorComponent
+} from '@home/components/widget/lib/gateway/gateway-service-rpc-connector.component';
+import {
+ GatewayServiceRPCConnectorTemplatesComponent
+} from '@home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component';
import { DeviceGatewayCommandComponent } from '@home/components/widget/lib/gateway/device-gateway-command.component';
import { GatewayConfigurationComponent } from '@home/components/widget/lib/gateway/gateway-configuration.component';
import {
@@ -94,6 +102,8 @@ import { DoughnutWidgetComponent } from '@home/components/widget/lib/chart/dough
GatewayLogsComponent,
GatewayStatisticsComponent,
GatewayServiceRPCComponent,
+ GatewayServiceRPCConnectorComponent,
+ GatewayServiceRPCConnectorTemplatesComponent,
DeviceGatewayCommandComponent,
GatewayConfigurationComponent,
GatewayRemoteConfigurationDialogComponent,
@@ -115,46 +125,48 @@ import { DoughnutWidgetComponent } from '@home/components/widget/lib/chart/dough
HomePageWidgetsModule,
SharedHomeComponentsModule
],
- exports: [
- EntitiesTableWidgetComponent,
- AlarmsTableWidgetComponent,
- TimeseriesTableWidgetComponent,
- EntitiesHierarchyWidgetComponent,
- EdgesOverviewWidgetComponent,
- RpcWidgetsModule,
- HomePageWidgetsModule,
- DateRangeNavigatorWidgetComponent,
- JsonInputWidgetComponent,
- MultipleInputWidgetComponent,
- TripAnimationComponent,
- PhotoCameraInputWidgetComponent,
- GatewayFormComponent,
- NavigationCardsWidgetComponent,
- NavigationCardWidgetComponent,
- QrCodeWidgetComponent,
- MarkdownWidgetComponent,
- LegendComponent,
- FlotWidgetComponent,
- GatewayConnectorComponent,
- GatewayLogsComponent,
- GatewayStatisticsComponent,
- GatewayServiceRPCComponent,
- DeviceGatewayCommandComponent,
- GatewayConfigurationComponent,
- GatewayRemoteConfigurationDialogComponent,
- ValueCardWidgetComponent,
- AggregatedValueCardWidgetComponent,
- CountWidgetComponent,
- BatteryLevelWidgetComponent,
- WindSpeedDirectionWidgetComponent,
- SignalStrengthWidgetComponent,
- ValueChartCardWidgetComponent,
- ProgressBarWidgetComponent,
- LiquidLevelWidgetComponent,
- DoughnutWidgetComponent
- ],
+ exports: [
+ EntitiesTableWidgetComponent,
+ AlarmsTableWidgetComponent,
+ TimeseriesTableWidgetComponent,
+ EntitiesHierarchyWidgetComponent,
+ EdgesOverviewWidgetComponent,
+ RpcWidgetsModule,
+ HomePageWidgetsModule,
+ DateRangeNavigatorWidgetComponent,
+ JsonInputWidgetComponent,
+ MultipleInputWidgetComponent,
+ TripAnimationComponent,
+ PhotoCameraInputWidgetComponent,
+ GatewayFormComponent,
+ NavigationCardsWidgetComponent,
+ NavigationCardWidgetComponent,
+ QrCodeWidgetComponent,
+ MarkdownWidgetComponent,
+ LegendComponent,
+ FlotWidgetComponent,
+ GatewayConnectorComponent,
+ GatewayLogsComponent,
+ GatewayServiceRPCConnectorComponent,
+ GatewayServiceRPCConnectorTemplatesComponent,
+ GatewayStatisticsComponent,
+ GatewayServiceRPCComponent,
+ DeviceGatewayCommandComponent,
+ GatewayConfigurationComponent,
+ GatewayRemoteConfigurationDialogComponent,
+ ValueCardWidgetComponent,
+ AggregatedValueCardWidgetComponent,
+ CountWidgetComponent,
+ BatteryLevelWidgetComponent,
+ WindSpeedDirectionWidgetComponent,
+ SignalStrengthWidgetComponent,
+ ValueChartCardWidgetComponent,
+ ProgressBarWidgetComponent,
+ LiquidLevelWidgetComponent,
+ DoughnutWidgetComponent
+ ],
providers: [
- {provide: WIDGET_COMPONENTS_MODULE_TOKEN, useValue: WidgetComponentsModule }
+ {provide: WIDGET_COMPONENTS_MODULE_TOKEN, useValue: WidgetComponentsModule}
]
})
export class WidgetComponentsModule {
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json
index fcf42dc0e2..294173e231 100644
--- a/ui-ngx/src/assets/locale/locale.constant-en_US.json
+++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json
@@ -2681,6 +2681,7 @@
"connectors-table-actions": "Actions",
"connectors-table-key": "Key",
"connectors-table-class": "Class",
+ "rpc-command-save-template": "Save Template",
"rpc-command-send": "Send",
"rpc-command-result": "Response",
"rpc-command-edit-params": "Edit parameters",
@@ -2772,6 +2773,79 @@
"remove-entry": "Remove configuration",
"remote-shell": "Remote shell",
"remote-configuration": "Remote Configuration",
+ "rpc": {
+ "title": "{{type}} Connector RPC parameters",
+ "templates-title": "Connector RPC Templates",
+ "method-filter": "Method filter",
+ "request-topic-expression": "Request topic expression",
+ "response-topic-expression": "Response topic expression",
+ "response-timeout": "Response Time",
+ "value-expression": "Value Expression",
+ "tag": "Tag",
+ "type": "Type",
+ "function-code": "Function Code",
+ "objects-count": "Objects Count",
+ "address": "Address",
+ "method": "Method",
+ "request-type": "Request Type",
+ "request-timeout": "Request Timeout",
+ "object-type": "Object type",
+ "identifier": "Identifier",
+ "property-id": "Property ID",
+ "method-rpc": "Method RPC name",
+ "with-response": "With Response",
+ "characteristic-uuid": "Characteristic UUID",
+ "method-processing": "Method Processing",
+ "node-id": "Node ID",
+ "is-extended-id": "Is Extended ID",
+ "is-fd": "Is FD",
+ "bitrate-switch": "Bitrate Switch",
+ "data-in-hex": "Data In HEX",
+ "data-length": "Data Length",
+ "data-byte-order": "Data Byte Order",
+ "data-before": "Data Before",
+ "data-after": "Data After",
+ "data-expression": "Data Expression",
+ "encoding": "Encoding",
+ "oid": "OID",
+ "add-oid": "Add OID",
+ "add-header": "Add header",
+ "add-security": "Add security",
+ "remove": "Remove",
+ "request-filter": "Request Filter",
+ "request-url": "Request URL Expression",
+ "http-method": "HTTP Method",
+ "timeout": "Timeout",
+ "tries": "Tries",
+ "http-headers": "HTTP Headers",
+ "header-name": "Header name",
+ "security-name": "Security name",
+ "value": "Value",
+ "security": "Security",
+ "response-value-expression": "Response Value Expression",
+ "request-value-expression": "Request Value Expression",
+ "arguments": "Arguments",
+ "add-argument": "Add argument",
+ "write-property": "Write property",
+ "read-property": "Read property",
+ "analog-output": "Analog output",
+ "binary-output": "Binary output",
+ "binary-value": "Binary value",
+ "write": "Write",
+ "read": "Read",
+ "scan": "Scan",
+ "oids": "OIDS",
+ "set": "Set",
+ "multiset": "Multiset",
+ "get": "Get",
+ "bulk-walk": "Bulk walk",
+ "table": "Table",
+ "multi-get": "Multiget",
+ "get-next": "Get next",
+ "bulk-get": "Bulk get",
+ "walk": "Walk"
+
+ },
"other": "Other",
"save-tip": "Save configuration file",
"security-type": "Security type",