From bbbc6fd558fc1f91775e4e85b63240b9fbd3a040 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 16 Sep 2024 17:36:11 +0300 Subject: [PATCH] Added posibility to configure modifier type for timeseries and attributes of Modbus Connector slaves --- .../modbus-data-keys-panel.component.html | 56 ++++++++++++++ .../modbus-data-keys-panel.component.ts | 74 ++++++++++++++++++- .../lib/gateway/gateway-widget.models.ts | 27 +++++++ .../assets/locale/locale.constant-en_US.json | 7 +- 4 files changed, 159 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.html index fc94fe49ea..e84158c91b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.html @@ -148,6 +148,62 @@ +
+ + + + + + {{ 'gateway.modifier' | translate }} + + + + +
+
+
gateway.type
+
+ + + +
+ + {{ ModifierTypesMap.get(keyControl.get('modifierType').value)?.name | translate}} +
+
+ + + + {{ ModifierTypesMap.get(modifierType).name | translate }} + +
+
+
+
+
+
+
gateway.value
+ + + + warning + + +
+
+
gateway.value
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.ts index 3c61d351b7..094ef24dcb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.ts @@ -18,6 +18,7 @@ import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angu import { AbstractControl, FormArray, + FormControl, FormGroup, UntypedFormArray, UntypedFormBuilder, @@ -32,7 +33,10 @@ import { ModbusObjectCountByDataType, ModbusValue, ModbusValueKey, + ModifierType, + ModifierTypesMap, noLeadTrailSpacesRegex, + nonZeroFloat, } from '@home/components/widget/lib/gateway/gateway-widget.models'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@shared/shared.module'; @@ -69,12 +73,17 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy { keysListFormArray: FormArray; modbusDataTypes = Object.values(ModbusDataType); + modifierTypes: ModifierType[] = Object.values(ModifierType); withFunctionCode = true; + + enableModifiersControlMap = new Map>(); + showModifiersMap = new Map(); functionCodesMap = new Map(); defaultFunctionCodes = []; readonly ModbusEditableDataTypes = ModbusEditableDataTypes; readonly ModbusFunctionCodeTranslationsMap = ModbusFunctionCodeTranslationsMap; + readonly ModifierTypesMap = ModifierTypesMap; private destroy$ = new Subject(); @@ -101,6 +110,7 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy { } addKey(): void { + const id = generateSecret(5); const dataKeyFormGroup = this.fb.group({ tag: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], value: [{value: '', disabled: !this.isMaster}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], @@ -108,9 +118,14 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy { address: [null, [Validators.required]], objectsCount: [1, [Validators.required]], functionCode: [{ value: this.getDefaultFunctionCodes()[0], disabled: !this.withFunctionCode }, [Validators.required]], - id: [{value: generateSecret(5), disabled: true}], + modifierType: [{ value: ModifierType.MULTIPLIER, disabled: true }], + modifierValue: [{ value: 1, disabled: true }, [Validators.pattern(nonZeroFloat)]], + id: [{value: id, disabled: true}], }); + this.showModifiersMap.set(id, false); + this.enableModifiersControlMap.set(id, this.fb.control(false)); this.observeKeyDataType(dataKeyFormGroup); + this.observeEnableModifier(dataKeyFormGroup); this.keysListFormArray.push(dataKeyFormGroup); } @@ -128,7 +143,17 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy { } applyKeysData(): void { - this.keysDataApplied.emit(this.keysListFormArray.value); + this.keysDataApplied.emit(this.mapKeysWithModifier()); + } + + private mapKeysWithModifier(): Array { + return this.keysListFormArray.value.map((keyData, i) => { + if (this.showModifiersMap.get(this.keysListFormArray.controls[i].get('id').value)) { + const { modifierType, modifierValue, ...value } = keyData; + return modifierType ? { ...value, [modifierType]: modifierValue } : value; + } + return keyData; + }); } private prepareKeysFormArray(values: ModbusValue[]): UntypedFormArray { @@ -138,6 +163,7 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy { values.forEach(value => { const dataKeyFormGroup = this.createDataKeyFormGroup(value); this.observeKeyDataType(dataKeyFormGroup); + this.observeEnableModifier(dataKeyFormGroup); this.functionCodesMap.set(dataKeyFormGroup.get('id').value, this.getFunctionCodes(value.type)); keysControlGroups.push(dataKeyFormGroup); @@ -148,7 +174,12 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy { } private createDataKeyFormGroup(modbusValue: ModbusValue): FormGroup { - const { tag, value, type, address, objectsCount, functionCode } = modbusValue; + const { tag, value, type, address, objectsCount, functionCode, multiplier, divider } = modbusValue; + const id = generateSecret(5); + + const showModifier = this.shouldShowModifier(type); + this.showModifiersMap.set(id, showModifier); + this.enableModifiersControlMap.set(id, this.fb.control((multiplier || divider) && showModifier)); return this.fb.group({ tag: [tag, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], @@ -157,19 +188,54 @@ export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy { address: [address, [Validators.required]], objectsCount: [objectsCount, [Validators.required]], functionCode: [{ value: functionCode, disabled: !this.withFunctionCode }, [Validators.required]], - id: [{ value: generateSecret(5), disabled: true }], + modifierType: [{ + value: divider ? ModifierType.DIVIDER : ModifierType.MULTIPLIER, + disabled: !this.enableModifiersControlMap.get(id).value + }], + modifierValue: [ + { value: multiplier ?? divider ?? 1, disabled: !this.enableModifiersControlMap.get(id).value }, + [Validators.pattern(nonZeroFloat)] + ], + id: [{ value: id, disabled: true }], }); } + private shouldShowModifier(type: ModbusDataType): boolean { + return !this.isMaster + && (this.keysType === ModbusValueKey.ATTRIBUTES || this.keysType === ModbusValueKey.TIMESERIES) + && (!this.ModbusEditableDataTypes.includes(type)); + } + private observeKeyDataType(keyFormGroup: FormGroup): void { keyFormGroup.get('type').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(dataType => { if (!this.ModbusEditableDataTypes.includes(dataType)) { keyFormGroup.get('objectsCount').patchValue(ModbusObjectCountByDataType[dataType], {emitEvent: false}); } + const withModifier = this.shouldShowModifier(dataType); + this.showModifiersMap.set(keyFormGroup.get('id').value, withModifier); this.updateFunctionCodes(keyFormGroup, dataType); }); } + private observeEnableModifier(keyFormGroup: FormGroup): void { + this.enableModifiersControlMap.get(keyFormGroup.get('id').value).valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(showModifier => this.toggleModifierControls(keyFormGroup, showModifier)); + } + + private toggleModifierControls(keyFormGroup: FormGroup, enable: boolean): void { + const modifierTypeControl = keyFormGroup.get('modifierType'); + const modifierValueControl = keyFormGroup.get('modifierValue'); + + if (enable) { + modifierTypeControl.enable(); + modifierValueControl.enable(); + } else { + modifierTypeControl.disable(); + modifierValueControl.disable(); + } + } + private updateFunctionCodes(keyFormGroup: FormGroup, dataType: ModbusDataType): void { const functionCodes = this.getFunctionCodes(dataType); this.functionCodesMap.set(keyFormGroup.get('id').value, functionCodes); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts index a2184089b1..fcc04beac6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts @@ -19,6 +19,7 @@ import { AttributeData } from '@shared/models/telemetry/telemetry.models'; export const noLeadTrailSpacesRegex = /^\S+(?: \S+)*$/; export const integerRegex = /^[-+]?\d+$/; +export const nonZeroFloat = /^-?(?!0(\.0+)?$)\d+(\.\d+)?$/; export enum StorageTypes { MEMORY = 'memory', @@ -897,6 +898,30 @@ export enum MappingValueType { BOOLEAN = 'boolean' } +export enum ModifierType { + DIVIDER = 'divider', + MULTIPLIER = 'multiplier', +} + +export const ModifierTypesMap = new Map( + [ + [ + ModifierType.DIVIDER, + { + name: 'gateway.divider', + icon: 'mdi:division' + } + ], + [ + ModifierType.MULTIPLIER, + { + name: 'gateway.multiplier', + icon: 'mdi:multiplication' + } + ], + ] +); + export const mappingValueTypesMap = new Map( [ [ @@ -1141,6 +1166,8 @@ export interface ModbusValue { objectsCount: number; address: number; value?: string; + multiplier?: number; + divider?: number; } export interface ModbusSecurity { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index f5f26beeeb..d5476dc388 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2934,6 +2934,7 @@ "details": "Details", "delete-mapping-title": "Delete mapping?", "delete-slave-title": "Delete slave?", + "divider": "Divider", "download-configuration-file": "Download configuration file", "download-docker-compose": "Download docker-compose.yml for your gateway", "enable-remote-logging": "Enable remote logging", @@ -3089,8 +3090,11 @@ "min-pack-send-delay-required": "Min pack send delay is required", "min-pack-send-delay-min": "Min pack send delay can not be less then 10", "min-pack-send-delay-pattern": "Min pack send delay is not valid", + "multiplier": "Multiplier", "mode": "Mode", "model-name": "Model name", + "modifier": "Modifier", + "modifier-invalid": "Modifier is not valid", "mqtt-version": "MQTT version", "name": "Name", "name-required": "Name is required.", @@ -3493,7 +3497,8 @@ "objects-count": "Depends on the selected type.", "address": "Register address to verify.", "key": "Key to be used as the attribute key for the platform instance.", - "data-keys": "For more information about function codes and data types click on help icon" + "data-keys": "For more information about function codes and data types click on help icon", + "modifier": "The retrieved value will be adjusted (by multiplying or dividing it) based on the specified modifier value." } } },