Browse Source

Merge pull request #11516 from maxunbearable/task/4114-gateway-version-compatibility

Added config version compatibility/upgrade/downgrade of Gateway MQTT/OPC-UA/Modbus Connectors
pull/11341/head
Igor Kulikov 2 years ago
committed by GitHub
parent
commit
b9b637b28f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 72
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract.ts
  2. 61
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/gateway-connector-version-processor.abstract.ts
  3. 68
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/modbus-version-processor.abstract.ts
  4. 101
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/mqtt-version-processor.abstract.ts
  5. 56
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/opc-version-processor.abstract.ts
  6. 4
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/models/gateway-configuration.models.ts
  7. 6
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.ts
  8. 4
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.ts
  9. 23
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.ts
  10. 81
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.abstract.ts
  11. 110
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.ts
  12. 76
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component.ts
  13. 191
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt-basic-config/mqtt-basic-config.component.ts
  14. 82
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.abstract.ts
  15. 4
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.html
  16. 0
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.scss
  17. 103
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.ts
  18. 124
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-legacy-basic-config.component.ts
  19. 0
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component.html
  20. 2
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component.ts
  21. 0
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component.html
  22. 0
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component.ts
  23. 134
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component.ts
  24. 2
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.html
  25. 0
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.scss
  26. 15
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.ts
  27. 2
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.html
  28. 0
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.scss
  29. 88
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.ts
  30. 88
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-legacy-basic-config.component.ts
  31. 24
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts
  32. 27
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.ts
  33. 43
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html
  34. 5
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.scss
  35. 32
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts
  36. 288
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts
  37. 10
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-help-link.pipe.ts
  38. 42
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/gateway-connector-version-mapping.util.ts
  39. 80
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/modbus-version-mapping.util.ts
  40. 217
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/mqtt-version-mapping.util.ts
  41. 134
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/opc-version-mapping.util.ts
  42. 22
      ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
  43. 698
      ui-ngx/src/assets/metadata/connector-default-configs/modbus.json
  44. 595
      ui-ngx/src/assets/metadata/connector-default-configs/mqtt.json
  45. 180
      ui-ngx/src/assets/metadata/connector-default-configs/opcua.json

72
ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract.ts

