Added posibility to configure modifier type for timeseries and attributes of Modbus Connector slaves
This commit is contained in:
parent
05a813a2c8
commit
bbbc6fd558
@ -148,6 +148,62 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="showModifiersMap.get(keyControl.get('id').value)" class="tb-form-panel stroked tb-slide-toggle">
|
||||
<mat-expansion-panel class="tb-settings" [expanded]="enableModifiersControlMap.get(keyControl.get('id').value).value">
|
||||
<mat-expansion-panel-header fxLayout="row wrap">
|
||||
<mat-panel-title>
|
||||
<mat-slide-toggle
|
||||
fxLayoutAlign="center"
|
||||
[formControl]="enableModifiersControlMap.get(keyControl.get('id').value)"
|
||||
class="mat-slide"
|
||||
(click)="$event.stopPropagation()"
|
||||
>
|
||||
<mat-label tb-hint-tooltip-icon="{{ 'gateway.hints.modbus.modifier' | translate }}">
|
||||
{{ 'gateway.modifier' | translate }}
|
||||
</mat-label>
|
||||
</mat-slide-toggle>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="tb-flex no-gap">
|
||||
<div class="tb-form-row column-xs tb-flex full-width" fxLayoutAlign="space-between center">
|
||||
<div class="fixed-title-width" translate>gateway.type</div>
|
||||
<div class="tb-flex no-gap">
|
||||
<mat-form-field class="tb-flex no-gap fill-width" appearance="outline" subscriptSizing="dynamic">
|
||||
<mat-select formControlName="modifierType">
|
||||
<mat-select-trigger>
|
||||
<div class="tb-flex align-center">
|
||||
<mat-icon class="tb-mat-18" [svgIcon]="ModifierTypesMap.get(keyControl.get('modifierType').value)?.icon"></mat-icon>
|
||||
<span>{{ ModifierTypesMap.get(keyControl.get('modifierType').value)?.name | translate}}</span>
|
||||
</div>
|
||||
</mat-select-trigger>
|
||||
<mat-option *ngFor="let modifierType of modifierTypes" [value]="modifierType">
|
||||
<mat-icon class="tb-mat-20" svgIcon="{{ ModifierTypesMap.get(modifierType).icon }}">
|
||||
</mat-icon>
|
||||
<span>{{ ModifierTypesMap.get(modifierType).name | translate }}</span>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||
<div class="fixed-title-width" translate>gateway.value</div>
|
||||
<mat-form-field fxFlex appearance="outline" subscriptSizing="dynamic" class="tb-inline-field flex tb-suffix-absolute">
|
||||
<input matInput required formControlName="modifierValue" step="0.1" type="number"
|
||||
placeholder="{{ 'gateway.set' | translate }}" />
|
||||
<mat-icon matSuffix
|
||||
matTooltipPosition="above"
|
||||
matTooltipClass="tb-error-tooltip"
|
||||
[matTooltip]="('gateway.modifier-invalid') | translate"
|
||||
*ngIf="keyControl.get('modifierValue').hasError('pattern') &&
|
||||
keyControl.get('modifierValue').touched"
|
||||
class="tb-error">
|
||||
warning
|
||||
</mat-icon>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</div>
|
||||
<div *ngIf="isMaster" class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||
<div class="fixed-title-width tb-required" translate>gateway.value</div>
|
||||
<div class="tb-flex no-gap">
|
||||
|
||||
@ -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<UntypedFormGroup>;
|
||||
modbusDataTypes = Object.values(ModbusDataType);
|
||||
modifierTypes: ModifierType[] = Object.values(ModifierType);
|
||||
withFunctionCode = true;
|
||||
|
||||
enableModifiersControlMap = new Map<string, FormControl<boolean>>();
|
||||
showModifiersMap = new Map<string, boolean>();
|
||||
functionCodesMap = new Map();
|
||||
defaultFunctionCodes = [];
|
||||
|
||||
readonly ModbusEditableDataTypes = ModbusEditableDataTypes;
|
||||
readonly ModbusFunctionCodeTranslationsMap = ModbusFunctionCodeTranslationsMap;
|
||||
readonly ModifierTypesMap = ModifierTypesMap;
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
@ -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<ModbusValue> {
|
||||
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);
|
||||
|
||||
@ -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, ValueTypeData>(
|
||||
[
|
||||
[
|
||||
ModifierType.DIVIDER,
|
||||
{
|
||||
name: 'gateway.divider',
|
||||
icon: 'mdi:division'
|
||||
}
|
||||
],
|
||||
[
|
||||
ModifierType.MULTIPLIER,
|
||||
{
|
||||
name: 'gateway.multiplier',
|
||||
icon: 'mdi:multiplication'
|
||||
}
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
export const mappingValueTypesMap = new Map<MappingValueType, ValueTypeData>(
|
||||
[
|
||||
[
|
||||
@ -1141,6 +1166,8 @@ export interface ModbusValue {
|
||||
objectsCount: number;
|
||||
address: number;
|
||||
value?: string;
|
||||
multiplier?: number;
|
||||
divider?: number;
|
||||
}
|
||||
|
||||
export interface ModbusSecurity {
|
||||
|
||||
@ -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."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user