@ -0,0 +1,72 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Directive, inject, Input, OnDestroy, TemplateRef } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, ValidationErrors, Validator } from '@angular/forms';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Directive()
export abstract class GatewayConnectorBasicConfigDirective<InputBasicConfig, OutputBasicConfig>
implements ControlValueAccessor, Validator, OnDestroy {
@Input() generalTabContent: TemplateRef<any>;
basicFormGroup: FormGroup;
protected fb = inject(FormBuilder);
protected onChange!: (value: OutputBasicConfig) => void;
protected onTouched!: () => void;
protected destroy$ = new Subject<void>();
constructor() {
this.basicFormGroup = this.initBasicFormGroup();
this.basicFormGroup.valueChanges
.pipe(takeUntil(this.destroy$))
.subscribe((value) => this.onBasicFormGroupChange(value));
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
validate(): ValidationErrors | null {
return this.basicFormGroup.valid ? null : { basicFormGroup: { valid: false } };
}
registerOnChange(fn: (value: OutputBasicConfig) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
writeValue(config: OutputBasicConfig): void {
this.basicFormGroup.setValue(this.mapConfigToFormValue(config), { emitEvent: false });
}
protected onBasicFormGroupChange(value: InputBasicConfig): void {
this.onChange(this.getMappedValue(value));
this.onTouched();
}
protected abstract mapConfigToFormValue(config: OutputBasicConfig): InputBasicConfig;
protected abstract getMappedValue(config: InputBasicConfig): OutputBasicConfig;
protected abstract initBasicFormGroup(): FormGroup;
}

61
ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/gateway-connector-version-processor.abstract.ts

@ -0,0 +1,61 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { GatewayConnector, GatewayVersion } from '@home/components/widget/lib/gateway/gateway-widget.models';
import { isNumber, isString } from '@core/utils';
export abstract class GatewayConnectorVersionProcessor<BasicConfig> {
gatewayVersion: number;
configVersion: number;
protected constructor(protected gatewayVersionIn: string | number, protected connector: GatewayConnector<BasicConfig>) {
this.gatewayVersion = this.parseVersion(this.gatewayVersionIn);
this.configVersion = this.parseVersion(connector.configVersion);
}
getProcessedByVersion(): GatewayConnector<BasicConfig> {
if (this.isVersionUpdateNeeded()) {
return this.isVersionUpgradeNeeded()
? this.getUpgradedVersion()
: this.getDowngradedVersion();
}
return this.connector;
}
private isVersionUpdateNeeded(): boolean {
if (!this.gatewayVersion) {
return false;
}
return this.configVersion !== this.gatewayVersion;
}
private isVersionUpgradeNeeded(): boolean {
return this.gatewayVersionIn === GatewayVersion.Current && (!this.configVersion || this.configVersion < this.gatewayVersion);
}
private parseVersion(version: string | number): number {
if (isNumber(version)) {
return version as number;
}
return isString(version) ? parseFloat((version as string).replace(/\./g, '').slice(0, 3)) / 100 : 0;
}
protected abstract getDowngradedVersion(): GatewayConnector<BasicConfig>;
protected abstract getUpgradedVersion(): GatewayConnector<BasicConfig>;
}

68
ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/modbus-version-processor.abstract.ts

@ -0,0 +1,68 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {
GatewayConnector,
ModbusBasicConfig,
ModbusBasicConfig_v3_5_2,
ModbusLegacyBasicConfig,
ModbusLegacySlave,
ModbusMasterConfig,
ModbusSlave,
} from '../gateway-widget.models';
import { GatewayConnectorVersionProcessor } from './gateway-connector-version-processor.abstract';
import { ModbusVersionMappingUtil } from '@home/components/widget/lib/gateway/utils/modbus-version-mapping.util';
export class ModbusVersionProcessor extends GatewayConnectorVersionProcessor<any> {
constructor(
protected gatewayVersionIn: string,
protected connector: GatewayConnector<ModbusBasicConfig>
) {
super(gatewayVersionIn, connector);
}
getUpgradedVersion(): GatewayConnector<ModbusBasicConfig_v3_5_2> {
const configurationJson = this.connector.configurationJson;
return {
...this.connector,
configurationJson: {
master: configurationJson.master
? ModbusVersionMappingUtil.mapMasterToUpgradedVersion(configurationJson.master)
: {} as ModbusMasterConfig,
slave: configurationJson.slave
? ModbusVersionMappingUtil.mapSlaveToUpgradedVersion(configurationJson.slave as ModbusLegacySlave)
: {} as ModbusSlave,
},
configVersion: this.gatewayVersionIn
} as GatewayConnector<ModbusBasicConfig_v3_5_2>;
}
getDowngradedVersion(): GatewayConnector<ModbusLegacyBasicConfig> {
const configurationJson = this.connector.configurationJson;
return {
...this.connector,
configurationJson: {
...configurationJson,
slave: configurationJson.slave
? ModbusVersionMappingUtil.mapSlaveToDowngradedVersion(configurationJson.slave as ModbusSlave)
: {} as ModbusLegacySlave,
master: configurationJson.master,
},
configVersion: this.gatewayVersionIn
} as GatewayConnector<ModbusLegacyBasicConfig>;
}
}

101
ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/mqtt-version-processor.abstract.ts

@ -0,0 +1,101 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { isEqual } from '@core/utils';
import {
GatewayConnector,
MQTTBasicConfig,
MQTTBasicConfig_v3_5_2,
MQTTLegacyBasicConfig,
RequestMappingData,
RequestType,
} from '../gateway-widget.models';
import { MqttVersionMappingUtil } from '../utils/mqtt-version-mapping.util';
import { GatewayConnectorVersionProcessor } from './gateway-connector-version-processor.abstract';
export class MqttVersionProcessor extends GatewayConnectorVersionProcessor<MQTTBasicConfig> {
private readonly mqttRequestTypeKeys = Object.values(RequestType);
constructor(
protected gatewayVersionIn: string,
protected connector: GatewayConnector<MQTTBasicConfig>
) {
super(gatewayVersionIn, connector);
}
getUpgradedVersion(): GatewayConnector<MQTTBasicConfig_v3_5_2> {
const {
connectRequests,
disconnectRequests,
attributeRequests,
attributeUpdates,
serverSideRpc
} = this.connector.configurationJson as MQTTLegacyBasicConfig;
let configurationJson = {
...this.connector.configurationJson,
requestsMapping: MqttVersionMappingUtil.mapRequestsToUpgradedVersion({
connectRequests,
disconnectRequests,
attributeRequests,
attributeUpdates,
serverSideRpc
}),
mapping: MqttVersionMappingUtil.mapMappingToUpgradedVersion((this.connector.configurationJson as MQTTLegacyBasicConfig).mapping),
};
this.mqttRequestTypeKeys.forEach((key: RequestType) => {
const { [key]: removedValue, ...rest } = configurationJson as MQTTLegacyBasicConfig;
configurationJson = { ...rest } as any;
});
this.cleanUpConfigJson(configurationJson as MQTTBasicConfig_v3_5_2);
return {
...this.connector,
configurationJson,
configVersion: this.gatewayVersionIn
} as GatewayConnector<MQTTBasicConfig_v3_5_2>;
}
getDowngradedVersion(): GatewayConnector<MQTTLegacyBasicConfig> {
const { requestsMapping, mapping, ...restConfig } = this.connector.configurationJson as MQTTBasicConfig_v3_5_2;
const updatedRequestsMapping =
MqttVersionMappingUtil.mapRequestsToDowngradedVersion(requestsMapping as Record<RequestType, RequestMappingData[]>);
const updatedMapping = MqttVersionMappingUtil.mapMappingToDowngradedVersion(mapping);
return {
...this.connector,
configurationJson: {
...restConfig,
...updatedRequestsMapping,
mapping: updatedMapping,
},
configVersion: this.gatewayVersionIn
} as GatewayConnector<MQTTLegacyBasicConfig>;
}
private cleanUpConfigJson(configurationJson: MQTTBasicConfig_v3_5_2): void {
if (isEqual(configurationJson.requestsMapping, {})) {
delete configurationJson.requestsMapping;
}
if (isEqual(configurationJson.mapping, [])) {
delete configurationJson.mapping;
}
}
}

56
ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/opc-version-processor.abstract.ts

@ -0,0 +1,56 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {
GatewayConnector, LegacyServerConfig,
OPCBasicConfig,
OPCBasicConfig_v3_5_2,
OPCLegacyBasicConfig,
} from '../gateway-widget.models';
import { GatewayConnectorVersionProcessor } from './gateway-connector-version-processor.abstract';
import { OpcVersionMappingUtil } from '@home/components/widget/lib/gateway/utils/opc-version-mapping.util';
export class OpcVersionProcessor extends GatewayConnectorVersionProcessor<OPCBasicConfig> {
constructor(
protected gatewayVersionIn: string,
protected connector: GatewayConnector<OPCBasicConfig>
) {
super(gatewayVersionIn, connector);
}
getUpgradedVersion(): GatewayConnector<OPCBasicConfig_v3_5_2> {
const server = this.connector.configurationJson.server as LegacyServerConfig;
return {
...this.connector,
configurationJson: {
server: server ? OpcVersionMappingUtil.mapServerToUpgradedVersion(server) : {},
mapping: server.mapping ? OpcVersionMappingUtil.mapMappingToUpgradedVersion(server.mapping) : [],
},
configVersion: this.gatewayVersionIn
} as GatewayConnector<OPCBasicConfig_v3_5_2>;
}
getDowngradedVersion(): GatewayConnector<OPCLegacyBasicConfig> {
return {
...this.connector,
configurationJson: {
server: OpcVersionMappingUtil.mapServerToDowngradedVersion(this.connector.configurationJson as OPCBasicConfig_v3_5_2)
},
configVersion: this.gatewayVersionIn
} as GatewayConnector<OPCLegacyBasicConfig>;
}
}

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

@ -16,7 +16,9 @@
import {
ConfigurationModes,
GatewayConnector, LocalLogsConfigs, LogSavingPeriod,
GatewayConnector,
LocalLogsConfigs,
LogSavingPeriod,
SecurityTypes,
StorageTypes
} from '@home/components/widget/lib/gateway/gateway-widget.models';

6
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.ts

@ -42,8 +42,8 @@ import {
import {
DeviceInfoType,
noLeadTrailSpacesRegex,
OPCUaSourceTypes,
SourceTypes,
OPCUaSourceType,
SourceType,
SourceTypeTranslationsMap
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { coerceBoolean } from '@shared/decorators/coercion';
@ -81,7 +81,7 @@ export class DeviceInfoTableComponent extends PageComponent implements ControlVa
required = false;
@Input()
sourceTypes: Array<SourceTypes | OPCUaSourceTypes> = Object.values(SourceTypes);
sourceTypes: Array<SourceType | OPCUaSourceType> = Object.values(SourceType);
deviceInfoTypeValue: any;

4
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.ts

@ -42,7 +42,7 @@ import {
MappingValueType,
mappingValueTypesMap,
noLeadTrailSpacesRegex,
OPCUaSourceTypes,
OPCUaSourceType,
RpcMethodsMapping,
} from '@home/components/widget/lib/gateway/gateway-widget.models';
@ -73,7 +73,7 @@ export class MappingDataKeysPanelComponent extends PageComponent implements OnIn
keysType: MappingKeysType;
@Input()
valueTypeKeys: Array<MappingValueType | OPCUaSourceTypes> = Object.values(MappingValueType);
valueTypeKeys: Array<MappingValueType | OPCUaSourceType> = Object.values(MappingValueType);
@Input()
valueTypeEnum = MappingValueType;

23
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.ts

@ -40,17 +40,21 @@ import {
Validator,
} from '@angular/forms';
import {
AttributeUpdate,
ConnectorMapping,
ConnectRequest,
ConverterConnectorMapping,
ConvertorTypeTranslationsMap,
DeviceConnectorMapping,
DisconnectRequest,
MappingInfo,
MappingType,
MappingTypeTranslationsMap,
MappingValue,
RequestMappingData,
RequestMappingValue,
RequestType,
RequestTypesTranslationsMap
RequestTypesTranslationsMap,
ServerSideRpc
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { MappingDialogComponent } from '@home/components/widget/lib/gateway/dialog/mapping-dialog.component';
import { isDefinedAndNotNull, isUndefinedOrNull } from '@core/utils';
@ -259,16 +263,17 @@ export class MappingTableComponent implements ControlValueAccessor, Validator, A
};
case MappingType.REQUESTS:
let details: string;
if ((value as RequestMappingData).requestType === RequestType.ATTRIBUTE_UPDATE) {
details = (value as RequestMappingData).requestValue.attributeFilter;
} else if ((value as RequestMappingData).requestType === RequestType.SERVER_SIDE_RPC) {
details = (value as RequestMappingData).requestValue.methodFilter;
const requestValue = value as RequestMappingValue;
if (requestValue.requestType === RequestType.ATTRIBUTE_UPDATE) {
details = (requestValue.requestValue as AttributeUpdate).attributeFilter;
} else if (requestValue.requestType === RequestType.SERVER_SIDE_RPC) {
details = (requestValue.requestValue as ServerSideRpc).methodFilter;
} else {
details = (value as RequestMappingData).requestValue.topicFilter;
details = (requestValue.requestValue as ConnectRequest | DisconnectRequest).topicFilter;
}
return {
requestType: (value as RequestMappingData).requestType,
type: this.translate.instant(RequestTypesTranslationsMap.get((value as RequestMappingData).requestType)),
requestType: (value as RequestMappingValue).requestType,
type: this.translate.instant(RequestTypesTranslationsMap.get((value as RequestMappingValue).requestType)),
details
};
case MappingType.OPCUA:

81
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.abstract.ts

@ -0,0 +1,81 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Directive } from '@angular/core';
import { FormControl, FormGroup, ValidationErrors } from '@angular/forms';
import { takeUntil } from 'rxjs/operators';
import { isEqual } from '@core/utils';
import { GatewayConnectorBasicConfigDirective } from '@home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract';
import {
ModbusBasicConfig,
ModbusBasicConfig_v3_5_2,
} from '@home/components/widget/lib/gateway/gateway-widget.models';
@Directive()
export abstract class ModbusBasicConfigDirective<BasicConfig>
extends GatewayConnectorBasicConfigDirective<ModbusBasicConfig_v3_5_2, BasicConfig> {
enableSlaveControl: FormControl<boolean> = new FormControl(false);
constructor() {
super();
this.enableSlaveControl.valueChanges
.pipe(takeUntil(this.destroy$))
.subscribe(enable => {
this.updateSlaveEnabling(enable);
this.basicFormGroup.get('slave').updateValueAndValidity({ emitEvent: !!this.onChange });
});
}
override writeValue(basicConfig: BasicConfig & ModbusBasicConfig): void {
super.writeValue(basicConfig);
this.onEnableSlaveControl(basicConfig);
}
override validate(): ValidationErrors | null {
const { master, slave } = this.basicFormGroup.value;
const isEmpty = !master?.slaves?.length && (isEqual(slave, {}) || !slave);
if (!this.basicFormGroup.valid || isEmpty) {
return { basicFormGroup: { valid: false } };
}
return null;
}
protected override initBasicFormGroup(): FormGroup {
return this.fb.group({
master: [],
slave: [],
});
}
protected override onBasicFormGroupChange(value: ModbusBasicConfig_v3_5_2): void {
super.onBasicFormGroupChange(value);
this.basicFormGroup.get('slave').updateValueAndValidity({ emitEvent: !!this.onChange });
}
private updateSlaveEnabling(isEnabled: boolean): void {
if (isEnabled) {
this.basicFormGroup.get('slave').enable({ emitEvent: false });
} else {
this.basicFormGroup.get('slave').disable({ emitEvent: false });
}
}
private onEnableSlaveControl(basicConfig: ModbusBasicConfig): void {
this.enableSlaveControl.setValue(!!basicConfig.slave && !isEqual(basicConfig.slave, {}));
}
}

110
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.ts

@ -14,28 +14,21 @@
/// limitations under the License.
///
import { ChangeDetectionStrategy, Component, forwardRef, Input, OnDestroy, TemplateRef } from '@angular/core';
import { ChangeDetectionStrategy, Component, forwardRef } from '@angular/core';
import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';
import {
ControlValueAccessor,
FormBuilder,
FormControl,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
UntypedFormControl,
ValidationErrors,
Validator,
} from '@angular/forms';
import { ModbusBasicConfig } from '@home/components/widget/lib/gateway/gateway-widget.models';
import { SharedModule } from '@shared/shared.module';
ModbusBasicConfig_v3_5_2,
ModbusMasterConfig,
ModbusSlave
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { CommonModule } from '@angular/common';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list.directive';
import { SharedModule } from '@shared/shared.module';
import { ModbusSlaveConfigComponent } from '../modbus-slave-config/modbus-slave-config.component';
import { ModbusMasterTableComponent } from '../modbus-master-table/modbus-master-table.component';
import { isEqual } from '@core/utils';
import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list.directive';
import {
ModbusBasicConfigDirective
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.abstract';
@Component({
selector: 'tb-modbus-basic-config',
@ -63,80 +56,19 @@ import { isEqual } from '@core/utils';
],
styleUrls: ['./modbus-basic-config.component.scss'],
})
export class ModbusBasicConfigComponent extends ModbusBasicConfigDirective<ModbusBasicConfig_v3_5_2> {
export class ModbusBasicConfigComponent implements ControlValueAccessor, Validator, OnDestroy {
@Input() generalTabContent: TemplateRef<any>;
basicFormGroup: FormGroup;
enableSlaveControl: FormControl<boolean>;
onChange: (value: ModbusBasicConfig) => void;
onTouched: () => void;
private destroy$ = new Subject<void>();
constructor(private fb: FormBuilder) {
this.basicFormGroup = this.fb.group({
master: [],
slave: [],
});
this.enableSlaveControl = new FormControl(false);
this.basicFormGroup.valueChanges
.pipe(takeUntil(this.destroy$))
.subscribe(({ master, slave }) => {
this.onChange({ master, slave: slave ?? {} });
this.onTouched();
});
this.enableSlaveControl.valueChanges
.pipe(takeUntil(this.destroy$))
.subscribe(enable => {
this.updateSlaveEnabling(enable);
this.basicFormGroup.get('slave').updateValueAndValidity({emitEvent: !!this.onChange});
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
registerOnChange(fn: (value: ModbusBasicConfig) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
writeValue(basicConfig: ModbusBasicConfig): void {
const editedBase = {
slave: basicConfig.slave ?? {},
master: basicConfig.master ?? {},
protected override mapConfigToFormValue(config: ModbusBasicConfig_v3_5_2): ModbusBasicConfig_v3_5_2 {
return {
master: config.master ?? {} as ModbusMasterConfig,
slave: config.slave ?? {} as ModbusSlave,
};
this.basicFormGroup.setValue(editedBase, {emitEvent: false});
this.enableSlaveControl.setValue(!!basicConfig.slave && !isEqual(basicConfig.slave, {}));
}
validate(basicFormControl: UntypedFormControl): ValidationErrors | null {
const { master, slave } = basicFormControl.value;
const isEmpty = !master?.slaves?.length && (isEqual(slave, {}) || !slave);
if (!this.basicFormGroup.valid || isEmpty) {
return {
basicFormGroup: {valid: false}
};
}
return null;
}
private updateSlaveEnabling(isEnabled: boolean): void {
if (isEnabled) {
this.basicFormGroup.get('slave').enable({emitEvent: false});
} else {
this.basicFormGroup.get('slave').disable({emitEvent: false});
}
protected override getMappedValue(value: ModbusBasicConfig_v3_5_2): ModbusBasicConfig_v3_5_2 {
return {
master: value.master,
slave: value.slave,
};
}
}

76
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component.ts

@ -0,0 +1,76 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { ChangeDetectionStrategy, Component, forwardRef } from '@angular/core';
import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';
import {
ModbusBasicConfig_v3_5_2,
ModbusLegacyBasicConfig, ModbusLegacySlave,
ModbusMasterConfig,
ModbusSlave
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module';
import { ModbusSlaveConfigComponent } from '../modbus-slave-config/modbus-slave-config.component';
import { ModbusMasterTableComponent } from '../modbus-master-table/modbus-master-table.component';
import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list.directive';
import { ModbusVersionMappingUtil } from '@home/components/widget/lib/gateway/utils/modbus-version-mapping.util';
import {
ModbusBasicConfigDirective
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.abstract';
@Component({
selector: 'tb-modbus-legacy-basic-config',
templateUrl: './modbus-basic-config.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ModbusLegacyBasicConfigComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => ModbusLegacyBasicConfigComponent),
multi: true
}
],
standalone: true,
imports: [
CommonModule,
SharedModule,
ModbusSlaveConfigComponent,
ModbusMasterTableComponent,
EllipsisChipListDirective,
],
styleUrls: ['./modbus-basic-config.component.scss'],
})
export class ModbusLegacyBasicConfigComponent extends ModbusBasicConfigDirective<ModbusLegacyBasicConfig> {
protected override mapConfigToFormValue(config: ModbusLegacyBasicConfig): ModbusBasicConfig_v3_5_2 {
return {
master: config.master ? ModbusVersionMappingUtil.mapMasterToUpgradedVersion(config.master) : {} as ModbusMasterConfig,
slave: config.slave ? ModbusVersionMappingUtil.mapSlaveToUpgradedVersion(config.slave) : {} as ModbusSlave,
};
}
protected override getMappedValue(value: ModbusBasicConfig_v3_5_2): ModbusLegacyBasicConfig {
return {
master: value.master,
slave: value.slave ? ModbusVersionMappingUtil.mapSlaveToDowngradedVersion(value.slave) : {} as ModbusLegacySlave,
};
}
}

191
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt-basic-config/mqtt-basic-config.component.ts

@ -1,191 +0,0 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { ChangeDetectionStrategy, Component, forwardRef, Input, OnDestroy, TemplateRef } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
} from '@angular/forms';
import {
MappingType,
MQTTBasicConfig,
RequestMappingData,
RequestType,
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { SharedModule } from '@shared/shared.module';
import { CommonModule } from '@angular/common';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { isObject } from 'lodash';
import {
SecurityConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component';
import {
WorkersConfigControlComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component';
import {
BrokerConfigControlComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/broker-config-control/broker-config-control.component';
import {
MappingTableComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component';
import { isDefinedAndNotNull } from '@core/utils';
@Component({
selector: 'tb-mqtt-basic-config',
templateUrl: './mqtt-basic-config.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MqttBasicConfigComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => MqttBasicConfigComponent),
multi: true
}
],
standalone: true,
imports: [
CommonModule,
SharedModule,
SecurityConfigComponent,
WorkersConfigControlComponent,
BrokerConfigControlComponent,
MappingTableComponent,
],
styleUrls: ['./mqtt-basic-config.component.scss']
})
export class MqttBasicConfigComponent implements ControlValueAccessor, Validator, OnDestroy {
@Input()
generalTabContent: TemplateRef<any>;
mappingTypes = MappingType;
basicFormGroup: FormGroup;
private onChange: (value: MQTTBasicConfig) => void;
private onTouched: () => void;
private destroy$ = new Subject<void>();
constructor(private fb: FormBuilder) {
this.basicFormGroup = this.fb.group({
dataMapping: [],
requestsMapping: [],
broker: [],
workers: [],
});
this.basicFormGroup.valueChanges
.pipe(takeUntil(this.destroy$))
.subscribe(value => {
this.onChange(this.getMappedMQTTConfig(value));
this.onTouched();
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
registerOnChange(fn: (value: MQTTBasicConfig) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
writeValue(basicConfig: MQTTBasicConfig): void {
const { broker, dataMapping = [], requestsMapping } = basicConfig;
const editedBase = {
workers: broker && (broker.maxNumberOfWorkers || broker.maxMessageNumberPerWorker) ? {
maxNumberOfWorkers: broker.maxNumberOfWorkers,
maxMessageNumberPerWorker: broker.maxMessageNumberPerWorker,
} : {},
dataMapping: dataMapping || [],
broker: broker || {},
requestsMapping: Array.isArray(requestsMapping)
? requestsMapping
: this.getRequestDataArray(requestsMapping),
};
this.basicFormGroup.setValue(editedBase, {emitEvent: false});
}
private getMappedMQTTConfig(basicConfig: MQTTBasicConfig): MQTTBasicConfig {
let { broker, workers, dataMapping, requestsMapping } = basicConfig || {};
if (isDefinedAndNotNull(workers.maxNumberOfWorkers) || isDefinedAndNotNull(workers.maxMessageNumberPerWorker)) {
broker = {
...broker,
...workers,
};
}
if ((requestsMapping as RequestMappingData[])?.length) {
requestsMapping = this.getRequestDataObject(requestsMapping as RequestMappingData[]);
}
return { broker, workers, dataMapping, requestsMapping };
}
validate(): ValidationErrors | null {
return this.basicFormGroup.valid ? null : {
basicFormGroup: {valid: false}
};
}
private getRequestDataArray(value: Record<RequestType, RequestMappingData[]>): RequestMappingData[] {
const mappingConfigs = [];
if (isObject(value)) {
Object.keys(value).forEach((configKey: string) => {
for (const mapping of value[configKey]) {
mappingConfigs.push({
requestType: configKey,
requestValue: mapping
});
}
});
}
return mappingConfigs;
}
private getRequestDataObject(array: RequestMappingData[]): Record<RequestType, RequestMappingData[]> {
return array.reduce((result, { requestType, requestValue }) => {
result[requestType].push(requestValue);
return result;
}, {
connectRequests: [],
disconnectRequests: [],
attributeRequests: [],
attributeUpdates: [],
serverSideRpc: [],
});
}
}

82
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.abstract.ts

@ -0,0 +1,82 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Directive } from '@angular/core';
import { FormGroup } from '@angular/forms';
import {
MappingType,
MQTTBasicConfig, MQTTBasicConfig_v3_5_2,
RequestMappingData,
RequestMappingValue,
RequestType
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { isObject } from '@core/utils';
import {
GatewayConnectorBasicConfigDirective
} from '@home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract';
@Directive()
export abstract class MqttBasicConfigDirective<BasicConfig>
extends GatewayConnectorBasicConfigDirective<MQTTBasicConfig_v3_5_2, BasicConfig> {
MappingType = MappingType;
protected override initBasicFormGroup(): FormGroup {
return this.fb.group({
mapping: [],
requestsMapping: [],
broker: [],
workers: [],
});
}
protected getRequestDataArray(value: Record<RequestType, RequestMappingData[]>): RequestMappingData[] {
const mappingConfigs = [];
if (isObject(value)) {
Object.keys(value).forEach((configKey: string) => {
for (const mapping of value[configKey]) {
mappingConfigs.push({
requestType: configKey,
requestValue: mapping
});
}
});
}
return mappingConfigs;
}
protected getRequestDataObject(array: RequestMappingValue[]): Record<RequestType, RequestMappingValue[]> {
return array.reduce((result, { requestType, requestValue }) => {
result[requestType].push(requestValue);
return result;
}, {
connectRequests: [],
disconnectRequests: [],
attributeRequests: [],
attributeUpdates: [],
serverSideRpc: [],
});
}
writeValue(basicConfig: BasicConfig): void {
this.basicFormGroup.setValue(this.mapConfigToFormValue(basicConfig), { emitEvent: false });
}
protected abstract override mapConfigToFormValue(config: BasicConfig): MQTTBasicConfig_v3_5_2;
protected abstract override getMappedValue(config: MQTTBasicConfig): BasicConfig;
}

4
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt-basic-config/mqtt-basic-config.component.html → ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.html

@ -24,12 +24,12 @@
</mat-tab>
<mat-tab label="{{ 'gateway.data-mapping' | translate }}*">
<div class="tb-form-panel no-border no-padding padding-top tb-flex fill-height">
<tb-mapping-table formControlName="dataMapping" [required]="true" [mappingType]="mappingTypes.DATA"></tb-mapping-table>
<tb-mapping-table formControlName="mapping" [required]="true" [mappingType]="MappingType.DATA"></tb-mapping-table>
</div>
</mat-tab>
<mat-tab label="{{ 'gateway.requests-mapping' | translate }}">
<div class="tb-form-panel no-border no-padding padding-top tb-flex fill-height">
<tb-mapping-table formControlName="requestsMapping" [mappingType]="mappingTypes.REQUESTS"></tb-mapping-table>
<tb-mapping-table formControlName="requestsMapping" [mappingType]="MappingType.REQUESTS"></tb-mapping-table>
</div>
</mat-tab>
<mat-tab label="{{ 'gateway.workers-settings' | translate }}">

0
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt-basic-config/mqtt-basic-config.component.scss → ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.scss

103
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.ts

@ -0,0 +1,103 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, forwardRef, ChangeDetectionStrategy } from '@angular/core';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
import {
BrokerConfig,
MQTTBasicConfig_v3_5_2,
RequestMappingData,
RequestMappingValue,
RequestType, WorkersConfig
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import {
MqttBasicConfigDirective
} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.abstract';
import { isDefinedAndNotNull } from '@core/utils';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module';
import {
SecurityConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component';
import {
WorkersConfigControlComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component';
import {
BrokerConfigControlComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component';
import {
MappingTableComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component';
@Component({
selector: 'tb-mqtt-basic-config',
templateUrl: './mqtt-basic-config.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MqttBasicConfigComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => MqttBasicConfigComponent),
multi: true
}
],
styleUrls: ['./mqtt-basic-config.component.scss'],
standalone: true,
imports: [
CommonModule,
SharedModule,
SecurityConfigComponent,
WorkersConfigControlComponent,
BrokerConfigControlComponent,
MappingTableComponent,
],
})
export class MqttBasicConfigComponent extends MqttBasicConfigDirective<MQTTBasicConfig_v3_5_2> {
protected override mapConfigToFormValue(basicConfig: MQTTBasicConfig_v3_5_2): MQTTBasicConfig_v3_5_2 {
const { broker, mapping = [], requestsMapping } = basicConfig;
return{
workers: broker && (broker.maxNumberOfWorkers || broker.maxMessageNumberPerWorker) ? {
maxNumberOfWorkers: broker.maxNumberOfWorkers,
maxMessageNumberPerWorker: broker.maxMessageNumberPerWorker,
} : {} as WorkersConfig,
mapping: mapping ?? [],
broker: broker ?? {} as BrokerConfig,
requestsMapping: this.getRequestDataArray(requestsMapping as Record<RequestType, RequestMappingData[]>),
};
}
protected override getMappedValue(basicConfig: MQTTBasicConfig_v3_5_2): MQTTBasicConfig_v3_5_2 {
let { broker, workers, mapping, requestsMapping } = basicConfig || {};
if (isDefinedAndNotNull(workers.maxNumberOfWorkers) || isDefinedAndNotNull(workers.maxMessageNumberPerWorker)) {
broker = {
...broker,
...workers,
};
}
if ((requestsMapping as RequestMappingData[])?.length) {
requestsMapping = this.getRequestDataObject(requestsMapping as RequestMappingValue[]);
}
return { broker, mapping, requestsMapping };
}
}

124
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-legacy-basic-config.component.ts

@ -0,0 +1,124 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, forwardRef, ChangeDetectionStrategy } from '@angular/core';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
import {
BrokerConfig,
MQTTBasicConfig_v3_5_2,
MQTTLegacyBasicConfig,
RequestMappingData,
RequestMappingValue,
RequestType, WorkersConfig
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { MqttVersionMappingUtil } from '@home/components/widget/lib/gateway/utils/mqtt-version-mapping.util';
import {
MqttBasicConfigDirective
} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.abstract';
import { isDefinedAndNotNull } from '@core/utils';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module';
import {
SecurityConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component';
import {
WorkersConfigControlComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component';
import {
BrokerConfigControlComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component';
import {
MappingTableComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component';
@Component({
selector: 'tb-mqtt-legacy-basic-config',
templateUrl: './mqtt-basic-config.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MqttLegacyBasicConfigComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => MqttLegacyBasicConfigComponent),
multi: true
}
],
styleUrls: ['./mqtt-basic-config.component.scss'],
standalone: true,
imports: [
CommonModule,
SharedModule,
SecurityConfigComponent,
WorkersConfigControlComponent,
BrokerConfigControlComponent,
MappingTableComponent,
],
})
export class MqttLegacyBasicConfigComponent extends MqttBasicConfigDirective<MQTTLegacyBasicConfig> {
protected override mapConfigToFormValue(config: MQTTLegacyBasicConfig): MQTTBasicConfig_v3_5_2 {
const {
broker,
mapping = [],
connectRequests = [],
disconnectRequests = [],
attributeRequests = [],
attributeUpdates = [],
serverSideRpc = []
} = config as MQTTLegacyBasicConfig;
const updatedRequestMapping = MqttVersionMappingUtil.mapRequestsToUpgradedVersion({
connectRequests,
disconnectRequests,
attributeRequests,
attributeUpdates,
serverSideRpc
});
return {
workers: broker && (broker.maxNumberOfWorkers || broker.maxMessageNumberPerWorker) ? {
maxNumberOfWorkers: broker.maxNumberOfWorkers,
maxMessageNumberPerWorker: broker.maxMessageNumberPerWorker,
} : {} as WorkersConfig,
mapping: MqttVersionMappingUtil.mapMappingToUpgradedVersion(mapping) || [],
broker: broker || {} as BrokerConfig,
requestsMapping: this.getRequestDataArray(updatedRequestMapping),
};
}
protected override getMappedValue(basicConfig: MQTTBasicConfig_v3_5_2): MQTTLegacyBasicConfig {
let { broker, workers, mapping, requestsMapping } = basicConfig || {};
if (isDefinedAndNotNull(workers.maxNumberOfWorkers) || isDefinedAndNotNull(workers.maxMessageNumberPerWorker)) {
broker = {
...broker,
...workers,
};
}
if ((requestsMapping as RequestMappingData[])?.length) {
requestsMapping = this.getRequestDataObject(requestsMapping as RequestMappingValue[]);
}
return {
broker,
mapping: MqttVersionMappingUtil.mapMappingToDowngradedVersion(mapping),
...(MqttVersionMappingUtil.mapRequestsToDowngradedVersion(requestsMapping as Record<RequestType, RequestMappingData[]>))
};
}
}

0
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/broker-config-control/broker-config-control.component.html → ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component.html

2
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/broker-config-control/broker-config-control.component.ts → ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component.ts

@ -36,7 +36,7 @@ import { CommonModule } from '@angular/common';
import { generateSecret } from '@core/utils';
import { Subject } from 'rxjs';
import { GatewayPortTooltipPipe } from '@home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe';
import { SecurityConfigComponent } from '../security-config/security-config.component';
import { SecurityConfigComponent } from '../../security-config/security-config.component';
@Component({
selector: 'tb-broker-config-control',

0
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component.html → ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component.html

0
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component.ts → ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component.ts

134
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component.ts

@ -1,134 +0,0 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { ChangeDetectionStrategy, Component, forwardRef, Input, OnDestroy, TemplateRef } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
} from '@angular/forms';
import {
ConnectorType,
MappingType,
OPCBasicConfig,
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { SharedModule } from '@shared/shared.module';
import { CommonModule } from '@angular/common';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import {
SecurityConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component';
import {
WorkersConfigControlComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component';
import {
BrokerConfigControlComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/broker-config-control/broker-config-control.component';
import {
MappingTableComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component';
import {
OpcServerConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component';
@Component({
selector: 'tb-opc-ua-basic-config',
templateUrl: './opc-ua-basic-config.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => OpcUaBasicConfigComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => OpcUaBasicConfigComponent),
multi: true
}
],
standalone: true,
imports: [
CommonModule,
SharedModule,
SecurityConfigComponent,
WorkersConfigControlComponent,
BrokerConfigControlComponent,
MappingTableComponent,
OpcServerConfigComponent,
],
styleUrls: ['./opc-ua-basic-config.component.scss']
})
export class OpcUaBasicConfigComponent implements ControlValueAccessor, Validator, OnDestroy {
@Input() generalTabContent: TemplateRef<any>;
mappingTypes = MappingType;
basicFormGroup: FormGroup;
onChange!: (value: string) => void;
onTouched!: () => void;
protected readonly connectorType = ConnectorType;
private destroy$ = new Subject<void>();
constructor(private fb: FormBuilder) {
this.basicFormGroup = this.fb.group({
mapping: [],
server: [],
});
this.basicFormGroup.valueChanges
.pipe(takeUntil(this.destroy$))
.subscribe(value => {
this.onChange(value);
this.onTouched();
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
registerOnChange(fn: (value: string) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
writeValue(basicConfig: OPCBasicConfig): void {
const editedBase = {
server: basicConfig.server || {},
mapping: basicConfig.mapping || [],
};
this.basicFormGroup.setValue(editedBase, {emitEvent: false});
}
validate(): ValidationErrors | null {
return this.basicFormGroup.valid ? null : {
basicFormGroup: {valid: false}
};
}
}

2
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component.html → ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.html

@ -84,7 +84,7 @@
</mat-form-field>
</div>
</div>
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
<div *ngIf="!hideNewFields" class="tb-form-row column-xs" fxLayoutAlign="space-between center">
<div class="fixed-title-width" tb-hint-tooltip-icon="{{ 'gateway.hints.poll-period' | translate }}">
<div tbTruncateWithTooltip>{{ 'gateway.poll-period' | translate }}</div>
</div>

0
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component.scss → ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.scss

15
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component.ts → ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { ChangeDetectionStrategy, Component, forwardRef, OnDestroy } from '@angular/core';
import { AfterViewInit, ChangeDetectionStrategy, Component, forwardRef, Input, OnDestroy } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
@ -39,6 +39,7 @@ import {
SecurityConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component';
import { HOUR } from '@shared/models/time/time.models';
import { coerceBoolean } from '@shared/decorators/coercion';
@Component({
selector: 'tb-opc-server-config',
@ -64,7 +65,11 @@ import { HOUR } from '@shared/models/time/time.models';
SecurityConfigComponent,
]
})
export class OpcServerConfigComponent implements ControlValueAccessor, Validator, OnDestroy {
export class OpcServerConfigComponent implements ControlValueAccessor, Validator, AfterViewInit, OnDestroy {
@Input()
@coerceBoolean()
hideNewFields: boolean = false;
securityPolicyTypes = SecurityPolicyTypes;
serverConfigFormGroup: UntypedFormGroup;
@ -95,6 +100,12 @@ export class OpcServerConfigComponent implements ControlValueAccessor, Validator
});
}
ngAfterViewInit(): void {
if (this.hideNewFields) {
this.serverConfigFormGroup.get('pollPeriodInMillis').disable({emitEvent: false});
}
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();

2
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component.html → ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.html

@ -20,7 +20,7 @@
<ng-container [ngTemplateOutlet]="generalTabContent"></ng-container>
</mat-tab>
<mat-tab label="{{ 'gateway.server' | translate }}*">
<tb-opc-server-config formControlName="server"></tb-opc-server-config>
<tb-opc-server-config formControlName="server" [hideNewFields]="isLegacy"></tb-opc-server-config>
</mat-tab>
<mat-tab label="{{ 'gateway.data-mapping' | translate }}*">
<div class="tb-form-panel no-border no-padding padding-top tb-flex fill-height">

0
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component.scss → ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.scss

88
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.ts

@ -0,0 +1,88 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { ChangeDetectionStrategy, Component, forwardRef } from '@angular/core';
import { FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';
import {
MappingType,
OPCBasicConfig_v3_5_2,
ServerConfig
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module';
import { MappingTableComponent } from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component';
import {
SecurityConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component';
import {
OpcServerConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component';
import {
GatewayConnectorBasicConfigDirective
} from '@home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract';
@Component({
selector: 'tb-opc-ua-basic-config',
templateUrl: './opc-ua-basic-config.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => OpcUaBasicConfigComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => OpcUaBasicConfigComponent),
multi: true
}
],
standalone: true,
imports: [
CommonModule,
SharedModule,
SecurityConfigComponent,
MappingTableComponent,
OpcServerConfigComponent,
],
styleUrls: ['./opc-ua-basic-config.component.scss']
})
export class OpcUaBasicConfigComponent extends GatewayConnectorBasicConfigDirective<OPCBasicConfig_v3_5_2, OPCBasicConfig_v3_5_2> {
mappingTypes = MappingType;
isLegacy = false;
protected override initBasicFormGroup(): FormGroup {
return this.fb.group({
mapping: [],
server: [],
});
}
protected override mapConfigToFormValue(config: OPCBasicConfig_v3_5_2): OPCBasicConfig_v3_5_2 {
return {
server: config.server ?? {} as ServerConfig,
mapping: config.mapping ?? [],
};
}
protected override getMappedValue(value: OPCBasicConfig_v3_5_2): OPCBasicConfig_v3_5_2 {
return {
server: value.server,
mapping: value.mapping,
};
}
}

88
ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-legacy-basic-config.component.ts

@ -0,0 +1,88 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { ChangeDetectionStrategy, Component, forwardRef } from '@angular/core';
import { FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';
import {
MappingType,
OPCBasicConfig_v3_5_2,
OPCLegacyBasicConfig, ServerConfig,
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module';
import { MappingTableComponent } from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component';
import {
SecurityConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component';
import {
OpcServerConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component';
import {
GatewayConnectorBasicConfigDirective
} from '@home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract';
import { OpcVersionMappingUtil } from '@home/components/widget/lib/gateway/utils/opc-version-mapping.util';
@Component({
selector: 'tb-opc-ua-legacy-basic-config',
templateUrl: './opc-ua-basic-config.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => OpcUaLegacyBasicConfigComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => OpcUaLegacyBasicConfigComponent),
multi: true
}
],
standalone: true,
imports: [
CommonModule,
SharedModule,
SecurityConfigComponent,
MappingTableComponent,
OpcServerConfigComponent,
],
styleUrls: ['./opc-ua-basic-config.component.scss']
})
export class OpcUaLegacyBasicConfigComponent extends GatewayConnectorBasicConfigDirective<OPCBasicConfig_v3_5_2, OPCLegacyBasicConfig> {
mappingTypes = MappingType;
isLegacy = true;
protected override initBasicFormGroup(): FormGroup {
return this.fb.group({
mapping: [],
server: [],
});
}
protected override mapConfigToFormValue(config: OPCLegacyBasicConfig): OPCBasicConfig_v3_5_2 {
return {
server: config.server ? OpcVersionMappingUtil.mapServerToUpgradedVersion(config.server) : {} as ServerConfig,
mapping: config.server?.mapping ? OpcVersionMappingUtil.mapMappingToUpgradedVersion(config.server.mapping) : [],
};
}
protected override getMappedValue(value: OPCBasicConfig_v3_5_2): OPCLegacyBasicConfig {
return {
server: OpcVersionMappingUtil.mapServerToDowngradedVersion(value),
};
}
}

24
ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts

@ -26,12 +26,14 @@ import {
AddConnectorConfigData,
ConnectorType,
CreatedConnectorConfigData,
GatewayConnector,
GatewayConnectorDefaultTypesTranslatesMap,
GatewayLogLevel,
getDefaultConfig,
GatewayVersion,
GatewayVersionedDefaultConfig,
noLeadTrailSpacesRegex
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { Subject } from 'rxjs';
import { Observable, Subject } from 'rxjs';
import { ResourcesService } from '@core/services/resources.service';
import { takeUntil, tap } from 'rxjs/operators';
import { helpBaseUrl } from '@shared/models/constants';
@ -42,7 +44,8 @@ import { helpBaseUrl } from '@shared/models/constants';
styleUrls: ['./add-connector-dialog.component.scss'],
providers: [],
})
export class AddConnectorDialogComponent extends DialogComponent<AddConnectorDialogComponent, BaseData<HasId>> implements OnInit, OnDestroy {
export class AddConnectorDialogComponent
extends DialogComponent<AddConnectorDialogComponent, BaseData<HasId>> implements OnInit, OnDestroy {
connectorForm: UntypedFormGroup;
@ -95,8 +98,15 @@ export class AddConnectorDialogComponent extends DialogComponent<AddConnectorDia
this.submitted = true;
const value = this.connectorForm.getRawValue();
if (value.useDefaults) {
getDefaultConfig(this.resourcesService, value.type).subscribe((defaultConfig) => {
value.configurationJson = defaultConfig;
this.getDefaultConfig(value.type).subscribe((defaultConfig: GatewayVersionedDefaultConfig) => {
const gatewayVersion = this.data.gatewayVersion;
if (gatewayVersion) {
value.configVersion = gatewayVersion;
}
value.configurationJson = (gatewayVersion === GatewayVersion.Current
? defaultConfig[this.data.gatewayVersion]
: defaultConfig.legacy)
?? defaultConfig;
if (this.connectorForm.valid) {
this.dialogRef.close(value);
}
@ -130,4 +140,8 @@ export class AddConnectorDialogComponent extends DialogComponent<AddConnectorDia
takeUntil(this.destroy$),
).subscribe();
}
private getDefaultConfig(type: string): Observable<GatewayVersionedDefaultConfig | GatewayConnector> {
return this.resourcesService.loadJsonResource(`/assets/metadata/connector-default-configs/${type}.json`);
};
}

27
ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.ts

@ -43,15 +43,16 @@ import {
MappingType,
MappingTypeTranslationsMap,
noLeadTrailSpacesRegex,
OPCUaSourceTypes,
OPCUaSourceType,
QualityTypes,
QualityTypeTranslationsMap,
RequestMappingData,
RequestMappingFormValue,
RequestType,
RequestTypesTranslationsMap,
RpcMethod,
ServerSideRPCType,
SourceTypes,
SourceType,
SourceTypeTranslationsMap,
Timeseries
} from '@home/components/widget/lib/gateway/gateway-widget.models';
@ -82,10 +83,10 @@ export class MappingDialogComponent extends DialogComponent<MappingDialogCompone
ConvertorTypeEnum = ConvertorType;
ConvertorTypeTranslationsMap = ConvertorTypeTranslationsMap;
sourceTypes: SourceTypes[] = Object.values(SourceTypes);
OPCUaSourceTypes = Object.values(OPCUaSourceTypes) as Array<OPCUaSourceTypes>;
OPCUaSourceTypesEnum = OPCUaSourceTypes;
sourceTypesEnum = SourceTypes;
sourceTypes: SourceType[] = Object.values(SourceType);
OPCUaSourceTypes = Object.values(OPCUaSourceType) as Array<OPCUaSourceType>;
OPCUaSourceTypesEnum = OPCUaSourceType;
sourceTypesEnum = SourceType;
SourceTypeTranslationsMap = SourceTypeTranslationsMap;
requestTypes: RequestType[] = Object.values(RequestType);
@ -230,8 +231,8 @@ export class MappingDialogComponent extends DialogComponent<MappingDialogCompone
noKeysText: MappingKeysNoKeysTextTranslationsMap.get(keysType)
};
if (this.data.mappingType === MappingType.OPCUA) {
ctx.valueTypeKeys = Object.values(OPCUaSourceTypes);
ctx.valueTypeEnum = OPCUaSourceTypes;
ctx.valueTypeKeys = Object.values(OPCUaSourceType);
ctx.valueTypeEnum = OPCUaSourceType;
ctx.valueTypes = SourceTypeTranslationsMap;
}
this.keysPopupClosed = false;
@ -293,8 +294,8 @@ export class MappingDialogComponent extends DialogComponent<MappingDialogCompone
requestType: this.data.value.requestType,
requestValue: {
[this.data.value.requestType]: this.data.value.requestValue
}
} as RequestMappingFormValue;
} as Record<RequestType, RequestMappingData>
};
default:
return this.data.value as DeviceConnectorMapping;
}
@ -349,10 +350,10 @@ export class MappingDialogComponent extends DialogComponent<MappingDialogCompone
attributeRequests: this.fb.group({
topicFilter: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
deviceInfo: this.fb.group({
deviceNameExpressionSource: [SourceTypes.MSG, []],
deviceNameExpressionSource: [SourceType.MSG, []],
deviceNameExpression: ['', [Validators.required]],
}),
attributeNameExpressionSource: [SourceTypes.MSG, []],
attributeNameExpressionSource: [SourceType.MSG, []],
attributeNameExpression: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
topicExpression: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
valueExpression: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]],
@ -407,7 +408,7 @@ export class MappingDialogComponent extends DialogComponent<MappingDialogCompone
private createOPCUAMappingForm(): void {
this.mappingForm = this.fb.group({
deviceNodeSource: [OPCUaSourceTypes.PATH, []],
deviceNodeSource: [OPCUaSourceType.PATH, []],
deviceNodePattern: ['', [Validators.required]],
deviceInfo: [{}, []],
attributes: [[], []],

43
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html

@ -156,6 +156,7 @@
<div class="tb-form-panel-title">
{{ initialConnector?.type ? GatewayConnectorTypesTranslatesMap.get(initialConnector.type) : '' }}
{{ 'gateway.configuration' | translate }}
<span class="version-placeholder" *ngIf="connectorForm.get('configVersion').value">v{{connectorForm.get('configVersion').value}}</span>
</div>
<tb-toggle-select *ngIf="initialConnector && allowBasicConfig.has(initialConnector.type)"
formControlName="mode" appearance="fill">
@ -175,18 +176,36 @@
<section class="tb-form-panel section-container no-border no-padding tb-flex space-between" *ngIf="initialConnector">
<ng-container *ngIf="connectorForm.get('mode')?.value === ConnectorConfigurationModes.BASIC else defaultConfig">
<ng-container [ngSwitch]="initialConnector.type">
<tb-mqtt-basic-config *ngSwitchCase="ConnectorType.MQTT"
formControlName="basicConfig"
[generalTabContent]="generalTabContent">
</tb-mqtt-basic-config>
<tb-opc-ua-basic-config *ngSwitchCase="ConnectorType.OPCUA"
formControlName="basicConfig"
[generalTabContent]="generalTabContent">
</tb-opc-ua-basic-config>
<tb-modbus-basic-config *ngSwitchCase="ConnectorType.MODBUS"
formControlName="basicConfig"
[generalTabContent]="generalTabContent">
</tb-modbus-basic-config>
<ng-container *ngSwitchCase="ConnectorType.MQTT">
<tb-mqtt-basic-config
*ngIf="connectorForm.get('configVersion').value === GatewayVersion.Current else legacy"
formControlName="basicConfig"
[generalTabContent]="generalTabContent"
/>
<ng-template #legacy>
<tb-mqtt-legacy-basic-config formControlName="basicConfig" [generalTabContent]="generalTabContent"/>
</ng-template>
</ng-container>
<ng-container *ngSwitchCase="ConnectorType.OPCUA">
<tb-opc-ua-basic-config
*ngIf="connectorForm.get('configVersion').value === GatewayVersion.Current else legacy"
formControlName="basicConfig"
[generalTabContent]="generalTabContent"
/>
<ng-template #legacy>
<tb-opc-ua-legacy-basic-config formControlName="basicConfig" [generalTabContent]="generalTabContent"/>
</ng-template>
</ng-container>
<ng-container *ngSwitchCase="ConnectorType.MODBUS">
<tb-modbus-basic-config
*ngIf="connectorForm.get('configVersion').value === GatewayVersion.Current else legacy"
formControlName="basicConfig"
[generalTabContent]="generalTabContent"
/>
<ng-template #legacy>
<tb-modbus-legacy-basic-config formControlName="basicConfig" [generalTabContent]="generalTabContent"/>
</ng-template>
</ng-container>
</ng-container>
</ng-container>
<ng-template #defaultConfig>

5
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.scss

@ -22,6 +22,11 @@
overflow-x: auto;
padding: 0;
.version-placeholder {
color: gray;
font-size: 12px
}
.connector-container {
height: 100%;
width: 100%;

32
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts

@ -59,12 +59,16 @@ import {
GatewayConnectorDefaultTypesTranslatesMap,
GatewayLogLevel,
noLeadTrailSpacesRegex,
GatewayVersion,
} from './gateway-widget.models';
import { MatDialog } from '@angular/material/dialog';
import { AddConnectorDialogComponent } from '@home/components/widget/lib/gateway/dialog/add-connector-dialog.component';
import { debounceTime, filter, switchMap, take, takeUntil, tap } from 'rxjs/operators';
import { ErrorStateMatcher } from '@angular/material/core';
import { PageData } from '@shared/models/page/page-data';
import {
GatewayConnectorVersionMappingUtil
} from '@home/components/widget/lib/gateway/utils/gateway-connector-version-mapping.util';
export class ForceErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null): boolean {
@ -98,6 +102,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
readonly displayedColumns = ['enabled', 'key', 'type', 'syncStatus', 'errors', 'actions'];
readonly GatewayConnectorTypesTranslatesMap = GatewayConnectorDefaultTypesTranslatesMap;
readonly ConnectorConfigurationModes = ConfigurationModes;
readonly GatewayVersion = GatewayVersion;
pageLink: PageLink;
dataSource: MatTableDataSource<GatewayAttributeData>;
@ -106,6 +111,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
mode: ConfigurationModes = this.ConnectorConfigurationModes.BASIC;
initialConnector: GatewayConnector;
private gatewayVersion: string;
private inactiveConnectors: Array<string>;
private attributeDataSource: AttributeDatasource;
private inactiveConnectorsDataSource: AttributeDatasource;
@ -239,6 +245,10 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
delete value.class;
}
if (this.gatewayVersion && !value.configVersion) {
value.configVersion = this.gatewayVersion;
}
value.ts = Date.now();
return value;
@ -263,7 +273,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
});
}
isConnectorSynced(attribute: GatewayAttributeData) {
isConnectorSynced(attribute: GatewayAttributeData): boolean {
const connectorData = attribute.value;
if (!connectorData.ts || attribute.skipSync) {
return false;
@ -340,7 +350,8 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
class: '',
configuration: '',
configurationJson: {},
basicConfig: {}
basicConfig: {},
configVersion: ''
}, {emitEvent: false});
this.connectorForm.markAsPristine();
}
@ -483,7 +494,10 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
return this.dialog.open<AddConnectorDialogComponent, AddConnectorConfigData>(AddConnectorDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: { dataSourceData: this.dataSource.data }
data: {
dataSourceData: this.dataSource.data,
gatewayVersion: this.gatewayVersion,
}
}).afterClosed();
}
@ -526,7 +540,8 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
class: [''],
configuration: [''],
configurationJson: [{}, [Validators.required]],
basicConfig: [{}]
basicConfig: [{}],
configVersion: [''],
});
this.connectorForm.disable();
}
@ -560,10 +575,12 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
forkJoin([
this.attributeService.getEntityAttributes(this.device, AttributeScope.SHARED_SCOPE, ['active_connectors']),
this.attributeService.getEntityAttributes(this.device, AttributeScope.SERVER_SCOPE, ['inactive_connectors'])
this.attributeService.getEntityAttributes(this.device, AttributeScope.SERVER_SCOPE, ['inactive_connectors']),
this.attributeService.getEntityAttributes(this.device, AttributeScope.CLIENT_SCOPE, ['Version'])
]).pipe(takeUntil(this.destroy$)).subscribe(attributes => {
this.activeConnectors = this.parseConnectors(attributes[0]);
this.inactiveConnectors = this.parseConnectors(attributes[1]);
this.gatewayVersion = attributes[2][0]?.value;
this.updateData(true);
});
@ -721,12 +738,12 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
this.connectorForm.enable();
}
const connectorState = {
const connectorState = GatewayConnectorVersionMappingUtil.getConfig({
configuration: '',
key: 'auto',
configurationJson: {} as ConnectorBaseConfig,
...connector,
};
}, this.gatewayVersion);
connectorState.basicConfig = connectorState.configurationJson;
this.initialConnector = connectorState;
@ -739,6 +756,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
case ConnectorType.OPCUA:
case ConnectorType.MODBUS:
this.connectorForm.get('mode').setValue(connector.mode || ConfigurationModes.BASIC, {emitEvent: false});
this.connectorForm.get('configVersion').setValue(connector.configVersion, {emitEvent: false});
setTimeout(() => {
this.connectorForm.patchValue(connector, {emitEvent: false});
this.connectorForm.markAsPristine();

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

@ -14,8 +14,6 @@
/// limitations under the License.
///
import { ResourcesService } from '@core/services/resources.service';
import { Observable } from 'rxjs';
import { helpBaseUrl, ValueTypeData } from '@shared/models/constants';
import { AttributeData } from '@shared/models/telemetry/telemetry.models';
@ -115,21 +113,30 @@ export interface GatewayAttributeData extends AttributeData {
skipSync?: boolean;
}
export interface GatewayConnector {
export interface GatewayConnectorBase {
name: string;
type: ConnectorType;
configuration?: string;
configurationJson: ConnectorBaseConfig;
basicConfig?: ConnectorBaseConfig;
logLevel: string;
key?: string;
class?: string;
mode?: ConfigurationModes;
configVersion?: string;
}
export interface GatewayConnector<BaseConfig = ConnectorBaseConfig> extends GatewayConnectorBase {
configurationJson: BaseConfig;
basicConfig?: BaseConfig;
}
export interface GatewayVersionedDefaultConfig {
legacy: GatewayConnector<ConnectorLegacyConfig>;
'3.5.2': GatewayConnector<ConnectorBaseConfig_v3_5_2>;
}
export interface DataMapping {
topicFilter: string;
QoS: string;
QoS: string | number;
converter: Converter;
}
@ -181,11 +188,20 @@ export interface ConnectorSecurity {
mode?: ModeType;
}
export type ConnectorMapping = DeviceConnectorMapping | RequestMappingData | ConverterConnectorMapping;
export enum GatewayVersion {
Current = '3.5.2',
Legacy = 'legacy'
}
export type ConnectorMapping = DeviceConnectorMapping | RequestMappingValue | ConverterConnectorMapping;
export type ConnectorMappingFormValue = DeviceConnectorMapping | RequestMappingFormValue | ConverterMappingFormValue;
export type ConnectorBaseConfig = ConnectorBaseInfo | MQTTBasicConfig | OPCBasicConfig | ModbusBasicConfig;
export type ConnectorBaseConfig = ConnectorBaseConfig_v3_5_2 | ConnectorLegacyConfig;
export type ConnectorLegacyConfig = ConnectorBaseInfo | MQTTLegacyBasicConfig | OPCLegacyBasicConfig | ModbusBasicConfig;
export type ConnectorBaseConfig_v3_5_2 = ConnectorBaseInfo | MQTTBasicConfig_v3_5_2 | OPCBasicConfig_v3_5_2;
export interface ConnectorBaseInfo {
name: string;
@ -194,33 +210,64 @@ export interface ConnectorBaseInfo {
logLevel: GatewayLogLevel;
}
export interface MQTTBasicConfig {
dataMapping: ConverterConnectorMapping[];
requestsMapping: Record<RequestType, RequestMappingData[]> | RequestMappingData[];
export type MQTTBasicConfig = MQTTBasicConfig_v3_5_2 | MQTTLegacyBasicConfig;
export interface MQTTBasicConfig_v3_5_2 {
mapping: ConverterConnectorMapping[];
requestsMapping: Record<RequestType, RequestMappingData[] | RequestMappingValue[]> | RequestMappingData[] | RequestMappingValue[];
broker: BrokerConfig;
workers?: WorkersConfig;
}
export interface OPCBasicConfig {
export interface MQTTLegacyBasicConfig {
mapping: LegacyConverterConnectorMapping[];
broker: BrokerConfig;
workers?: WorkersConfig;
connectRequests: LegacyRequestMappingData[];
disconnectRequests: LegacyRequestMappingData[];
attributeRequests: LegacyRequestMappingData[];
attributeUpdates: LegacyRequestMappingData[];
serverSideRpc: LegacyRequestMappingData[];
}
export type OPCBasicConfig = OPCBasicConfig_v3_5_2 | OPCLegacyBasicConfig;
export interface OPCBasicConfig_v3_5_2 {
mapping: DeviceConnectorMapping[];
server: ServerConfig;
}
export interface ModbusBasicConfig {
export interface OPCLegacyBasicConfig {
server: LegacyServerConfig;
}
export interface LegacyServerConfig extends Omit<ServerConfig, 'enableSubscriptions'> {
mapping: LegacyDeviceConnectorMapping[];
disableSubscriptions: boolean;
}
export type ModbusBasicConfig = ModbusBasicConfig_v3_5_2 | ModbusLegacyBasicConfig;
export interface ModbusBasicConfig_v3_5_2 {
master: ModbusMasterConfig;
slave: ModbusSlave;
}
export interface ModbusLegacyBasicConfig {
master: ModbusMasterConfig;
slave: ModbusLegacySlave;
}
export interface WorkersConfig {
maxNumberOfWorkers: number;
maxMessageNumberPerWorker: number;
}
interface DeviceInfo {
export interface ConnectorDeviceInfo {
deviceNameExpression: string;
deviceNameExpressionSource: string;
deviceNameExpressionSource: SourceType | OPCUaSourceType;
deviceProfileExpression: string;
deviceProfileExpressionSource: string;
deviceProfileExpressionSource: SourceType | OPCUaSourceType;
}
export interface Attribute {
@ -229,13 +276,23 @@ export interface Attribute {
value: string;
}
export interface LegacyAttribute {
key: string;
path: string;
}
export interface Timeseries {
key: string;
type: string;
value: string;
}
interface RpcArgument {
export interface LegacyTimeseries {
key: string;
path: string;
}
export interface RpcArgument {
type: string;
value: number;
}
@ -245,28 +302,59 @@ export interface RpcMethod {
arguments: RpcArgument[];
}
export interface LegacyRpcMethod {
method: string;
arguments: unknown[];
}
export interface AttributesUpdate {
key: string;
type: string;
value: string;
}
export interface LegacyDeviceAttributeUpdate {
attributeOnThingsBoard: string;
attributeOnDevice: string;
}
export interface Converter {
type: ConvertorType;
deviceNameJsonExpression: string;
deviceTypeJsonExpression: string;
deviceInfo?: ConnectorDeviceInfo;
sendDataOnlyOnChange: boolean;
timeout: number;
attributes: Attribute[];
timeseries: Timeseries[];
attributes?: Attribute[];
timeseries?: Timeseries[];
extension?: string;
cached?: boolean;
extensionConfig?: Record<string, number>;
}
export interface LegacyConverter extends Converter {
deviceNameJsonExpression?: string;
deviceTypeJsonExpression?: string;
deviceNameTopicExpression?: string;
deviceTypeTopicExpression?: string;
deviceNameExpression?: string;
deviceNameExpressionSource?: string;
deviceTypeExpression?: string;
deviceProfileExpression?: string;
deviceProfileExpressionSource?: string;
['extension-config']?: Record<string, unknown>;
}
export interface ConverterConnectorMapping {
topicFilter: string;
subscriptionQos?: string;
subscriptionQos?: string | number;
converter: Converter;
}
export interface LegacyConverterConnectorMapping {
topicFilter: string;
subscriptionQos?: string | number;
converter: LegacyConverter;
}
export type ConverterMappingFormValue = Omit<ConverterConnectorMapping, 'converter'> & {
converter: {
type: ConvertorType;
@ -275,14 +363,24 @@ export type ConverterMappingFormValue = Omit<ConverterConnectorMapping, 'convert
export interface DeviceConnectorMapping {
deviceNodePattern: string;
deviceNodeSource: string;
deviceInfo: DeviceInfo;
deviceNodeSource: OPCUaSourceType;
deviceInfo: ConnectorDeviceInfo;
attributes?: Attribute[];
timeseries?: Timeseries[];
rpc_methods?: RpcMethod[];
attributes_updates?: AttributesUpdate[];
}
export interface LegacyDeviceConnectorMapping {
deviceNamePattern: string;
deviceNodePattern: string;
deviceTypePattern: string;
attributes?: LegacyAttribute[];
timeseries?: LegacyTimeseries[];
rpc_methods?: LegacyRpcMethod[];
attributes_updates?: LegacyDeviceAttributeUpdate[];
}
export enum ConnectorType {
MQTT = 'mqtt',
MODBUS = 'modbus',
@ -461,6 +559,7 @@ export interface GatewayLogData {
export interface AddConnectorConfigData {
dataSourceData: Array<any>;
gatewayVersion: string;
}
export interface CreatedConnectorConfigData {
@ -591,13 +690,13 @@ export const ConvertorTypeTranslationsMap = new Map<ConvertorType, string>(
]
);
export enum SourceTypes {
export enum SourceType {
MSG = 'message',
TOPIC = 'topic',
CONST = 'constant'
}
export enum OPCUaSourceTypes {
export enum OPCUaSourceType {
PATH = 'path',
IDENTIFIER = 'identifier',
CONST = 'constant'
@ -608,33 +707,116 @@ export enum DeviceInfoType {
PARTIAL = 'partial'
}
export const SourceTypeTranslationsMap = new Map<SourceTypes | OPCUaSourceTypes, string>(
export const SourceTypeTranslationsMap = new Map<SourceType | OPCUaSourceType, string>(
[
[SourceTypes.MSG, 'gateway.source-type.msg'],
[SourceTypes.TOPIC, 'gateway.source-type.topic'],
[SourceTypes.CONST, 'gateway.source-type.const'],
[OPCUaSourceTypes.PATH, 'gateway.source-type.path'],
[OPCUaSourceTypes.IDENTIFIER, 'gateway.source-type.identifier'],
[OPCUaSourceTypes.CONST, 'gateway.source-type.const']
[SourceType.MSG, 'gateway.source-type.msg'],
[SourceType.TOPIC, 'gateway.source-type.topic'],
[SourceType.CONST, 'gateway.source-type.const'],
[OPCUaSourceType.PATH, 'gateway.source-type.path'],
[OPCUaSourceType.IDENTIFIER, 'gateway.source-type.identifier'],
[OPCUaSourceType.CONST, 'gateway.source-type.const']
]
);
export interface RequestMappingData {
export interface RequestMappingValue {
requestType: RequestType;
requestValue: RequestDataItem;
requestValue: RequestMappingData;
}
export type RequestMappingFormValue = Omit<RequestMappingData, 'requestValue'> & {
requestValue: Record<RequestType, RequestDataItem>;
};
export interface RequestDataItem {
type: string;
details: string;
export interface RequestMappingFormValue {
requestType: RequestType;
methodFilter?: string;
attributeFilter?: string;
topicFilter?: string;
requestValue: Record<RequestType, RequestMappingData>;
}
export type RequestMappingData = ConnectRequest | DisconnectRequest | AttributeRequest | AttributeUpdate | ServerSideRpc;
export type LegacyRequestMappingData =
LegacyConnectRequest
| LegacyDisconnectRequest
| LegacyAttributeRequest
| LegacyAttributeUpdate
| LegacyServerSideRpc;
export interface ConnectRequest {
topicFilter: string;
deviceInfo: ConnectorDeviceInfo;
}
export interface DisconnectRequest {
topicFilter: string;
deviceInfo: ConnectorDeviceInfo;
}
export interface AttributeRequest {
retain: boolean;
topicFilter: string;
deviceInfo: ConnectorDeviceInfo;
attributeNameExpressionSource: SourceType;
attributeNameExpression: string;
topicExpression: string;
valueExpression: string;
}
export interface AttributeUpdate {
retain: boolean;
deviceNameFilter: string;
attributeFilter: string;
topicExpression: string;
valueExpression: string;
}
export interface ServerSideRpc {
type: ServerSideRpcType;
deviceNameFilter: string;
methodFilter: string;
requestTopicExpression: string;
responseTopicExpression?: string;
responseTopicQoS?: number;
responseTimeout?: number;
valueExpression: string;
}
export enum ServerSideRpcType {
WithResponse = 'twoWay',
WithoutResponse = 'oneWay'
}
export interface LegacyConnectRequest {
topicFilter: string;
deviceNameJsonExpression?: string;
deviceNameTopicExpression?: string;
}
interface LegacyDisconnectRequest {
topicFilter: string;
deviceNameJsonExpression?: string;
deviceNameTopicExpression?: string;
}
interface LegacyAttributeRequest {
retain: boolean;
topicFilter: string;
deviceNameJsonExpression: string;
attributeNameJsonExpression: string;
topicExpression: string;
valueExpression: string;
}
interface LegacyAttributeUpdate {
retain: boolean;
deviceNameFilter: string;
attributeFilter: string;
topicExpression: string;
valueExpression: string;
}
interface LegacyServerSideRpc {
deviceNameFilter: string;
methodFilter: string;
requestTopicExpression: string;
responseTopicExpression?: string;
responseTimeout?: number;
valueExpression: string;
}
export enum RequestType {
@ -708,9 +890,6 @@ export enum ServerSideRPCType {
TWO_WAY = 'twoWay'
}
export const getDefaultConfig = (resourcesService: ResourcesService, type: string): Observable<any> =>
resourcesService.loadJsonResource(`/assets/metadata/connector-default-configs/${type}.json`);
export enum MappingValueType {
STRING = 'string',
INTEGER = 'integer',
@ -938,7 +1117,7 @@ export interface SlaveConfig {
pollPeriod: number;
unitId: number;
deviceName: string;
deviceType: string;
deviceType?: string;
sendDataOnlyOnChange: boolean;
connectAttemptTimeMs: number;
connectAttemptCount: number;
@ -990,8 +1169,19 @@ export interface ModbusSlave {
security: ModbusSecurity;
}
export interface ModbusLegacySlave extends Omit<ModbusSlave, 'values'> {
values: ModbusLegacyRegisterValues;
}
export type ModbusValuesState = ModbusRegisterValues | ModbusValues;
export interface ModbusLegacyRegisterValues {
holding_registers: ModbusValues[];
coils_initializer: ModbusValues[];
input_registers: ModbusValues[];
discrete_inputs: ModbusValues[];
}
export interface ModbusRegisterValues {
holding_registers: ModbusValues;
coils_initializer: ModbusValues;

10
ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-help-link.pipe.ts

@ -17,8 +17,8 @@
import { Pipe, PipeTransform } from '@angular/core';
import {
MappingValueType,
OPCUaSourceTypes,
SourceTypes
OPCUaSourceType,
SourceType
} from '@home/components/widget/lib/gateway/gateway-widget.models';
@Pipe({
@ -26,9 +26,9 @@ import {
standalone: true,
})
export class GatewayHelpLinkPipe implements PipeTransform {
transform(field: string, sourceType: SourceTypes | OPCUaSourceTypes, sourceTypes?: Array<SourceTypes | OPCUaSourceTypes | MappingValueType> ): string {
if (!sourceTypes || sourceTypes?.includes(OPCUaSourceTypes.PATH)) {
if (sourceType !== OPCUaSourceTypes.CONST) {
transform(field: string, sourceType: SourceType | OPCUaSourceType, sourceTypes?: Array<SourceType | OPCUaSourceType | MappingValueType> ): string {
if (!sourceTypes || sourceTypes?.includes(OPCUaSourceType.PATH)) {
if (sourceType !== OPCUaSourceType.CONST) {
return `widget/lib/gateway/${field}-${sourceType}_fn`;
} else {
return;

42
ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/gateway-connector-version-mapping.util.ts

@ -0,0 +1,42 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {
ConnectorType,
GatewayConnector,
ModbusBasicConfig,
MQTTBasicConfig,
OPCBasicConfig,
} from '@home/components/widget/lib/gateway/gateway-widget.models';
import { MqttVersionProcessor } from '@home/components/widget/lib/gateway/abstract/mqtt-version-processor.abstract';
import { OpcVersionProcessor } from '@home/components/widget/lib/gateway/abstract/opc-version-processor.abstract';
import { ModbusVersionProcessor } from '@home/components/widget/lib/gateway/abstract/modbus-version-processor.abstract';
export abstract class GatewayConnectorVersionMappingUtil {
static getConfig(connector: GatewayConnector, gatewayVersion: string): GatewayConnector {
switch(connector.type) {
case ConnectorType.MQTT:
return new MqttVersionProcessor(gatewayVersion, connector as GatewayConnector<MQTTBasicConfig>).getProcessedByVersion();
case ConnectorType.OPCUA:
return new OpcVersionProcessor(gatewayVersion, connector as GatewayConnector<OPCBasicConfig>).getProcessedByVersion();
case ConnectorType.MODBUS:
return new ModbusVersionProcessor(gatewayVersion, connector as GatewayConnector<ModbusBasicConfig>).getProcessedByVersion();
default:
return connector;
}
}
}

80
ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/modbus-version-mapping.util.ts

@ -0,0 +1,80 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {
ModbusDataType,
ModbusLegacyRegisterValues,
ModbusLegacySlave,
ModbusMasterConfig,
ModbusRegisterValues,
ModbusSlave,
ModbusValue,
ModbusValues,
SlaveConfig
} from '@home/components/widget/lib/gateway/gateway-widget.models';
export class ModbusVersionMappingUtil {
static mapMasterToUpgradedVersion(master: ModbusMasterConfig): ModbusMasterConfig {
return {
slaves: master.slaves.map((slave: SlaveConfig) => ({
...slave,
deviceType: slave.deviceType ?? 'default',
}))
};
}
static mapSlaveToDowngradedVersion(slave: ModbusSlave): ModbusLegacySlave {
const values = Object.keys(slave.values).reduce((acc, valueKey) => {
acc = {
...acc,
[valueKey]: [
slave.values[valueKey]
]
};
return acc;
}, {} as ModbusLegacyRegisterValues);
return {
...slave,
values
};
}
static mapSlaveToUpgradedVersion(slave: ModbusLegacySlave): ModbusSlave {
const values = Object.keys(slave.values).reduce((acc, valueKey) => {
acc = {
...acc,
[valueKey]: this.mapValuesToUpgradedVersion(slave.values[valueKey][0])
};
return acc;
}, {} as ModbusRegisterValues);
return {
...slave,
values
};
}
private static mapValuesToUpgradedVersion(registerValues: ModbusValues): ModbusValues {
return Object.keys(registerValues).reduce((acc, valueKey) => {
acc = {
...acc,
[valueKey]: registerValues[valueKey].map((value: ModbusValue) =>
({ ...value, type: (value.type as string) === 'int' ? ModbusDataType.INT16 : value.type }))
};
return acc;
}, {} as ModbusValues);
}
}

217
ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/mqtt-version-mapping.util.ts

@ -0,0 +1,217 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { deleteNullProperties } from '@core/utils';
import {
AttributeRequest,
ConnectorDeviceInfo,
Converter,
ConverterConnectorMapping,
ConvertorType,
LegacyConverter,
LegacyConverterConnectorMapping,
LegacyRequestMappingData,
RequestMappingData,
RequestType,
ServerSideRpc,
ServerSideRpcType,
SourceType
} from '@home/components/widget/lib/gateway/gateway-widget.models';
export class MqttVersionMappingUtil {
static readonly mqttRequestTypeKeys = Object.values(RequestType);
static readonly mqttRequestMappingOldFields =
['attributeNameJsonExpression', 'deviceNameJsonExpression', 'deviceNameTopicExpression', 'extension-config'];
static readonly mqttRequestMappingNewFields =
['attributeNameExpressionSource', 'responseTopicQoS', 'extensionConfig'];
static mapMappingToUpgradedVersion(
mapping: LegacyConverterConnectorMapping[]
): ConverterConnectorMapping[] {
return mapping?.map(({ converter, topicFilter, subscriptionQos = 1 }) => {
const deviceInfo = converter.deviceInfo ?? this.extractConverterDeviceInfo(converter);
const newConverter = {
...converter,
deviceInfo,
extensionConfig: converter.extensionConfig || converter['extension-config'] || null
};
this.cleanUpOldFields(newConverter);
return { converter: newConverter, topicFilter, subscriptionQos };
}) as ConverterConnectorMapping[];
}
static mapRequestsToUpgradedVersion(
requestMapping: Record<RequestType,
LegacyRequestMappingData[]>
): Record<RequestType, RequestMappingData[]> {
return this.mqttRequestTypeKeys.reduce((acc, key: RequestType) => {
if (!requestMapping[key]) {
return acc;
}
acc[key] = requestMapping[key].map(value => {
const newValue = this.mapRequestToUpgradedVersion(value as LegacyRequestMappingData, key);
this.cleanUpOldFields(newValue as {});
return newValue;
});
return acc;
}, {}) as Record<RequestType, RequestMappingData[]>;
}
static mapRequestsToDowngradedVersion(
requestsMapping: Record<RequestType, RequestMappingData[]>
): Record<RequestType, LegacyRequestMappingData[]> {
return this.mqttRequestTypeKeys.reduce((acc, key) => {
if (!requestsMapping[key]) {
return acc;
}
acc[key] = requestsMapping[key].map((value: RequestMappingData) => {
if (key === RequestType.SERVER_SIDE_RPC) {
delete (value as ServerSideRpc).type;
}
const { attributeNameExpression, deviceInfo, ...rest } = value as AttributeRequest;
const newValue = {
...rest,
attributeNameJsonExpression: attributeNameExpression || null,
deviceNameJsonExpression: deviceInfo?.deviceNameExpressionSource !== SourceType.TOPIC ? deviceInfo?.deviceNameExpression : null,
deviceNameTopicExpression: deviceInfo?.deviceNameExpressionSource === SourceType.TOPIC ? deviceInfo?.deviceNameExpression : null,
};
this.cleanUpNewFields(newValue);
return newValue;
});
return acc;
}, {}) as Record<RequestType, LegacyRequestMappingData[]>;
}
static mapMappingToDowngradedVersion(
mapping: ConverterConnectorMapping[]
): LegacyConverterConnectorMapping[] {
return mapping?.map((converterMapping: ConverterConnectorMapping) => {
const converter = this.mapConverterToDowngradedVersion(converterMapping.converter);
this.cleanUpNewFields(converter as {});
return { converter, topicFilter: converterMapping.topicFilter };
});
}
private static mapConverterToDowngradedVersion(converter: Converter): LegacyConverter {
const { deviceInfo, ...restConverter } = converter;
return converter.type !== ConvertorType.BYTES ? {
...restConverter,
deviceNameJsonExpression: deviceInfo?.deviceNameExpressionSource === SourceType.MSG ? deviceInfo.deviceNameExpression : null,
deviceTypeJsonExpression:
deviceInfo?.deviceProfileExpressionSource === SourceType.MSG ? deviceInfo.deviceProfileExpression : null,
deviceNameTopicExpression:
deviceInfo?.deviceNameExpressionSource !== SourceType.MSG
? deviceInfo?.deviceNameExpression
: null,
deviceTypeTopicExpression: deviceInfo?.deviceProfileExpressionSource !== SourceType.MSG
? deviceInfo?.deviceProfileExpression
: null,
} : {
...restConverter,
deviceNameExpression: deviceInfo.deviceNameExpression,
deviceTypeExpression: deviceInfo.deviceProfileExpression,
['extension-config']: converter.extensionConfig,
};
}
private static cleanUpOldFields(obj: Record<string, unknown>): void {
this.mqttRequestMappingOldFields.forEach(field => delete obj[field]);
deleteNullProperties(obj);
}
private static cleanUpNewFields(obj: Record<string, unknown>): void {
this.mqttRequestMappingNewFields.forEach(field => delete obj[field]);
deleteNullProperties(obj);
}
private static getTypeSourceByValue(value: string): SourceType {
if (value.includes('${')) {
return SourceType.MSG;
}
if (value.includes(`/`)) {
return SourceType.TOPIC;
}
return SourceType.CONST;
}
private static extractConverterDeviceInfo(converter: LegacyConverter): ConnectorDeviceInfo {
const deviceNameExpression = converter.deviceNameExpression
|| converter.deviceNameJsonExpression
|| converter.deviceNameTopicExpression
|| null;
const deviceNameExpressionSource = converter.deviceNameExpressionSource
? converter.deviceNameExpressionSource as SourceType
: deviceNameExpression ? this.getTypeSourceByValue(deviceNameExpression) : null;
const deviceProfileExpression = converter.deviceProfileExpression
|| converter.deviceTypeTopicExpression
|| converter.deviceTypeJsonExpression
|| 'default';
const deviceProfileExpressionSource = converter.deviceProfileExpressionSource
? converter.deviceProfileExpressionSource as SourceType
: deviceProfileExpression ? this.getTypeSourceByValue(deviceProfileExpression) : null;
return deviceNameExpression || deviceProfileExpression ? {
deviceNameExpression,
deviceNameExpressionSource,
deviceProfileExpression,
deviceProfileExpressionSource
} : null;
}
private static mapRequestToUpgradedVersion(value, key: RequestType): RequestMappingData {
const deviceNameExpression = value.deviceNameJsonExpression || value.deviceNameTopicExpression || null;
const deviceProfileExpression = value.deviceTypeTopicExpression || value.deviceTypeJsonExpression || 'default';
const deviceProfileExpressionSource = deviceProfileExpression ? this.getTypeSourceByValue(deviceProfileExpression) : null;
const attributeNameExpression = value.attributeNameExpressionSource || value.attributeNameJsonExpression || null;
const responseTopicQoS = key === RequestType.SERVER_SIDE_RPC ? 1 : null;
const type = key === RequestType.SERVER_SIDE_RPC
? (value as ServerSideRpc).responseTopicExpression
? ServerSideRpcType.WithResponse
: ServerSideRpcType.WithoutResponse
: null;
return {
...value,
attributeNameExpression,
attributeNameExpressionSource: attributeNameExpression ? this.getTypeSourceByValue(attributeNameExpression) : null,
deviceInfo: value.deviceInfo ? value.deviceInfo : deviceNameExpression ? {
deviceNameExpression,
deviceNameExpressionSource: this.getTypeSourceByValue(deviceNameExpression),
deviceProfileExpression,
deviceProfileExpressionSource
} : null,
responseTopicQoS,
type
};
}
}

134
ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/opc-version-mapping.util.ts

@ -0,0 +1,134 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {
Attribute,
AttributesUpdate,
DeviceConnectorMapping,
LegacyAttribute,
LegacyDeviceAttributeUpdate,
LegacyDeviceConnectorMapping,
LegacyRpcMethod,
LegacyServerConfig,
LegacyTimeseries,
OPCBasicConfig_v3_5_2,
OPCUaSourceType,
RpcArgument,
RpcMethod,
ServerConfig,
Timeseries
} from '@home/components/widget/lib/gateway/gateway-widget.models';
export class OpcVersionMappingUtil {
static mapServerToUpgradedVersion(server: LegacyServerConfig): ServerConfig {
const { mapping, disableSubscriptions, ...restServer } = server;
return {
...restServer,
enableSubscriptions: !disableSubscriptions,
};
}
static mapServerToDowngradedVersion(config: OPCBasicConfig_v3_5_2): LegacyServerConfig {
const { mapping, server } = config;
const { enableSubscriptions, ...restServer } = server;
return {
...restServer,
mapping: mapping ? this.mapMappingToDowngradedVersion(mapping) : [],
disableSubscriptions: !enableSubscriptions,
};
}
static mapMappingToUpgradedVersion(mapping: LegacyDeviceConnectorMapping[]): DeviceConnectorMapping[] {
return mapping.map((legacyMapping: LegacyDeviceConnectorMapping) => ({
...legacyMapping,
deviceNodeSource: this.getTypeSourceByValue(legacyMapping.deviceNodePattern),
deviceInfo: {
deviceNameExpression: legacyMapping.deviceNamePattern,
deviceNameExpressionSource: this.getTypeSourceByValue(legacyMapping.deviceNamePattern),
deviceProfileExpression: legacyMapping.deviceTypePattern ?? 'default',
deviceProfileExpressionSource: this.getTypeSourceByValue(legacyMapping.deviceTypePattern ?? 'default'),
},
attributes: legacyMapping.attributes.map((attribute: LegacyAttribute) => ({
key: attribute.key,
type: this.getTypeSourceByValue(attribute.path),
value: attribute.path,
})),
attributes_updates: legacyMapping.attributes_updates.map((attributeUpdate: LegacyDeviceAttributeUpdate) => ({
key: attributeUpdate.attributeOnThingsBoard,
type: this.getTypeSourceByValue(attributeUpdate.attributeOnDevice),
value: attributeUpdate.attributeOnDevice,
})),
timeseries: legacyMapping.timeseries.map((timeseries: LegacyTimeseries) => ({
key: timeseries.key,
type: this.getTypeSourceByValue(timeseries.path),
value: timeseries.path,
})),
rpc_methods: legacyMapping.rpc_methods.map((rpcMethod: LegacyRpcMethod) => ({
method: rpcMethod.method,
arguments: rpcMethod.arguments.map(arg => ({
value: arg,
type: this.getArgumentType(arg),
} as RpcArgument))
}))
}));
}
static mapMappingToDowngradedVersion(mapping: DeviceConnectorMapping[]): LegacyDeviceConnectorMapping[] {
return mapping.map((upgradedMapping: DeviceConnectorMapping) => ({
...upgradedMapping,
deviceNamePattern: upgradedMapping.deviceInfo.deviceNameExpression,
deviceTypePattern: upgradedMapping.deviceInfo.deviceProfileExpression,
attributes: upgradedMapping.attributes.map((attribute: Attribute) => ({
key: attribute.key,
path: attribute.value,
})),
attributes_updates: upgradedMapping.attributes_updates.map((attributeUpdate: AttributesUpdate) => ({
attributeOnThingsBoard: attributeUpdate.key,
attributeOnDevice: attributeUpdate.value,
})),
timeseries: upgradedMapping.timeseries.map((timeseries: Timeseries) => ({
key: timeseries.key,
path: timeseries.value,
})),
rpc_methods: upgradedMapping.rpc_methods.map((rpcMethod: RpcMethod) => ({
method: rpcMethod.method,
arguments: rpcMethod.arguments.map((arg: RpcArgument) => arg.value)
}))
}));
}
private static getTypeSourceByValue(value: string): OPCUaSourceType {
if (value.includes('${')) {
return OPCUaSourceType.IDENTIFIER;
}
if (value.includes(`/`) || value.includes('\\')) {
return OPCUaSourceType.PATH;
}
return OPCUaSourceType.CONST;
}
private static getArgumentType(arg: unknown): string {
switch (typeof arg) {
case 'boolean':
return 'boolean';
case 'number':
return Number.isInteger(arg) ? 'integer' : 'float';
default:
return 'string';
}
}
}

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

@ -113,22 +113,22 @@ import { GatewayHelpLinkPipe } from '@home/components/widget/lib/gateway/pipes/g
import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list.directive';
import {
BrokerConfigControlComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/broker-config-control/broker-config-control.component';
} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component';
import {
WorkersConfigControlComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component';
} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component';
import {
OpcServerConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component';
} from '@home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component';
import {
MqttBasicConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt-basic-config/mqtt-basic-config.component';
} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component';
import {
MappingTableComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component';
import {
OpcUaBasicConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component';
} from '@home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component';
import {
ModbusBasicConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component';
@ -145,12 +145,21 @@ import {
ModbusRpcParametersComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-rpc-parameters/modbus-rpc-parameters.component';
import { ScadaSymbolWidgetComponent } from '@home/components/widget/lib/scada/scada-symbol-widget.component';
import {
MqttLegacyBasicConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-legacy-basic-config.component';
import {
GatewayBasicConfigurationComponent
} from '@home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component';
import {
GatewayAdvancedConfigurationComponent
} from '@home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component';
import {
OpcUaLegacyBasicConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-legacy-basic-config.component';
import {
ModbusLegacyBasicConfigComponent
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component';
@NgModule({
declarations: [
@ -239,8 +248,11 @@ import {
ModbusBasicConfigComponent,
EllipsisChipListDirective,
ModbusRpcParametersComponent,
MqttLegacyBasicConfigComponent,
GatewayBasicConfigurationComponent,
GatewayAdvancedConfigurationComponent,
OpcUaLegacyBasicConfigComponent,
ModbusLegacyBasicConfigComponent,
],
exports: [
EntitiesTableWidgetComponent,

698
ui-ngx/src/assets/metadata/connector-default-configs/modbus.json

@ -1,246 +1,474 @@
{
"master": {
"slaves": [
{
"name": "Slave 1",
"host": "127.0.0.1",
"port": 5021,
"type": "tcp",
"method": "socket",
"timeout": 35,
"byteOrder": "LITTLE",
"wordOrder": "LITTLE",
"retries": true,
"retryOnEmpty": true,
"retryOnInvalid": true,
"pollPeriod": 5000,
"unitId": 1,
"deviceName": "Temp Sensor",
"deviceType": "default",
"sendDataOnlyOnChange": true,
"connectAttemptTimeMs": 5000,
"connectAttemptCount": 5,
"waitAfterFailedAttemptsMs": 300000,
"attributes": [
{
"tag": "string_read",
"type": "string",
"functionCode": 4,
"objectsCount": 4,
"address": 1
},
{
"tag": "bits_read",
"type": "bits",
"functionCode": 4,
"objectsCount": 1,
"address": 5
},
{
"tag": "8int_read",
"type": "8int",
"functionCode": 4,
"objectsCount": 1,
"address": 6
},
{
"tag": "16int_read",
"type": "16int",
"functionCode": 4,
"objectsCount": 1,
"address": 7
},
{
"tag": "32int_read_divider",
"type": "32int",
"functionCode": 4,
"objectsCount": 2,
"address": 8,
"divider": 10
},
{
"tag": "8int_read_multiplier",
"type": "8int",
"functionCode": 4,
"objectsCount": 1,
"address": 10,
"multiplier": 10
},
{
"tag": "32int_read",
"type": "32int",
"functionCode": 4,
"objectsCount": 2,
"address": 11
},
{
"tag": "64int_read",
"type": "64int",
"functionCode": 4,
"objectsCount": 4,
"address": 13
}
],
"timeseries": [
{
"tag": "8uint_read",
"type": "8uint",
"functionCode": 4,
"objectsCount": 1,
"address": 17
},
{
"tag": "16uint_read",
"type": "16uint",
"functionCode": 4,
"objectsCount": 2,
"address": 18
},
{
"tag": "32uint_read",
"type": "32uint",
"functionCode": 4,
"objectsCount": 4,
"address": 20
},
{
"tag": "64uint_read",
"type": "64uint",
"functionCode": 4,
"objectsCount": 1,
"address": 24
},
{
"tag": "16float_read",
"type": "16float",
"functionCode": 4,
"objectsCount": 1,
"address": 25
},
{
"tag": "32float_read",
"type": "32float",
"functionCode": 4,
"objectsCount": 2,
"address": 26
},
{
"tag": "64float_read",
"type": "64float",
"functionCode": 4,
"objectsCount": 4,
"address": 28
}
],
"attributeUpdates": [
{
"tag": "shared_attribute_write",
"type": "32int",
"functionCode": 6,
"objectsCount": 2,
"address": 29
}
],
"rpc": [
{
"tag": "setValue",
"type": "bits",
"functionCode": 5,
"objectsCount": 1,
"address": 31
},
{
"tag": "getValue",
"type": "bits",
"functionCode": 1,
"objectsCount": 1,
"address": 31
},
{
"tag": "setCPUFanSpeed",
"type": "32int",
"functionCode": 16,
"objectsCount": 2,
"address": 33
},
{
"tag": "getCPULoad",
"type": "32int",
"functionCode": 4,
"objectsCount": 2,
"address": 35
}
]
}
]
},
"slave": {
"type": "tcp",
"host": "127.0.0.1",
"port": 5026,
"method": "socket",
"deviceName": "Modbus Slave Example",
"deviceType": "default",
"pollPeriod": 5000,
"sendDataToThingsBoard": false,
"byteOrder": "LITTLE",
"wordOrder": "LITTLE",
"unitId": 0,
"values": {
"holding_registers": {
"attributes": [
"3.5.2": {
"master": {
"slaves": [
{
"address": 1,
"type": "string",
"tag": "sm",
"objectsCount": 1,
"value": "ON"
"name": "Slave 1",
"host": "127.0.0.1",
"port": 5021,
"type": "tcp",
"method": "socket",
"timeout": 35,
"byteOrder": "LITTLE",
"wordOrder": "LITTLE",
"retries": true,
"retryOnEmpty": true,
"retryOnInvalid": true,
"pollPeriod": 5000,
"unitId": 1,
"deviceName": "Temp Sensor",
"deviceType": "default",
"sendDataOnlyOnChange": true,
"connectAttemptTimeMs": 5000,
"connectAttemptCount": 5,
"waitAfterFailedAttemptsMs": 300000,
"attributes": [
{
"tag": "string_read",
"type": "string",
"functionCode": 4,
"objectsCount": 4,
"address": 1
},
{
"tag": "bits_read",
"type": "bits",
"functionCode": 4,
"objectsCount": 1,
"address": 5
},
{
"tag": "8int_read",
"type": "8int",
"functionCode": 4,
"objectsCount": 1,
"address": 6
},
{
"tag": "16int_read",
"type": "16int",
"functionCode": 4,
"objectsCount": 1,
"address": 7
},
{
"tag": "32int_read_divider",
"type": "32int",
"functionCode": 4,
"objectsCount": 2,
"address": 8,
"divider": 10
},
{
"tag": "8int_read_multiplier",
"type": "8int",
"functionCode": 4,
"objectsCount": 1,
"address": 10,
"multiplier": 10
},
{
"tag": "32int_read",
"type": "32int",
"functionCode": 4,
"objectsCount": 2,
"address": 11
},
{
"tag": "64int_read",
"type": "64int",
"functionCode": 4,
"objectsCount": 4,
"address": 13
}
],
"timeseries": [
{
"tag": "8uint_read",
"type": "8uint",
"functionCode": 4,
"objectsCount": 1,
"address": 17
},
{
"tag": "16uint_read",
"type": "16uint",
"functionCode": 4,
"objectsCount": 2,
"address": 18
},
{
"tag": "32uint_read",
"type": "32uint",
"functionCode": 4,
"objectsCount": 4,
"address": 20
},
{
"tag": "64uint_read",
"type": "64uint",
"functionCode": 4,
"objectsCount": 1,
"address": 24
},
{
"tag": "16float_read",
"type": "16float",
"functionCode": 4,
"objectsCount": 1,
"address": 25
},
{
"tag": "32float_read",
"type": "32float",
"functionCode": 4,
"objectsCount": 2,
"address": 26
},
{
"tag": "64float_read",
"type": "64float",
"functionCode": 4,
"objectsCount": 4,
"address": 28
}
],
"attributeUpdates": [
{
"tag": "shared_attribute_write",
"type": "32int",
"functionCode": 6,
"objectsCount": 2,
"address": 29
}
],
"rpc": [
{
"tag": "setValue",
"type": "bits",
"functionCode": 5,
"objectsCount": 1,
"address": 31
},
{
"tag": "getValue",
"type": "bits",
"functionCode": 1,
"objectsCount": 1,
"address": 31
},
{
"tag": "setCPUFanSpeed",
"type": "32int",
"functionCode": 16,
"objectsCount": 2,
"address": 33
},
{
"tag": "getCPULoad",
"type": "32int",
"functionCode": 4,
"objectsCount": 2,
"address": 35
}
]
}
],
"timeseries": [
{
"address": 2,
"type": "8int",
"tag": "smm",
"objectsCount": 1,
"value": "12334"
}
],
"attributeUpdates": [
{
"tag": "shared_attribute_write",
"type": "32int",
"functionCode": 6,
"objectsCount": 2,
"address": 29,
"value": 1243
]
},
"slave": {
"type": "tcp",
"host": "127.0.0.1",
"port": 5026,
"method": "socket",
"deviceName": "Modbus Slave Example",
"deviceType": "default",
"pollPeriod": 5000,
"sendDataToThingsBoard": false,
"byteOrder": "LITTLE",
"wordOrder": "LITTLE",
"unitId": 0,
"values": {
"holding_registers": {
"attributes": [
{
"address": 1,
"type": "string",
"tag": "sm",
"objectsCount": 1,
"value": "ON"
}
],
"timeseries": [
{
"address": 2,
"type": "8int",
"tag": "smm",
"objectsCount": 1,
"value": "12334"
}
],
"attributeUpdates": [
{
"tag": "shared_attribute_write",
"type": "32int",
"functionCode": 6,
"objectsCount": 2,
"address": 29,
"value": 1243
}
],
"rpc": [
{
"tag": "setValue",
"type": "bits",
"functionCode": 5,
"objectsCount": 1,
"address": 31,
"value": 22
}
]
},
"coils_initializer": {
"attributes": [
{
"address": 5,
"type": "string",
"tag": "sm",
"objectsCount": 1,
"value": "12"
}
],
"timeseries": [],
"attributeUpdates": [],
"rpc": []
}
],
"rpc": [
}
}
},
"legacy": {
"master": {
"slaves": [
{
"tag": "setValue",
"type": "bits",
"functionCode": 5,
"objectsCount": 1,
"address": 31,
"value": 22
"host": "127.0.0.1",
"port": 5021,
"type": "tcp",
"method": "socket",
"timeout": 35,
"byteOrder": "LITTLE",
"wordOrder": "LITTLE",
"retries": true,
"retryOnEmpty": true,
"retryOnInvalid": true,
"pollPeriod": 5000,
"unitId": 1,
"deviceName": "Temp Sensor",
"sendDataOnlyOnChange": true,
"connectAttemptTimeMs": 5000,
"connectAttemptCount": 5,
"waitAfterFailedAttemptsMs": 300000,
"attributes": [
{
"tag": "string_read",
"type": "string",
"functionCode": 4,
"objectsCount": 4,
"address": 1
},
{
"tag": "bits_read",
"type": "bits",
"functionCode": 4,
"objectsCount": 1,
"address": 5
},
{
"tag": "16int_read",
"type": "16int",
"functionCode": 4,
"objectsCount": 1,
"address": 7
},
{
"tag": "32int_read_divider",
"type": "32int",
"functionCode": 4,
"objectsCount": 2,
"address": 8,
"divider": 10
},
{
"tag": "32int_read",
"type": "32int",
"functionCode": 4,
"objectsCount": 2,
"address": 11
},
{
"tag": "64int_read",
"type": "64int",
"functionCode": 4,
"objectsCount": 4,
"address": 13
}
],
"timeseries": [
{
"tag": "16uint_read",
"type": "16uint",
"functionCode": 4,
"objectsCount": 2,
"address": 18
},
{
"tag": "32uint_read",
"type": "32uint",
"functionCode": 4,
"objectsCount": 4,
"address": 20
},
{
"tag": "64uint_read",
"type": "64uint",
"functionCode": 4,
"objectsCount": 1,
"address": 24
},
{
"tag": "16float_read",
"type": "16float",
"functionCode": 4,
"objectsCount": 1,
"address": 25
},
{
"tag": "32float_read",
"type": "32float",
"functionCode": 4,
"objectsCount": 2,
"address": 26
},
{
"tag": "64float_read",
"type": "64float",
"functionCode": 4,
"objectsCount": 4,
"address": 28
}
],
"attributeUpdates": [
{
"tag": "shared_attribute_write",
"type": "32int",
"functionCode": 6,
"objectsCount": 2,
"address": 29
}
],
"rpc": [
{
"tag": "setValue",
"type": "bits",
"functionCode": 5,
"objectsCount": 1,
"address": 31
},
{
"tag": "getValue",
"type": "bits",
"functionCode": 1,
"objectsCount": 1,
"address": 31
},
{
"tag": "setCPUFanSpeed",
"type": "32int",
"functionCode": 16,
"objectsCount": 2,
"address": 33
},
{
"tag": "getCPULoad",
"type": "32int",
"functionCode": 4,
"objectsCount": 2,
"address": 35
}
]
}
]
},
"coils_initializer": {
"attributes": [
{
"address": 5,
"type": "string",
"tag": "sm",
"objectsCount": 1,
"value": "12"
}
],
"timeseries": [],
"attributeUpdates": [],
"rpc": []
"slave": {
"type": "tcp",
"host": "127.0.0.1",
"port": 5026,
"method": "socket",
"deviceName": "Modbus Slave Example",
"deviceType": "default",
"pollPeriod": 5000,
"sendDataToThingsBoard": false,
"byteOrder": "LITTLE",
"wordOrder": "LITTLE",
"unitId": 0,
"values": {
"holding_registers": [
{
"attributes": [
{
"address": 1,
"type": "string",
"tag": "sm",
"objectsCount": 1,
"value": "ON"
}
],
"timeseries": [
{
"address": 2,
"type": "int",
"tag": "smm",
"objectsCount": 1,
"value": "12334"
}
],
"attributeUpdates": [
{
"tag": "shared_attribute_write",
"type": "32int",
"functionCode": 6,
"objectsCount": 2,
"address": 29,
"value": 1243
}
],
"rpc": [
{
"tag": "setValue",
"type": "bits",
"functionCode": 5,
"objectsCount": 1,
"address": 31,
"value": 22
}
]
}
],
"coils_initializer": [
{
"attributes": [
{
"address": 5,
"type": "string",
"tag": "sm",
"objectsCount": 1,
"value": "12"
}
],
"timeseries": [],
"attributeUpdates": [],
"rpc": []
}
]
}
}
}
}
}

595
ui-ngx/src/assets/metadata/connector-default-configs/mqtt.json

@ -1,217 +1,398 @@
{
"broker": {
"host": "127.0.0.1",
"port": 1883,
"clientId": "ThingsBoard_gateway",
"version": 5,
"maxMessageNumberPerWorker": 10,
"maxNumberOfWorkers": 100,
"sendDataOnlyOnChange": false,
"security": {
"type": "anonymous"
}
},
"dataMapping": [
{
"topicFilter": "sensor/data",
"subscriptionQos": 1,
"converter": {
"type": "json",
"deviceInfo": {
"deviceNameExpressionSource": "message",
"deviceNameExpression": "${serialNumber}",
"deviceProfileExpressionSource": "message",
"deviceProfileExpression": "${sensorType}"
"3.5.2": {
"broker": {
"host": "127.0.0.1",
"port": 1883,
"clientId": "ThingsBoard_gateway",
"version": 5,
"maxMessageNumberPerWorker": 10,
"maxNumberOfWorkers": 100,
"sendDataOnlyOnChange": false,
"security": {
"type": "anonymous"
}
},
"sendDataOnlyOnChange": false,
"timeout": 60000,
"attributes": [
{
"type": "string",
"key": "model",
"value": "${sensorModel}"
},
{
"type": "string",
"key": "${sensorModel}",
"value": "on"
}
"mapping": [
{
"topicFilter": "sensor/data",
"subscriptionQos": 1,
"converter": {
"type": "json",
"deviceInfo": {
"deviceNameExpressionSource": "message",
"deviceNameExpression": "${serialNumber}",
"deviceProfileExpressionSource": "message",
"deviceProfileExpression": "${sensorType}"
},
"sendDataOnlyOnChange": false,
"timeout": 60000,
"attributes": [
{
"type": "string",
"key": "model",
"value": "${sensorModel}"
},
{
"type": "string",
"key": "${sensorModel}",
"value": "on"
}
],
"timeseries": [
{
"type": "string",
"key": "temperature",
"value": "${temp}"
},
{
"type": "double",
"key": "humidity",
"value": "${hum}"
},
{
"type": "string",
"key": "combine",
"value": "${hum}:${temp}"
}
]
}
},
{
"topicFilter": "sensor/+/data",
"subscriptionQos": 1,
"converter": {
"type": "json",
"deviceInfo": {
"deviceNameExpressionSource": "topic",
"deviceNameExpression": "(?<=sensor\/)(.*?)(?=\/data)",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "Thermometer"
},
"sendDataOnlyOnChange": false,
"timeout": 60000,
"attributes": [
{
"type": "string",
"key": "model",
"value": "${sensorModel}"
}
],
"timeseries": [
{
"type": "double",
"key": "temperature",
"value": "${temp}"
},
{
"type": "string",
"key": "humidity",
"value": "${hum}"
}
]
}
},
{
"topicFilter": "sensor/raw_data",
"subscriptionQos": 1,
"converter": {
"type": "bytes",
"deviceInfo": {
"deviceNameExpressionSource": "message",
"deviceNameExpression": "[0:4]",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "default"
},
"sendDataOnlyOnChange": false,
"timeout": 60000,
"attributes": [
{
"type": "raw",
"key": "rawData",
"value": "[:]"
}
],
"timeseries": [
{
"type": "raw",
"key": "temp",
"value": "[4:]"
}
]
}
},
{
"topicFilter": "custom/sensors/+",
"subscriptionQos": 1,
"converter": {
"type": "custom",
"extension": "CustomMqttUplinkConverter",
"cached": true,
"extensionConfig": {
"temperature": 2,
"humidity": 2,
"batteryLevel": 1
}
}
}
],
"timeseries": [
{
"type": "string",
"key": "temperature",
"value": "${temp}"
},
{
"type": "double",
"key": "humidity",
"value": "${hum}"
},
{
"type": "string",
"key": "combine",
"value": "${hum}:${temp}"
}
]
}
"requestsMapping": {
"connectRequests": [
{
"topicFilter": "sensor/connect",
"deviceInfo": {
"deviceNameExpressionSource": "message",
"deviceNameExpression": "${serialNumber}",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "Thermometer"
}
},
{
"topicFilter": "sensor/+/connect",
"deviceInfo": {
"deviceNameExpressionSource": "topic",
"deviceNameExpression": "(?<=sensor\/)(.*?)(?=\/connect)",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "Thermometer"
}
}
],
"disconnectRequests": [
{
"topicFilter": "sensor/disconnect",
"deviceInfo": {
"deviceNameExpressionSource": "message",
"deviceNameExpression": "${serialNumber}"
}
},
{
"topicFilter": "sensor/+/disconnect",
"deviceInfo": {
"deviceNameExpressionSource": "topic",
"deviceNameExpression": "(?<=sensor\/)(.*?)(?=\/connect)"
}
}
],
"attributeRequests": [
{
"retain": false,
"topicFilter": "v1/devices/me/attributes/request",
"deviceInfo": {
"deviceNameExpressionSource": "message",
"deviceNameExpression": "${serialNumber}"
},
"attributeNameExpressionSource": "message",
"attributeNameExpression": "${versionAttribute}, ${pduAttribute}",
"topicExpression": "devices/${deviceName}/attrs",
"valueExpression": "${attributeKey}: ${attributeValue}"
}
],
"attributeUpdates": [
{
"retain": true,
"deviceNameFilter": ".*",
"attributeFilter": "firmwareVersion",
"topicExpression": "sensor/${deviceName}/${attributeKey}",
"valueExpression": "{\"${attributeKey}\":\"${attributeValue}\"}"
}
],
"serverSideRpc": [
{
"type": "twoWay",
"deviceNameFilter": ".*",
"methodFilter": "echo",
"requestTopicExpression": "sensor/${deviceName}/request/${methodName}/${requestId}",
"responseTopicExpression": "sensor/${deviceName}/response/${methodName}/${requestId}",
"responseTopicQoS": 1,
"responseTimeout": 10000,
"valueExpression": "${params}"
},
{
"type": "oneWay",
"deviceNameFilter": ".*",
"methodFilter": "no-reply",
"requestTopicExpression": "sensor/${deviceName}/request/${methodName}/${requestId}",
"valueExpression": "${params}"
}
]
}
},
{
"topicFilter": "sensor/+/data",
"subscriptionQos": 1,
"converter": {
"type": "json",
"deviceInfo": {
"deviceNameExpressionSource": "topic",
"deviceNameExpression": "(?<=sensor\/)(.*?)(?=\/data)",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "Thermometer"
"legacy": {
"broker": {
"name": "Default Local Broker",
"host": "127.0.0.1",
"port": 1883,
"clientId": "ThingsBoard_gateway",
"version": 5,
"maxMessageNumberPerWorker": 10,
"maxNumberOfWorkers": 100,
"sendDataOnlyOnChange": false,
"security": {
"type": "basic",
"username": "user",
"password": "password"
}
},
"sendDataOnlyOnChange": false,
"timeout": 60000,
"attributes": [
{
"type": "string",
"key": "model",
"value": "${sensorModel}"
}
"mapping": [
{
"topicFilter": "sensor/data",
"converter": {
"type": "json",
"deviceNameJsonExpression": "${serialNumber}",
"deviceTypeJsonExpression": "${sensorType}",
"sendDataOnlyOnChange": false,
"timeout": 60000,
"attributes": [
{
"type": "string",
"key": "model",
"value": "${sensorModel}"
},
{
"type": "string",
"key": "${sensorModel}",
"value": "on"
}
],
"timeseries": [
{
"type": "double",
"key": "temperature",
"value": "${temp}"
},
{
"type": "double",
"key": "humidity",
"value": "${hum}"
},
{
"type": "string",
"key": "combine",
"value": "${hum}:${temp}"
}
]
}
},
{
"topicFilter": "sensor/+/data",
"converter": {
"type": "json",
"deviceNameTopicExpression": "(?<=sensor\/)(.*?)(?=\/data)",
"deviceTypeTopicExpression": "Thermometer",
"sendDataOnlyOnChange": false,
"timeout": 60000,
"attributes": [
{
"type": "string",
"key": "model",
"value": "${sensorModel}"
}
],
"timeseries": [
{
"type": "double",
"key": "temperature",
"value": "${temp}"
},
{
"type": "double",
"key": "humidity",
"value": "${hum}"
}
]
}
},
{
"topicFilter": "sensor/raw_data",
"converter": {
"type": "bytes",
"deviceNameExpression": "[0:4]",
"deviceTypeExpression": "default",
"sendDataOnlyOnChange": false,
"timeout": 60000,
"attributes": [
{
"type": "raw",
"key": "rawData",
"value": "[:]"
}
],
"timeseries": [
{
"type": "raw",
"key": "temp",
"value": "[4:]"
}
]
}
},
{
"topicFilter": "custom/sensors/+",
"converter": {
"type": "custom",
"extension": "CustomMqttUplinkConverter",
"cached": true,
"extension-config": {
"temperatureBytes": 2,
"humidityBytes": 2,
"batteryLevelBytes": 1
}
}
}
],
"timeseries": [
{
"type": "double",
"key": "temperature",
"value": "${temp}"
},
{
"type": "string",
"key": "humidity",
"value": "${hum}"
}
]
}
},
{
"topicFilter": "sensor/raw_data",
"subscriptionQos": 1,
"converter": {
"type": "bytes",
"deviceInfo": {
"deviceNameExpressionSource": "message",
"deviceNameExpression": "[0:4]",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "default"
},
"sendDataOnlyOnChange": false,
"timeout": 60000,
"attributes": [
{
"type": "raw",
"key": "rawData",
"value": "[:]"
}
"connectRequests": [
{
"topicFilter": "sensor/connect",
"deviceNameJsonExpression": "${serialNumber}"
},
{
"topicFilter": "sensor/+/connect",
"deviceNameTopicExpression": "(?<=sensor\/)(.*?)(?=\/connect)"
}
],
"disconnectRequests": [
{
"topicFilter": "sensor/disconnect",
"deviceNameJsonExpression": "${serialNumber}"
},
{
"topicFilter": "sensor/+/disconnect",
"deviceNameTopicExpression": "(?<=sensor\/)(.*?)(?=\/disconnect)"
}
],
"attributeRequests": [
{
"retain": false,
"topicFilter": "v1/devices/me/attributes/request",
"deviceNameJsonExpression": "${serialNumber}",
"attributeNameJsonExpression": "${versionAttribute}, ${pduAttribute}",
"topicExpression": "devices/${deviceName}/attrs",
"valueExpression": "${attributeKey}: ${attributeValue}"
}
],
"timeseries": [
{
"type": "raw",
"key": "temp",
"value": "[4:]"
}
"attributeUpdates": [
{
"retain": true,
"deviceNameFilter": ".*",
"attributeFilter": "firmwareVersion",
"topicExpression": "sensor/${deviceName}/${attributeKey}",
"valueExpression": "{\"${attributeKey}\":\"${attributeValue}\"}"
}
],
"serverSideRpc": [
{
"deviceNameFilter": ".*",
"methodFilter": "echo",
"requestTopicExpression": "sensor/${deviceName}/request/${methodName}/${requestId}",
"responseTopicExpression": "sensor/${deviceName}/response/${methodName}/${requestId}",
"responseTimeout": 10000,
"valueExpression": "${params}"
},
{
"deviceNameFilter": ".*",
"methodFilter": "no-reply",
"requestTopicExpression": "sensor/${deviceName}/request/${methodName}/${requestId}",
"valueExpression": "${params}"
}
]
}
},
{
"topicFilter": "custom/sensors/+",
"subscriptionQos": 1,
"converter": {
"type": "custom",
"extension": "CustomMqttUplinkConverter",
"cached": true,
"extensionConfig": {
"temperature": 2,
"humidity": 2,
"batteryLevel": 1
}
}
}
],
"requestsMapping": {
"connectRequests": [
{
"topicFilter": "sensor/connect",
"deviceInfo": {
"deviceNameExpressionSource": "message",
"deviceNameExpression": "${serialNumber}",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "Thermometer"
}
},
{
"topicFilter": "sensor/+/connect",
"deviceInfo": {
"deviceNameExpressionSource": "topic",
"deviceNameExpression": "(?<=sensor\/)(.*?)(?=\/connect)",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "Thermometer"
}
}
],
"disconnectRequests": [
{
"topicFilter": "sensor/disconnect",
"deviceInfo": {
"deviceNameExpressionSource": "message",
"deviceNameExpression": "${serialNumber}"
}
},
{
"topicFilter": "sensor/+/disconnect",
"deviceInfo": {
"deviceNameExpressionSource": "topic",
"deviceNameExpression": "(?<=sensor\/)(.*?)(?=\/connect)"
}
}
],
"attributeRequests": [
{
"retain": false,
"topicFilter": "v1/devices/me/attributes/request",
"deviceInfo": {
"deviceNameExpressionSource": "message",
"deviceNameExpression": "${serialNumber}"
},
"attributeNameExpressionSource": "message",
"attributeNameExpression": "${versionAttribute}, ${pduAttribute}",
"topicExpression": "devices/${deviceName}/attrs",
"valueExpression": "${attributeKey}: ${attributeValue}"
}
],
"attributeUpdates": [
{
"retain": true,
"deviceNameFilter": ".*",
"attributeFilter": "firmwareVersion",
"topicExpression": "sensor/${deviceName}/${attributeKey}",
"valueExpression": "{\"${attributeKey}\":\"${attributeValue}\"}"
}
],
"serverSideRpc": [
{
"type": "twoWay",
"deviceNameFilter": ".*",
"methodFilter": "echo",
"requestTopicExpression": "sensor/${deviceName}/request/${methodName}/${requestId}",
"responseTopicExpression": "sensor/${deviceName}/response/${methodName}/${requestId}",
"responseTopicQoS": 1,
"responseTimeout": 10000,
"valueExpression": "${params}"
},
{
"type": "oneWay",
"deviceNameFilter": ".*",
"methodFilter": "no-reply",
"requestTopicExpression": "sensor/${deviceName}/request/${methodName}/${requestId}",
"valueExpression": "${params}"
}
]
}
}

180
ui-ngx/src/assets/metadata/connector-default-configs/opcua.json

@ -1,66 +1,120 @@
{
"server": {
"url": "localhost:4840/freeopcua/server/",
"timeoutInMillis": 5000,
"scanPeriodInMillis": 3600000,
"pollPeriodInMillis": 5000,
"enableSubscriptions": true,
"subCheckPeriodInMillis": 100,
"showMap": false,
"security": "Basic128Rsa15",
"identity": {
"type": "anonymous"
}
},
"mapping": [{
"deviceNodePattern": "Root\\.Objects\\.Device1",
"deviceNodeSource": "path",
"deviceInfo": {
"deviceNameExpression": "Device ${Root\\.Objects\\.Device1\\.serialNumber}",
"deviceNameExpressionSource": "path",
"deviceProfileExpression": "Device",
"deviceProfileExpressionSource": "constant"
"3.5.2": {
"server": {
"url": "localhost:4840/freeopcua/server/",
"timeoutInMillis": 5000,
"scanPeriodInMillis": 3600000,
"pollPeriodInMillis": 5000,
"enableSubscriptions": true,
"subCheckPeriodInMillis": 100,
"showMap": false,
"security": "Basic128Rsa15",
"identity": {
"type": "anonymous"
}
},
"mapping": [{
"deviceNodePattern": "Root\\.Objects\\.Device1",
"deviceNodeSource": "path",
"deviceInfo": {
"deviceNameExpression": "Device ${Root\\.Objects\\.Device1\\.serialNumber}",
"deviceNameExpressionSource": "path",
"deviceProfileExpression": "Device",
"deviceProfileExpressionSource": "constant"
},
"attributes": [
{
"key": "temperature °C",
"type": "path",
"value": "${ns=2;i=5}"
}
],
"timeseries": [
{
"key": "humidity",
"type": "path",
"value": "${Root\\.Objects\\.Device1\\.TemperatureAndHumiditySensor\\.Humidity}"
},
{
"key": "batteryLevel",
"type": "path",
"value": "${Battery\\.batteryLevel}"
}
],
"rpc_methods": [
{
"method": "multiply",
"arguments": [
{
"type": "integer",
"value": 2
},
{
"type": "integer",
"value": 4
}
]
}
],
"attributes_updates": [
{
"key": "deviceName",
"type": "path",
"value": "Root\\.Objects\\.Device1\\.serialNumber"
}
]
}]
},
"attributes": [
{
"key": "temperature °C",
"type": "path",
"value": "${ns=2;i=5}"
}
],
"timeseries": [
{
"key": "humidity",
"type": "path",
"value": "${Root\\.Objects\\.Device1\\.TemperatureAndHumiditySensor\\.Humidity}"
},
{
"key": "batteryLevel",
"type": "path",
"value": "${Battery\\.batteryLevel}"
}
],
"rpc_methods": [
{
"method": "multiply",
"arguments": [
{
"type": "integer",
"value": 2
},
{
"type": "integer",
"value": 4
}
]
}
],
"attributes_updates": [
{
"key": "deviceName",
"type": "path",
"value": "Root\\.Objects\\.Device1\\.serialNumber"
}
]
}]
"legacy": {
"server": {
"name": "OPC-UA Default Server",
"url": "localhost:4840/freeopcua/server/",
"timeoutInMillis": 5000,
"scanPeriodInMillis": 5000,
"disableSubscriptions": false,
"subCheckPeriodInMillis": 100,
"showMap": false,
"security": "Basic128Rsa15",
"identity": {
"type": "anonymous"
},
"mapping": [
{
"deviceNodePattern": "Root\\.Objects\\.Device1",
"deviceNamePattern": "Device ${Root\\.Objects\\.Device1\\.serialNumber}",
"attributes": [
{
"key": "temperature °C",
"path": "${ns=2;i=5}"
}
],
"timeseries": [
{
"key": "humidity",
"path": "${Root\\.Objects\\.Device1\\.TemperatureAndHumiditySensor\\.Humidity}"
},
{
"key": "batteryLevel",
"path": "${Battery\\.batteryLevel}"
}
],
"rpc_methods": [
{
"method": "multiply",
"arguments": [
2,
4
]
}
],
"attributes_updates": [
{
"attributeOnThingsBoard": "deviceName",
"attributeOnDevice": "Root\\.Objects\\.Device1\\.serialNumber"
}
]
}
]
}
}
}

Loading…
Cancel
Save