From 7caffa941a12764e1ec3ab323f8de5e268c2a7bb Mon Sep 17 00:00:00 2001 From: Alexander Hartung Date: Mon, 20 Jul 2026 14:37:00 +0200 Subject: [PATCH 1/7] Add Universeller Modbus --- .../modules/devices/generic/modbus/bat.py | 148 ++++++++++ .../modules/devices/generic/modbus/config.py | 193 ++++++++++++++ .../modules/devices/generic/modbus/counter.py | 252 ++++++++++++++++++ .../modules/devices/generic/modbus/device.py | 58 ++++ .../modules/devices/generic/modbus/helper.py | 6 + .../devices/generic/modbus/inverter.py | 149 +++++++++++ 6 files changed, 806 insertions(+) create mode 100644 packages/modules/devices/generic/modbus/bat.py create mode 100644 packages/modules/devices/generic/modbus/config.py create mode 100644 packages/modules/devices/generic/modbus/counter.py create mode 100644 packages/modules/devices/generic/modbus/device.py create mode 100644 packages/modules/devices/generic/modbus/helper.py create mode 100644 packages/modules/devices/generic/modbus/inverter.py diff --git a/packages/modules/devices/generic/modbus/bat.py b/packages/modules/devices/generic/modbus/bat.py new file mode 100644 index 0000000000..ec6abb1550 --- /dev/null +++ b/packages/modules/devices/generic/modbus/bat.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any + +from modules.common.modbus import Endian + +from modules.common.abstract_device import AbstractBat +from modules.common.component_state import BatState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.store import get_bat_value_store +from modules.devices.generic.modbus.config import GenericModbusBatSetup +from modules.common.utils.peak_filter import PeakFilter +from modules.common.component_type import ComponentType +from modules.common.simcount import SimCounter + +from modules.devices.generic.modbus.helper import check_data + + +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + +class GenericModbusBat(AbstractBat): + def __init__(self, component_config: GenericModbusBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__modbus_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.store = get_bat_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.peak_filter = PeakFilter(ComponentType.BAT, self.component_config.id, self.fault_state) + self.sim_counter = SimCounter(self.__modbus_id, self.component_config.id, prefix="speicher") + + def update(self) -> None: + + unit = self.component_config.configuration.modbus_id + + # power + # + if self.component_config.configuration.power.reg_address is not None: + check_data(self.component_config.configuration.power) + power = self.client.read_input_registers( + int(self.component_config.configuration.power.reg_address), ModbusDataType[ + self.component_config.configuration.power.reg_type], + byteorder=self.component_config.configuration.power.byteorder, + wordorder=self.component_config.configuration.power.wordorder, unit=unit) + else: + raise ValueError("Leistungsregister muss angegeben werden.") + + # SOC + # + if self.component_config.configuration.soc.reg_address is not None: + check_data(self.component_config.configuration.soc) + soc = self.client.read_input_registers( + int(self.component_config.configuration.soc.reg_address), ModbusDataType[ + self.component_config.configuration.soc.reg_type], + byteorder=self.component_config.configuration.soc.byteorder, + wordorder=self.component_config.configuration.soc.wordorder, unit=unit) + + # currents + # + if (self.component_config.configuration.current_L1.reg_address is not None or + self.component_config.configuration.current_L2.reg_address is not None or + self.component_config.configuration.current_L3.reg_address is not None): + # mind. ein register angegeben + currents = [0.0]*3 + if self.component_config.configuration.current_L1.reg_address is not None: + check_data(self.component_config.configuration.current_L1) + currents[0] = self.client.read_input_registers(int(self.component_config.configuration.current_L1.reg_address), + ModbusDataType[self.component_config.configuration.current_L1.reg_type], + byteorder=self.component_config.configuration.current_L1.byteorder, + wordorder=self.component_config.configuration.current_L1.wordorder, unit=unit) + + if self.component_config.configuration.current_L2.reg_address is not None: + check_data(self.component_config.configuration.current_L2) + currents[1] = self.client.read_input_registers(int(self.component_config.configuration.current_L2.reg_address), + ModbusDataType[self.component_config.configuration.current_L2.reg_type], + byteorder=self.component_config.configuration.current_L2.byteorder, + wordorder=self.component_config.configuration.current_L2.wordorder, unit=unit) + + if self.component_config.configuration.current_L3.reg_address is not None: + check_data(self.component_config.configuration.current_L3) + currents[2] = self.client.read_input_registers(int(self.component_config.configuration.current_L3.reg_address), + ModbusDataType[self.component_config.configuration.current_L3.reg_type], + byteorder=self.component_config.configuration.current_L3.byteorder, + wordorder=self.component_config.configuration.current_L3.wordorder, unit=unit) + + # Import + # + if self.component_config.configuration.imported.reg_address is not None: + check_data(self.component_config.configuration.imported) + imported = self.client.read_input_registers( + int(self.component_config.configuration.imported.reg_address), ModbusDataType[ + self.component_config.configuration.imported.reg_type], + byteorder=self.component_config.configuration.imported.byteorder, + wordorder=self.component_config.configuration.imported.wordorder, unit=unit) + else: + imported = None + + # Export + # + if self.component_config.configuration.exported.reg_address is not None: + check_data(self.component_config.configuration.exported) + exported = self.client.read_input_registers( + int(self.component_config.configuration.exported.reg_address), ModbusDataType[ + self.component_config.configuration.exported.reg_type], + byteorder=self.component_config.configuration.exported.byteorder, + wordorder=self.component_config.configuration.exported.wordorder, unit=unit) + else: + exported = None + + # Serial Number + # + if self.component_config.configuration.serial_number.reg_address is not None: + check_data(self.component_config.configuration.serial_number) + serial_number = self.client.read_input_registers( + int(self.component_config.configuration.serial_number.reg_address), ModbusDataType[ + self.component_config.configuration.serial_number.reg_type], + byteorder=self.component_config.configuration.serial_number.byteorder, + wordorder=self.component_config.configuration.serial_number.wordorder, unit=unit) + + if power is not None: + self.peak_filter.check_values(power) + if imported is None or exported is None: + imported, exported = self.sim_counter.sim_count(power) + imported, exported = self.peak_filter.check_values(power, imported, exported) + + bat_state = BatState( + imported=imported, + exported=exported, + power=power, + ) + + if "soc" in locals(): + bat_state.soc = soc + if "currents" in locals(): + bat_state.currents = currents + if 'serial_number' in locals(): + bat_state.serial_number = serial_number + + self.store.set(bat_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=GenericModbusBatSetup) diff --git a/packages/modules/devices/generic/modbus/config.py b/packages/modules/devices/generic/modbus/config.py new file mode 100644 index 0000000000..229858aba5 --- /dev/null +++ b/packages/modules/devices/generic/modbus/config.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +from modules.common.component_setup import ComponentSetup +from ..vendor import vendor_descriptor + +from typing import Optional +from dataclasses import dataclass +from modules.common.modbus import ModbusDataType, Endian + + +class GenericModbusConfiguration: + def __init__(self, ip_address: str = "192.168.1.230", port: int = 502): + self.ip_address = ip_address + self.port = port + + +class GenericModbus: + def __init__(self, + name: str = "Modbus", + type: str = "modbus", + id: int = 0, + configuration: GenericModbusConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or GenericModbusConfiguration() + + +@dataclass +class gerneric_modbus: + reg_address: int + reg_type: str + byteorder: str + wordorder: str + + +class GenericModbusCounterConfiguration: + def __init__(self, modbus_id: int = 105, + voltage_L1: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + voltage_L2: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + voltage_L3: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + current_L1: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + current_L2: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + current_L3: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + powers_L1: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + powers_L2: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + powers_L3: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + power_factor_L1: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + power_factor_L2: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + power_factor_L3: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + imported: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + exported: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + power: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + frequency: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + serial_number: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None)): + + self.modbus_id = modbus_id + self.voltage_L1 = voltage_L1 + self.voltage_L2 = voltage_L2 + self.voltage_L3 = voltage_L3 + + self.current_L1 = current_L1 + self.current_L2 = current_L2 + self.current_L3 = current_L3 + + self.powers_L1 = powers_L1 + self.powers_L2 = powers_L2 + self.powers_L3 = powers_L3 + + self.power_factor_L1 = power_factor_L1 + self.power_factor_L2 = power_factor_L2 + self.power_factor_L3 = power_factor_L3 + + self.imported = imported + self.exported = exported + + self.power = power + + self.frequency = frequency + + self.serial_number = serial_number + + +class GenericModbusCounterSetup(ComponentSetup[GenericModbusCounterConfiguration]): + def __init__(self, + name: str = "Universeller Modbus Zähler", + type: str = "counter", + id: int = 0, + configuration: GenericModbusCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or GenericModbusCounterConfiguration()) + + +class GenericModbusBatConfiguration: + def __init__(self, modbus_id: int = 100, + current_L1: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + current_L2: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + current_L3: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + imported: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + exported: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + power: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + soc: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + serial_number: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None)): + + self.modbus_id = modbus_id + + self.current_L1 = current_L1 + self.current_L2 = current_L2 + self.current_L3 = current_L3 + + self.imported = imported + self.exported = exported + + self.power = power + self.soc = soc + + self.serial_number = serial_number + + +class GenericModbusBatSetup(ComponentSetup[GenericModbusBatConfiguration]): + def __init__(self, + name: str = "Universeller Modbus Batterie", + type: str = "bat", + id: int = 0, + configuration: GenericModbusBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or GenericModbusBatConfiguration()) + + +class GenericModbusInverterConfiguration: + def __init__(self, modbus_id: int = 100, + current_L1: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + current_L2: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + current_L3: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + imported: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + exported: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + power: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + dc_power: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None), + serial_number: gerneric_modbus = gerneric_modbus( + reg_address=None, reg_type=None, byteorder=None, wordorder=None)): + + self.modbus_id = modbus_id + + self.current_L1 = current_L1 + self.current_L2 = current_L2 + self.current_L3 = current_L3 + + self.imported = imported + self.exported = exported + + self.power = power + self.dc_power = dc_power + + self.serial_number = serial_number + + +class GenericModbusInverterSetup(ComponentSetup[GenericModbusInverterConfiguration]): + def __init__(self, + name: str = "Universeller Modbus Wechselrichter", + type: str = "inverter", + id: int = 0, + configuration: GenericModbusInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or GenericModbusInverterConfiguration()) diff --git a/packages/modules/devices/generic/modbus/counter.py b/packages/modules/devices/generic/modbus/counter.py new file mode 100644 index 0000000000..be8fae831c --- /dev/null +++ b/packages/modules/devices/generic/modbus/counter.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python3 +from typing import Any, TypedDict +from modules.devices.generic.modbus.helper import check_data +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.component_type import ComponentDescriptor +from modules.common.simcount._simcounter import SimCounter +from modules.devices.generic.modbus.config import GenericModbusCounterSetup +from modules.common.utils.peak_filter import PeakFilter +from modules.common.component_type import ComponentType + +from modules.common.store import get_counter_value_store + +from modules.common.modbus import ModbusDataType, Endian, ModbusTcpClient_ + +import logging +log = logging.getLogger(__name__) + + +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + +class GenericModbusCounter(AbstractCounter): + def __init__(self, component_config: GenericModbusCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.peak_filter = PeakFilter(ComponentType.COUNTER, self.component_config.id, self.fault_state) + + def update(self) -> None: + unit = self.component_config.configuration.modbus_id + + # Power + # + if self.component_config.configuration.power.reg_address is not None: + check_data(self.component_config.configuration.power) + power = self.client.read_input_registers( + int(self.component_config.configuration.power.reg_address), ModbusDataType[ + self.component_config.configuration.power.reg_type], + byteorder=self.component_config.configuration.power.byteorder, + wordorder=self.component_config.configuration.power.wordorder, unit=unit) + else: + raise ValueError("Leistungsregister muss angegeben werden.") + + # Voltages + # + if (self.component_config.configuration.voltage_L1.reg_address is not None or + self.component_config.configuration.voltage_L2.reg_address is not None or + self.component_config.configuration.voltage_L3.reg_address is not None): + # Kein register angegeben + voltages = [0.0]*3 + if self.component_config.configuration.voltage_L1.reg_address is not None: + check_data(self.component_config.configuration.voltage_L1) + voltages[0] = self.client.read_input_registers( + int(self.component_config.configuration.voltage_L1.reg_address), + ModbusDataType[self.component_config.configuration.voltage_L1.reg_type], + byteorder=self.component_config.configuration.voltage_L1.byteorder, + wordorder=self.component_config.configuration.voltage_L1.wordorder, unit=unit) + + if self.component_config.configuration.voltage_L2.reg_address is not None: + check_data(self.component_config.configuration.voltage_L2) + voltages[1] = self.client.read_input_registers( + int(self.component_config.configuration.voltage_L2.reg_address), + ModbusDataType[self.component_config.configuration.voltage_L2.reg_type], + byteorder=self.component_config.configuration.voltage_L2.byteorder, + wordorder=self.component_config.configuration.voltage_L2.wordorder, unit=unit) + + if self.component_config.configuration.voltage_L3.reg_address is not None: + check_data(self.component_config.configuration.voltage_L3) + voltages[2] = self.client.read_input_registers( + int(self.component_config.configuration.voltage_L3.reg_address), + ModbusDataType[self.component_config.configuration.voltage_L3.reg_type], + byteorder=self.component_config.configuration.voltage_L3.byteorder, + wordorder=self.component_config.configuration.voltage_L3.wordorder, unit=unit) + + # Currents + # + if (self.component_config.configuration.current_L1.reg_address is not None or + self.component_config.configuration.current_L2.reg_address is not None or + self.component_config.configuration.current_L3.reg_address is not None): + # mind. ein register angegeben + currents = [0.0]*3 + if self.component_config.configuration.current_L1.reg_address is not None: + check_data(self.component_config.configuration.current_L1) + currents[0] = self.client.read_input_registers( + int(self.component_config.configuration.current_L1.reg_address), + ModbusDataType[self.component_config.configuration.current_L1.reg_type], + byteorder=self.component_config.configuration.current_L1.byteorder, + wordorder=self.component_config.configuration.current_L1.wordorder, unit=unit) + + if self.component_config.configuration.current_L2.reg_address is not None: + check_data(self.component_config.configuration.current_L2) + currents[1] = self.client.read_input_registers( + int(self.component_config.configuration.current_L2.reg_address), + ModbusDataType[self.component_config.configuration.current_L2.reg_type], + byteorder=self.component_config.configuration.current_L2.byteorder, + wordorder=self.component_config.configuration.current_L2.wordorder, unit=unit) + + if self.component_config.configuration.current_L3.reg_address is not None: + check_data(self.component_config.configuration.current_L3) + currents[2] = self.client.read_input_registers( + int(self.component_config.configuration.current_L3.reg_address), + ModbusDataType[self.component_config.configuration.current_L3.reg_type], + byteorder=self.component_config.configuration.current_L3.byteorder, + wordorder=self.component_config.configuration.current_L3.wordorder, unit=unit) + + # Powers + # + if (self.component_config.configuration.powers_L1.reg_address is not None or + self.component_config.configuration.powers_L2.reg_address is not None or + self.component_config.configuration.powers_L3.reg_address is not None): + # mind. ein register angegeben + powers = [0.0]*3 + if self.component_config.configuration.powers_L1.reg_address is not None: + check_data(self.component_config.configuration.powers_L1) + powers[0] = self.client.read_input_registers( + int(self.component_config.configuration.powers_L1.reg_address), + ModbusDataType[self.component_config.configuration.powers_L1.reg_type], + byteorder=self.component_config.configuration.powers_L1.byteorder, + wordorder=self.component_config.configuration.powers_L1.wordorder, unit=unit) + + if self.component_config.configuration.powers_L2.reg_address is not None: + check_data(self.component_config.configuration.powers_L2) + powers[1] = self.client.read_input_registers( + int(self.component_config.configuration.powers_L2.reg_address), + ModbusDataType[self.component_config.configuration.powers_L2.reg_type], + byteorder=self.component_config.configuration.powers_L2.byteorder, + wordorder=self.component_config.configuration.powers_L2.wordorder, unit=unit) + + if self.component_config.configuration.powers_L3.reg_address is not None: + check_data(self.component_config.configuration.powers_L3) + powers[2] = self.client.read_input_registers( + int(self.component_config.configuration.powers_L3.reg_address), + ModbusDataType[self.component_config.configuration.powers_L3.reg_type], + byteorder=self.component_config.configuration.powers_L3.byteorder, + wordorder=self.component_config.configuration.powers_L3.wordorder, unit=unit) + + # Power Factors‚ + # + if (self.component_config.configuration.power_factor_L1.reg_address is not None or + self.component_config.configuration.power_factor_L2.reg_address is not None or + self.component_config.configuration.power_factor_L3.reg_address is not None): + # mind. ein register angegeben + power_factors = [0.0]*3 + if self.component_config.configuration.power_factor_L1.reg_address is not None: + check_data(self.component_config.configuration.power_factor_L1) + power_factors[0] = self.client.read_input_registers( + int(self.component_config.configuration.power_factor_L1.reg_address), + ModbusDataType[self.component_config.configuration.power_factor_L1.reg_type], + byteorder=self.component_config.configuration.power_factor_L1.byteorder, + wordorder=self.component_config.configuration.power_factor_L1.wordorder, unit=unit) + + if self.component_config.configuration.power_factor_L2.reg_address is not None: + check_data(self.component_config.configuration.power_factor_L2) + power_factors[1] = self.client.read_input_registers( + int(self.component_config.configuration.power_factor_L2.reg_address), + ModbusDataType[self.component_config.configuration.power_factor_L2.reg_type], + byteorder=self.component_config.configuration.power_factor_L2.byteorder, + wordorder=self.component_config.configuration.power_factor_L2.wordorder, unit=unit) + + if self.component_config.configuration.power_factor_L3.reg_address is not None: + check_data(self.component_config.configuration.power_factor_L3) + power_factors[2] = self.client.read_input_registers( + int(self.component_config.configuration.power_factor_L3.reg_address), + ModbusDataType[self.component_config.configuration.power_factor_L3.reg_type], + byteorder=self.component_config.configuration.power_factor_L3.byteorder, + wordorder=self.component_config.configuration.power_factor_L3.wordorder, unit=unit) + + # Frequency + # + if self.component_config.configuration.frequency.reg_address is not None: + check_data(self.component_config.configuration.frequency) + frequency = self.client.read_input_registers( + int(self.component_config.configuration.frequency.reg_address), ModbusDataType[ + self.component_config.configuration.frequency.reg_type], + byteorder=self.component_config.configuration.frequency.byteorder, + wordorder=self.component_config.configuration.frequency.wordorder, unit=unit) + + # Imported + # + if self.component_config.configuration.imported.reg_address is not None: + check_data(self.component_config.configuration.imported) + imported = self.client.read_input_registers( + int(self.component_config.configuration.imported.reg_address), ModbusDataType[ + self.component_config.configuration.imported.reg_type], + byteorder=self.component_config.configuration.imported.byteorder, + wordorder=self.component_config.configuration.imported.wordorder, unit=unit) + else: + imported = None + + # Exported + # + if self.component_config.configuration.exported.reg_address is not None: + check_data(self.component_config.configuration.exported) + exported = self.client.read_input_registers( + int(self.component_config.configuration.exported.reg_address), ModbusDataType[ + self.component_config.configuration.exported.reg_type], + byteorder=self.component_config.configuration.exported.byteorder, + wordorder=self.component_config.configuration.exported.wordorder, unit=unit) + else: + exported = None + + # Serial Number + # + if self.component_config.configuration.serial_number.reg_address is not None: + check_data(self.component_config.configuration.serial_number) + serial_number = self.client.read_input_registers( + int(self.component_config.configuration.serial_number.reg_address), ModbusDataType[ + self.component_config.configuration.serial_number.reg_type], + byteorder=self.component_config.configuration.serial_number.byteorder, + wordorder=self.component_config.configuration.serial_number.wordorder, unit=unit) + + if power is not None: + self.peak_filter.check_values(power) + if imported is None or exported is None: + imported, exported = self.sim_counter.sim_count(power) + imported, exported = self.peak_filter.check_values(power, imported, exported) + + counter_state = CounterState( + imported=imported, + exported=exported, + power=power + ) + + if "voltages" in locals(): + counter_state.voltages = voltages + if "currents" in locals(): + counter_state.currents = currents + if 'powers' in locals(): + counter_state.powers = powers + if "power_factors" in locals(): + counter_state.power_factors = power_factors + if 'frequency' in locals(): + counter_state.frequency = frequency + if 'serial_number' in locals(): + counter_state.serial_number = serial_number + + self.store.set(counter_state) + + log.debug(f"CounterState updated: {counter_state}") + + +component_descriptor = ComponentDescriptor(configuration_factory=GenericModbusCounterSetup) diff --git a/packages/modules/devices/generic/modbus/device.py b/packages/modules/devices/generic/modbus/device.py new file mode 100644 index 0000000000..a26f36f5b1 --- /dev/null +++ b/packages/modules/devices/generic/modbus/device.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +from typing import Iterable, Union +import logging + +from helpermodules.broker import BrokerClient +from helpermodules.utils.topic_parser import decode_payload +from modules.devices.generic.modbus.bat import GenericModbusBat +from modules.devices.generic.modbus.inverter import GenericModbusInverter +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.component_type import type_to_topic_mapping +from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater +from modules.devices.generic.modbus.counter import GenericModbusCounter + +from modules.devices.generic.modbus.config import GenericModbus, GenericModbusCounterSetup, GenericModbusBatSetup, GenericModbusInverterSetup + +from modules.common import modbus + +log = logging.getLogger(__name__) + + +def create_device(device_config: GenericModbus): + client = None + + def create_counter_component(component_config: GenericModbusCounterSetup): + nonlocal client + return GenericModbusCounter(component_config, device_id=device_config.id, client=client) + + def create_bat_component(component_config: GenericModbusBatSetup): + nonlocal client + return GenericModbusBat(component_config, device_id=device_config.id, client=client) + + def create_inverter_component(component_config: GenericModbusInverterSetup): + nonlocal client + return GenericModbusInverter(component_config, device_id=device_config.id, client=client) + + def update_components(components: Iterable[Union[GenericModbusCounter, GenericModbusBat, GenericModbusInverter]]): + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + nonlocal client + client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) + + return ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + counter=create_counter_component, + inverter=create_inverter_component, + bat=create_bat_component + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=GenericModbus) diff --git a/packages/modules/devices/generic/modbus/helper.py b/packages/modules/devices/generic/modbus/helper.py new file mode 100644 index 0000000000..8f0f2d0ce6 --- /dev/null +++ b/packages/modules/devices/generic/modbus/helper.py @@ -0,0 +1,6 @@ +def check_data(wert): + if (wert.reg_type is None or + wert.byteorder is None or + wert.wordorder is None): + raise ValueError( + f"Unvollständige Konfiguration für Universeller-Modbus: Register-Adresse {wert.reg_address}") diff --git a/packages/modules/devices/generic/modbus/inverter.py b/packages/modules/devices/generic/modbus/inverter.py new file mode 100644 index 0000000000..d2dc10a2aa --- /dev/null +++ b/packages/modules/devices/generic/modbus/inverter.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any + +from modules.common.modbus import Endian + +from modules.common.abstract_device import AbstractInverter +from modules.common.component_state import InverterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.utils.peak_filter import PeakFilter +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.store import get_inverter_value_store +from modules.devices.generic.modbus.config import GenericModbusInverterSetup +from modules.common.component_type import ComponentType +from modules.common.simcount import SimCounter + +from modules.devices.generic.modbus.helper import check_data + + +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + +class GenericModbusInverter(AbstractInverter): + def __init__(self, component_config: GenericModbusInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.store = get_inverter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.peak_filter = PeakFilter(ComponentType.INVERTER, self.component_config.id, self.fault_state) + self.sim_counter = SimCounter(self.kwargs['device_id'], self.component_config.id, prefix="pv") + + def update(self) -> None: + unit = self.component_config.configuration.modbus_id + + # Power + # + if self.component_config.configuration.power.reg_address is not None: + check_data(self.component_config.configuration.power) + power = self.client.read_input_registers( + int(self.component_config.configuration.power.reg_address), ModbusDataType[ + self.component_config.configuration.power.reg_type], + byteorder=self.component_config.configuration.power.byteorder, + wordorder=self.component_config.configuration.power.wordorder, unit=unit) + else: + raise ValueError("Leistungsregister muss angegeben werden.") + + # Exported + # + if self.component_config.configuration.exported.reg_address is not None: + check_data(self.component_config.configuration.exported) + exported = self.client.read_input_registers( + int(self.component_config.configuration.exported.reg_address), ModbusDataType[ + self.component_config.configuration.exported.reg_type], + byteorder=self.component_config.configuration.exported.byteorder, + wordorder=self.component_config.configuration.exported.wordorder, unit=unit) + else: + exported = None + + # Imported + # + if self.component_config.configuration.imported.reg_address is not None: + check_data(self.component_config.configuration.imported) + imported = self.client.read_input_registers( + int(self.component_config.configuration.imported.reg_address), ModbusDataType[ + self.component_config.configuration.imported.reg_type], + byteorder=self.component_config.configuration.imported.byteorder, + wordorder=self.component_config.configuration.imported.wordorder, unit=unit) + else: + imported = None + + # Currents + # + if (self.component_config.configuration.current_L1.reg_address is not None or + self.component_config.configuration.current_L2.reg_address is not None or + self.component_config.configuration.current_L3.reg_address is not None): + # mind. ein register angegeben + currents = [0.0]*3 + if self.component_config.configuration.current_L1.reg_address is not None: + check_data(self.component_config.configuration.current_L1) + currents[0] = self.client.read_input_registers( + int(self.component_config.configuration.current_L1.reg_address), ModbusDataType[ + self.component_config.configuration.current_L1.reg_type], + byteorder=self.component_config.configuration.current_L1.byteorder, + wordorder=self.component_config.configuration.current_L1.wordorder, unit=unit) + + if self.component_config.configuration.current_L2.reg_address is not None: + check_data(self.component_config.configuration.current_L2) + currents[1] = self.client.read_input_registers( + int(self.component_config.configuration.current_L2.reg_address), ModbusDataType[ + self.component_config.configuration.current_L2.reg_type], + byteorder=self.component_config.configuration.current_L2.byteorder, + wordorder=self.component_config.configuration.current_L2.wordorder, unit=unit) + + if self.component_config.configuration.current_L3.reg_address is not None: + check_data(self.component_config.configuration.current_L3) + currents[2] = self.client.read_input_registers( + int(self.component_config.configuration.current_L3.reg_address), ModbusDataType[ + self.component_config.configuration.current_L3.reg_type], + byteorder=self.component_config.configuration.current_L3.byteorder, + wordorder=self.component_config.configuration.current_L3.wordorder, unit=unit) + + # DC Power + # + if self.component_config.configuration.dc_power.reg_address is not None: + check_data(self.component_config.configuration.dc_power) + dc_power = self.client.read_input_registers( + int(self.component_config.configuration.dc_power.reg_address), ModbusDataType[ + self.component_config.configuration.dc_power.reg_type], + byteorder=self.component_config.configuration.dc_power.byteorder, + wordorder=self.component_config.configuration.dc_power.wordorder, unit=unit) + + # Serial Number + # + if self.component_config.configuration.serial_number.reg_address is not None: + check_data(self.component_config.configuration.serial_number) + serial_number = self.client.read_input_registers( + int(self.component_config.configuration.serial_number.reg_address), ModbusDataType[ + self.component_config.configuration.serial_number.reg_type], + byteorder=self.component_config.configuration.serial_number.byteorder, + wordorder=self.component_config.configuration.serial_number.wordorder, unit=unit) + + if power is not None: + self.peak_filter.check_values(power) + if imported is None or exported is None: + imported, exported = self.sim_counter.sim_count(power) + imported, exported = self.peak_filter.check_values(power, imported, exported) + + inverter_state = InverterState( + power=power, + exported=exported, + imported=imported, + ) + + if "dc_power" in locals(): + inverter_state.dc_power = dc_power + if "currents" in locals(): + inverter_state.currents = currents + if 'serial_number' in locals(): + inverter_state.serial_number = serial_number + + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=GenericModbusInverterSetup) From 4189fca01acd55fd631e89174fc1da1e217d3ff1 Mon Sep 17 00:00:00 2001 From: Alexander Hartung Date: Thu, 23 Jul 2026 11:02:14 +0200 Subject: [PATCH 2/7] fix --- .../modules/devices/generic/modbus/bat.py | 29 ++++++++++--------- .../modules/devices/generic/modbus/config.py | 2 -- .../modules/devices/generic/modbus/counter.py | 2 +- .../modules/devices/generic/modbus/device.py | 10 ++++--- .../devices/generic/modbus/inverter.py | 2 -- 5 files changed, 22 insertions(+), 23 deletions(-) diff --git a/packages/modules/devices/generic/modbus/bat.py b/packages/modules/devices/generic/modbus/bat.py index ec6abb1550..883a584550 100644 --- a/packages/modules/devices/generic/modbus/bat.py +++ b/packages/modules/devices/generic/modbus/bat.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 from typing import TypedDict, Any -from modules.common.modbus import Endian - from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -70,24 +68,27 @@ def update(self) -> None: currents = [0.0]*3 if self.component_config.configuration.current_L1.reg_address is not None: check_data(self.component_config.configuration.current_L1) - currents[0] = self.client.read_input_registers(int(self.component_config.configuration.current_L1.reg_address), - ModbusDataType[self.component_config.configuration.current_L1.reg_type], - byteorder=self.component_config.configuration.current_L1.byteorder, - wordorder=self.component_config.configuration.current_L1.wordorder, unit=unit) + currents[0] = self.client.read_input_registers( + int(self.component_config.configuration.current_L1.reg_address), + ModbusDataType[self.component_config.configuration.current_L1.reg_type], + byteorder=self.component_config.configuration.current_L1.byteorder, + wordorder=self.component_config.configuration.current_L1.wordorder, unit=unit) if self.component_config.configuration.current_L2.reg_address is not None: check_data(self.component_config.configuration.current_L2) - currents[1] = self.client.read_input_registers(int(self.component_config.configuration.current_L2.reg_address), - ModbusDataType[self.component_config.configuration.current_L2.reg_type], - byteorder=self.component_config.configuration.current_L2.byteorder, - wordorder=self.component_config.configuration.current_L2.wordorder, unit=unit) + currents[1] = self.client.read_input_registers( + int(self.component_config.configuration.current_L2.reg_address), + ModbusDataType[self.component_config.configuration.current_L2.reg_type], + byteorder=self.component_config.configuration.current_L2.byteorder, + wordorder=self.component_config.configuration.current_L2.wordorder, unit=unit) if self.component_config.configuration.current_L3.reg_address is not None: check_data(self.component_config.configuration.current_L3) - currents[2] = self.client.read_input_registers(int(self.component_config.configuration.current_L3.reg_address), - ModbusDataType[self.component_config.configuration.current_L3.reg_type], - byteorder=self.component_config.configuration.current_L3.byteorder, - wordorder=self.component_config.configuration.current_L3.wordorder, unit=unit) + currents[2] = self.client.read_input_registers( + int(self.component_config.configuration.current_L3.reg_address), + ModbusDataType[self.component_config.configuration.current_L3.reg_type], + byteorder=self.component_config.configuration.current_L3.byteorder, + wordorder=self.component_config.configuration.current_L3.wordorder, unit=unit) # Import # diff --git a/packages/modules/devices/generic/modbus/config.py b/packages/modules/devices/generic/modbus/config.py index 229858aba5..4aa902960a 100644 --- a/packages/modules/devices/generic/modbus/config.py +++ b/packages/modules/devices/generic/modbus/config.py @@ -2,9 +2,7 @@ from modules.common.component_setup import ComponentSetup from ..vendor import vendor_descriptor -from typing import Optional from dataclasses import dataclass -from modules.common.modbus import ModbusDataType, Endian class GenericModbusConfiguration: diff --git a/packages/modules/devices/generic/modbus/counter.py b/packages/modules/devices/generic/modbus/counter.py index be8fae831c..fcb55f9ac5 100644 --- a/packages/modules/devices/generic/modbus/counter.py +++ b/packages/modules/devices/generic/modbus/counter.py @@ -12,7 +12,7 @@ from modules.common.store import get_counter_value_store -from modules.common.modbus import ModbusDataType, Endian, ModbusTcpClient_ +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ import logging log = logging.getLogger(__name__) diff --git a/packages/modules/devices/generic/modbus/device.py b/packages/modules/devices/generic/modbus/device.py index a26f36f5b1..2682b70110 100644 --- a/packages/modules/devices/generic/modbus/device.py +++ b/packages/modules/devices/generic/modbus/device.py @@ -2,17 +2,19 @@ from typing import Iterable, Union import logging -from helpermodules.broker import BrokerClient -from helpermodules.utils.topic_parser import decode_payload from modules.devices.generic.modbus.bat import GenericModbusBat from modules.devices.generic.modbus.inverter import GenericModbusInverter from modules.common.abstract_device import DeviceDescriptor from modules.common.component_context import SingleComponentUpdateContext -from modules.common.component_type import type_to_topic_mapping from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.devices.generic.modbus.counter import GenericModbusCounter -from modules.devices.generic.modbus.config import GenericModbus, GenericModbusCounterSetup, GenericModbusBatSetup, GenericModbusInverterSetup +from modules.devices.generic.modbus.config import ( + GenericModbus, + GenericModbusCounterSetup, + GenericModbusBatSetup, + GenericModbusInverterSetup, +) from modules.common import modbus diff --git a/packages/modules/devices/generic/modbus/inverter.py b/packages/modules/devices/generic/modbus/inverter.py index d2dc10a2aa..67a377266a 100644 --- a/packages/modules/devices/generic/modbus/inverter.py +++ b/packages/modules/devices/generic/modbus/inverter.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 from typing import TypedDict, Any -from modules.common.modbus import Endian - from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor From adc6fed97d7a513efcc2ab571e16d6b3d9f02a73 Mon Sep 17 00:00:00 2001 From: Alexander Hartung Date: Thu, 23 Jul 2026 11:05:55 +0200 Subject: [PATCH 3/7] fix --- packages/modules/devices/generic/modbus/device.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/modules/devices/generic/modbus/device.py b/packages/modules/devices/generic/modbus/device.py index 2682b70110..8672a0777e 100644 --- a/packages/modules/devices/generic/modbus/device.py +++ b/packages/modules/devices/generic/modbus/device.py @@ -25,15 +25,12 @@ def create_device(device_config: GenericModbus): client = None def create_counter_component(component_config: GenericModbusCounterSetup): - nonlocal client return GenericModbusCounter(component_config, device_id=device_config.id, client=client) def create_bat_component(component_config: GenericModbusBatSetup): - nonlocal client return GenericModbusBat(component_config, device_id=device_config.id, client=client) def create_inverter_component(component_config: GenericModbusInverterSetup): - nonlocal client return GenericModbusInverter(component_config, device_id=device_config.id, client=client) def update_components(components: Iterable[Union[GenericModbusCounter, GenericModbusBat, GenericModbusInverter]]): From c1986d7d26d8f14c1fa2e531d0e96466a8b9a0ee Mon Sep 17 00:00:00 2001 From: Alexander Hartung Date: Thu, 23 Jul 2026 11:43:09 +0200 Subject: [PATCH 4/7] Fix imports --- packages/modules/devices/generic/modbus/bat.py | 4 ++-- packages/modules/devices/generic/modbus/counter.py | 4 ++-- packages/modules/devices/generic/modbus/inverter.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/modules/devices/generic/modbus/bat.py b/packages/modules/devices/generic/modbus/bat.py index 883a584550..2e3a6ab76f 100644 --- a/packages/modules/devices/generic/modbus/bat.py +++ b/packages/modules/devices/generic/modbus/bat.py @@ -6,7 +6,7 @@ from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState from modules.common.modbus import ModbusDataType, ModbusTcpClient_ -from modules.common.store import get_bat_value_store +from modules.common.store._battery import get_bat_value_store from modules.devices.generic.modbus.config import GenericModbusBatSetup from modules.common.utils.peak_filter import PeakFilter from modules.common.component_type import ComponentType @@ -31,7 +31,7 @@ def initialize(self) -> None: self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self.peak_filter = PeakFilter(ComponentType.BAT, self.component_config.id, self.fault_state) - self.sim_counter = SimCounter(self.__modbus_id, self.component_config.id, prefix="speicher") + self.sim_counter = SimCounter(self.__modbus_id, self.component_config.id, self.component_config.type) def update(self) -> None: diff --git a/packages/modules/devices/generic/modbus/counter.py b/packages/modules/devices/generic/modbus/counter.py index fcb55f9ac5..6b12c89c7d 100644 --- a/packages/modules/devices/generic/modbus/counter.py +++ b/packages/modules/devices/generic/modbus/counter.py @@ -10,7 +10,7 @@ from modules.common.utils.peak_filter import PeakFilter from modules.common.component_type import ComponentType -from modules.common.store import get_counter_value_store +from modules.common.store._counter import get_counter_value_store from modules.common.modbus import ModbusDataType, ModbusTcpClient_ @@ -31,7 +31,7 @@ def __init__(self, component_config: GenericModbusCounterSetup, **kwargs: Any) - def initialize(self) -> None: self.__device_id: int = self.kwargs['device_id'] self.client: ModbusTcpClient_ = self.kwargs['client'] - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, self.component_config.type) self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self.peak_filter = PeakFilter(ComponentType.COUNTER, self.component_config.id, self.fault_state) diff --git a/packages/modules/devices/generic/modbus/inverter.py b/packages/modules/devices/generic/modbus/inverter.py index 67a377266a..94ed28fcbb 100644 --- a/packages/modules/devices/generic/modbus/inverter.py +++ b/packages/modules/devices/generic/modbus/inverter.py @@ -7,7 +7,7 @@ from modules.common.fault_state import ComponentInfo, FaultState from modules.common.utils.peak_filter import PeakFilter from modules.common.modbus import ModbusDataType, ModbusTcpClient_ -from modules.common.store import get_inverter_value_store +from modules.common.store._inverter import get_inverter_value_store from modules.devices.generic.modbus.config import GenericModbusInverterSetup from modules.common.component_type import ComponentType from modules.common.simcount import SimCounter @@ -30,7 +30,7 @@ def initialize(self) -> None: self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self.peak_filter = PeakFilter(ComponentType.INVERTER, self.component_config.id, self.fault_state) - self.sim_counter = SimCounter(self.kwargs['device_id'], self.component_config.id, prefix="pv") + self.sim_counter = SimCounter(self.kwargs['device_id'], self.component_config.id, self.component_config.type) def update(self) -> None: unit = self.component_config.configuration.modbus_id From ea2c51c186a5198b18d2b262d86c52f62219ba24 Mon Sep 17 00:00:00 2001 From: Alexander Hartung Date: Thu, 23 Jul 2026 14:47:28 +0200 Subject: [PATCH 5/7] Add suggestions and refactor --- .../modules/devices/generic/modbus/bat.py | 118 +++------ .../modules/devices/generic/modbus/config.py | 177 ++++---------- .../modules/devices/generic/modbus/counter.py | 223 ++++-------------- .../modules/devices/generic/modbus/helper.py | 30 +++ .../devices/generic/modbus/inverter.py | 114 ++------- 5 files changed, 171 insertions(+), 491 deletions(-) diff --git a/packages/modules/devices/generic/modbus/bat.py b/packages/modules/devices/generic/modbus/bat.py index 2e3a6ab76f..de6c97f781 100644 --- a/packages/modules/devices/generic/modbus/bat.py +++ b/packages/modules/devices/generic/modbus/bat.py @@ -5,14 +5,14 @@ from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType, ModbusTcpClient_ -from modules.common.store._battery import get_bat_value_store +from modules.common.modbus import ModbusTcpClient_ +from modules.common.store._factory import get_component_value_store from modules.devices.generic.modbus.config import GenericModbusBatSetup from modules.common.utils.peak_filter import PeakFilter from modules.common.component_type import ComponentType from modules.common.simcount import SimCounter -from modules.devices.generic.modbus.helper import check_data +from modules.devices.generic.modbus.helper import read_phase_values, read_value class KwargsDict(TypedDict): @@ -26,123 +26,59 @@ def __init__(self, component_config: GenericModbusBatSetup, **kwargs: Any) -> No self.kwargs: KwargsDict = kwargs def initialize(self) -> None: - self.__modbus_id: int = self.kwargs['device_id'] + self.__device_id: int = self.kwargs['device_id'] self.client: ModbusTcpClient_ = self.kwargs['client'] - self.store = get_bat_value_store(self.component_config.id) + self.store = get_component_value_store(self.component_config.type, self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self.peak_filter = PeakFilter(ComponentType.BAT, self.component_config.id, self.fault_state) - self.sim_counter = SimCounter(self.__modbus_id, self.component_config.id, self.component_config.type) + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, self.component_config.type) def update(self) -> None: unit = self.component_config.configuration.modbus_id + config = self.component_config.configuration # power - # - if self.component_config.configuration.power.reg_address is not None: - check_data(self.component_config.configuration.power) - power = self.client.read_input_registers( - int(self.component_config.configuration.power.reg_address), ModbusDataType[ - self.component_config.configuration.power.reg_type], - byteorder=self.component_config.configuration.power.byteorder, - wordorder=self.component_config.configuration.power.wordorder, unit=unit) - else: + power = read_value(self.client, unit, config.power) + if power is None: raise ValueError("Leistungsregister muss angegeben werden.") # SOC - # - if self.component_config.configuration.soc.reg_address is not None: - check_data(self.component_config.configuration.soc) - soc = self.client.read_input_registers( - int(self.component_config.configuration.soc.reg_address), ModbusDataType[ - self.component_config.configuration.soc.reg_type], - byteorder=self.component_config.configuration.soc.byteorder, - wordorder=self.component_config.configuration.soc.wordorder, unit=unit) + soc_value = read_value(self.client, unit, config.soc) + if soc_value is not None: + soc = soc_value # currents - # - if (self.component_config.configuration.current_L1.reg_address is not None or - self.component_config.configuration.current_L2.reg_address is not None or - self.component_config.configuration.current_L3.reg_address is not None): - # mind. ein register angegeben - currents = [0.0]*3 - if self.component_config.configuration.current_L1.reg_address is not None: - check_data(self.component_config.configuration.current_L1) - currents[0] = self.client.read_input_registers( - int(self.component_config.configuration.current_L1.reg_address), - ModbusDataType[self.component_config.configuration.current_L1.reg_type], - byteorder=self.component_config.configuration.current_L1.byteorder, - wordorder=self.component_config.configuration.current_L1.wordorder, unit=unit) - - if self.component_config.configuration.current_L2.reg_address is not None: - check_data(self.component_config.configuration.current_L2) - currents[1] = self.client.read_input_registers( - int(self.component_config.configuration.current_L2.reg_address), - ModbusDataType[self.component_config.configuration.current_L2.reg_type], - byteorder=self.component_config.configuration.current_L2.byteorder, - wordorder=self.component_config.configuration.current_L2.wordorder, unit=unit) - - if self.component_config.configuration.current_L3.reg_address is not None: - check_data(self.component_config.configuration.current_L3) - currents[2] = self.client.read_input_registers( - int(self.component_config.configuration.current_L3.reg_address), - ModbusDataType[self.component_config.configuration.current_L3.reg_type], - byteorder=self.component_config.configuration.current_L3.byteorder, - wordorder=self.component_config.configuration.current_L3.wordorder, unit=unit) + currents_value = read_phase_values(self.client, unit, config.current_L1, config.current_L2, config.current_L3) + if currents_value is not None: + currents = currents_value # Import - # - if self.component_config.configuration.imported.reg_address is not None: - check_data(self.component_config.configuration.imported) - imported = self.client.read_input_registers( - int(self.component_config.configuration.imported.reg_address), ModbusDataType[ - self.component_config.configuration.imported.reg_type], - byteorder=self.component_config.configuration.imported.byteorder, - wordorder=self.component_config.configuration.imported.wordorder, unit=unit) - else: - imported = None + imported = read_value(self.client, unit, config.imported) # Export - # - if self.component_config.configuration.exported.reg_address is not None: - check_data(self.component_config.configuration.exported) - exported = self.client.read_input_registers( - int(self.component_config.configuration.exported.reg_address), ModbusDataType[ - self.component_config.configuration.exported.reg_type], - byteorder=self.component_config.configuration.exported.byteorder, - wordorder=self.component_config.configuration.exported.wordorder, unit=unit) - else: - exported = None + exported = read_value(self.client, unit, config.exported) # Serial Number - # - if self.component_config.configuration.serial_number.reg_address is not None: - check_data(self.component_config.configuration.serial_number) - serial_number = self.client.read_input_registers( - int(self.component_config.configuration.serial_number.reg_address), ModbusDataType[ - self.component_config.configuration.serial_number.reg_type], - byteorder=self.component_config.configuration.serial_number.byteorder, - wordorder=self.component_config.configuration.serial_number.wordorder, unit=unit) - - if power is not None: + serial_number_value = read_value(self.client, unit, config.serial_number) + if serial_number_value is not None: + serial_number = serial_number_value + + if imported is None or exported is None: self.peak_filter.check_values(power) - if imported is None or exported is None: - imported, exported = self.sim_counter.sim_count(power) + imported, exported = self.sim_counter.sim_count(power) + else: imported, exported = self.peak_filter.check_values(power, imported, exported) bat_state = BatState( imported=imported, exported=exported, power=power, + soc=soc if "soc" in locals() else None, + currents=currents if "currents" in locals() else None, + serial_number=serial_number if "serial_number" in locals() else None, ) - if "soc" in locals(): - bat_state.soc = soc - if "currents" in locals(): - bat_state.currents = currents - if 'serial_number' in locals(): - bat_state.serial_number = serial_number - self.store.set(bat_state) diff --git a/packages/modules/devices/generic/modbus/config.py b/packages/modules/devices/generic/modbus/config.py index 4aa902960a..c1237e6e6c 100644 --- a/packages/modules/devices/generic/modbus/config.py +++ b/packages/modules/devices/generic/modbus/config.py @@ -1,12 +1,13 @@ #!/usr/bin/env python3 from modules.common.component_setup import ComponentSetup +from typing import Optional from ..vendor import vendor_descriptor -from dataclasses import dataclass +from dataclasses import dataclass, field class GenericModbusConfiguration: - def __init__(self, ip_address: str = "192.168.1.230", port: int = 502): + def __init__(self, ip_address: Optional[str] = None, port: int = 502): self.ip_address = ip_address self.port = port @@ -25,75 +26,33 @@ def __init__(self, @dataclass -class gerneric_modbus: - reg_address: int - reg_type: str - byteorder: str - wordorder: str +class RegisterConfig: + reg_address: Optional[int] = None + reg_type: Optional[str] = None + byteorder: Optional[str] = None + wordorder: Optional[str] = None +@dataclass class GenericModbusCounterConfiguration: - def __init__(self, modbus_id: int = 105, - voltage_L1: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - voltage_L2: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - voltage_L3: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - current_L1: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - current_L2: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - current_L3: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - powers_L1: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - powers_L2: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - powers_L3: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - power_factor_L1: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - power_factor_L2: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - power_factor_L3: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - imported: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - exported: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - power: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - frequency: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - serial_number: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None)): - - self.modbus_id = modbus_id - self.voltage_L1 = voltage_L1 - self.voltage_L2 = voltage_L2 - self.voltage_L3 = voltage_L3 - - self.current_L1 = current_L1 - self.current_L2 = current_L2 - self.current_L3 = current_L3 - - self.powers_L1 = powers_L1 - self.powers_L2 = powers_L2 - self.powers_L3 = powers_L3 - - self.power_factor_L1 = power_factor_L1 - self.power_factor_L2 = power_factor_L2 - self.power_factor_L3 = power_factor_L3 - - self.imported = imported - self.exported = exported - - self.power = power - - self.frequency = frequency - - self.serial_number = serial_number + modbus_id: int = 105 + voltage_L1: RegisterConfig = field(default_factory=RegisterConfig) + voltage_L2: RegisterConfig = field(default_factory=RegisterConfig) + voltage_L3: RegisterConfig = field(default_factory=RegisterConfig) + current_L1: RegisterConfig = field(default_factory=RegisterConfig) + current_L2: RegisterConfig = field(default_factory=RegisterConfig) + current_L3: RegisterConfig = field(default_factory=RegisterConfig) + powers_L1: RegisterConfig = field(default_factory=RegisterConfig) + powers_L2: RegisterConfig = field(default_factory=RegisterConfig) + powers_L3: RegisterConfig = field(default_factory=RegisterConfig) + power_factor_L1: RegisterConfig = field(default_factory=RegisterConfig) + power_factor_L2: RegisterConfig = field(default_factory=RegisterConfig) + power_factor_L3: RegisterConfig = field(default_factory=RegisterConfig) + imported: RegisterConfig = field(default_factory=RegisterConfig) + exported: RegisterConfig = field(default_factory=RegisterConfig) + power: RegisterConfig = field(default_factory=RegisterConfig) + frequency: RegisterConfig = field(default_factory=RegisterConfig) + serial_number: RegisterConfig = field(default_factory=RegisterConfig) class GenericModbusCounterSetup(ComponentSetup[GenericModbusCounterConfiguration]): @@ -105,38 +64,17 @@ def __init__(self, super().__init__(name, type, id, configuration or GenericModbusCounterConfiguration()) +@dataclass class GenericModbusBatConfiguration: - def __init__(self, modbus_id: int = 100, - current_L1: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - current_L2: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - current_L3: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - imported: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - exported: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - power: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - soc: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - serial_number: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None)): - - self.modbus_id = modbus_id - - self.current_L1 = current_L1 - self.current_L2 = current_L2 - self.current_L3 = current_L3 - - self.imported = imported - self.exported = exported - - self.power = power - self.soc = soc - - self.serial_number = serial_number + modbus_id: int = 100 + current_L1: RegisterConfig = field(default_factory=RegisterConfig) + current_L2: RegisterConfig = field(default_factory=RegisterConfig) + current_L3: RegisterConfig = field(default_factory=RegisterConfig) + imported: RegisterConfig = field(default_factory=RegisterConfig) + exported: RegisterConfig = field(default_factory=RegisterConfig) + power: RegisterConfig = field(default_factory=RegisterConfig) + soc: RegisterConfig = field(default_factory=RegisterConfig) + serial_number: RegisterConfig = field(default_factory=RegisterConfig) class GenericModbusBatSetup(ComponentSetup[GenericModbusBatConfiguration]): @@ -148,38 +86,17 @@ def __init__(self, super().__init__(name, type, id, configuration or GenericModbusBatConfiguration()) +@dataclass class GenericModbusInverterConfiguration: - def __init__(self, modbus_id: int = 100, - current_L1: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - current_L2: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - current_L3: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - imported: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - exported: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - power: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - dc_power: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None), - serial_number: gerneric_modbus = gerneric_modbus( - reg_address=None, reg_type=None, byteorder=None, wordorder=None)): - - self.modbus_id = modbus_id - - self.current_L1 = current_L1 - self.current_L2 = current_L2 - self.current_L3 = current_L3 - - self.imported = imported - self.exported = exported - - self.power = power - self.dc_power = dc_power - - self.serial_number = serial_number + modbus_id: int = 100 + current_L1: RegisterConfig = field(default_factory=RegisterConfig) + current_L2: RegisterConfig = field(default_factory=RegisterConfig) + current_L3: RegisterConfig = field(default_factory=RegisterConfig) + imported: RegisterConfig = field(default_factory=RegisterConfig) + exported: RegisterConfig = field(default_factory=RegisterConfig) + power: RegisterConfig = field(default_factory=RegisterConfig) + dc_power: RegisterConfig = field(default_factory=RegisterConfig) + serial_number: RegisterConfig = field(default_factory=RegisterConfig) class GenericModbusInverterSetup(ComponentSetup[GenericModbusInverterConfiguration]): diff --git a/packages/modules/devices/generic/modbus/counter.py b/packages/modules/devices/generic/modbus/counter.py index 6b12c89c7d..d8d8b85de5 100644 --- a/packages/modules/devices/generic/modbus/counter.py +++ b/packages/modules/devices/generic/modbus/counter.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 from typing import Any, TypedDict -from modules.devices.generic.modbus.helper import check_data +from modules.devices.generic.modbus.helper import read_phase_values, read_value from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.fault_state import ComponentInfo, FaultState @@ -10,9 +10,9 @@ from modules.common.utils.peak_filter import PeakFilter from modules.common.component_type import ComponentType -from modules.common.store._counter import get_counter_value_store +from modules.common.store._factory import get_component_value_store -from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.modbus import ModbusTcpClient_ import logging log = logging.getLogger(__name__) @@ -32,192 +32,60 @@ def initialize(self) -> None: self.__device_id: int = self.kwargs['device_id'] self.client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, self.component_config.type) - self.store = get_counter_value_store(self.component_config.id) + self.store = get_component_value_store(self.component_config.type, self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self.peak_filter = PeakFilter(ComponentType.COUNTER, self.component_config.id, self.fault_state) def update(self) -> None: unit = self.component_config.configuration.modbus_id + config = self.component_config.configuration # Power - # - if self.component_config.configuration.power.reg_address is not None: - check_data(self.component_config.configuration.power) - power = self.client.read_input_registers( - int(self.component_config.configuration.power.reg_address), ModbusDataType[ - self.component_config.configuration.power.reg_type], - byteorder=self.component_config.configuration.power.byteorder, - wordorder=self.component_config.configuration.power.wordorder, unit=unit) - else: + power = read_value(self.client, unit, config.power) + if power is None: raise ValueError("Leistungsregister muss angegeben werden.") # Voltages - # - if (self.component_config.configuration.voltage_L1.reg_address is not None or - self.component_config.configuration.voltage_L2.reg_address is not None or - self.component_config.configuration.voltage_L3.reg_address is not None): - # Kein register angegeben - voltages = [0.0]*3 - if self.component_config.configuration.voltage_L1.reg_address is not None: - check_data(self.component_config.configuration.voltage_L1) - voltages[0] = self.client.read_input_registers( - int(self.component_config.configuration.voltage_L1.reg_address), - ModbusDataType[self.component_config.configuration.voltage_L1.reg_type], - byteorder=self.component_config.configuration.voltage_L1.byteorder, - wordorder=self.component_config.configuration.voltage_L1.wordorder, unit=unit) - - if self.component_config.configuration.voltage_L2.reg_address is not None: - check_data(self.component_config.configuration.voltage_L2) - voltages[1] = self.client.read_input_registers( - int(self.component_config.configuration.voltage_L2.reg_address), - ModbusDataType[self.component_config.configuration.voltage_L2.reg_type], - byteorder=self.component_config.configuration.voltage_L2.byteorder, - wordorder=self.component_config.configuration.voltage_L2.wordorder, unit=unit) - - if self.component_config.configuration.voltage_L3.reg_address is not None: - check_data(self.component_config.configuration.voltage_L3) - voltages[2] = self.client.read_input_registers( - int(self.component_config.configuration.voltage_L3.reg_address), - ModbusDataType[self.component_config.configuration.voltage_L3.reg_type], - byteorder=self.component_config.configuration.voltage_L3.byteorder, - wordorder=self.component_config.configuration.voltage_L3.wordorder, unit=unit) + voltages_value = read_phase_values(self.client, unit, config.voltage_L1, config.voltage_L2, config.voltage_L3) + if voltages_value is not None: + voltages = voltages_value # Currents - # - if (self.component_config.configuration.current_L1.reg_address is not None or - self.component_config.configuration.current_L2.reg_address is not None or - self.component_config.configuration.current_L3.reg_address is not None): - # mind. ein register angegeben - currents = [0.0]*3 - if self.component_config.configuration.current_L1.reg_address is not None: - check_data(self.component_config.configuration.current_L1) - currents[0] = self.client.read_input_registers( - int(self.component_config.configuration.current_L1.reg_address), - ModbusDataType[self.component_config.configuration.current_L1.reg_type], - byteorder=self.component_config.configuration.current_L1.byteorder, - wordorder=self.component_config.configuration.current_L1.wordorder, unit=unit) - - if self.component_config.configuration.current_L2.reg_address is not None: - check_data(self.component_config.configuration.current_L2) - currents[1] = self.client.read_input_registers( - int(self.component_config.configuration.current_L2.reg_address), - ModbusDataType[self.component_config.configuration.current_L2.reg_type], - byteorder=self.component_config.configuration.current_L2.byteorder, - wordorder=self.component_config.configuration.current_L2.wordorder, unit=unit) - - if self.component_config.configuration.current_L3.reg_address is not None: - check_data(self.component_config.configuration.current_L3) - currents[2] = self.client.read_input_registers( - int(self.component_config.configuration.current_L3.reg_address), - ModbusDataType[self.component_config.configuration.current_L3.reg_type], - byteorder=self.component_config.configuration.current_L3.byteorder, - wordorder=self.component_config.configuration.current_L3.wordorder, unit=unit) + currents_value = read_phase_values(self.client, unit, config.current_L1, config.current_L2, config.current_L3) + if currents_value is not None: + currents = currents_value # Powers - # - if (self.component_config.configuration.powers_L1.reg_address is not None or - self.component_config.configuration.powers_L2.reg_address is not None or - self.component_config.configuration.powers_L3.reg_address is not None): - # mind. ein register angegeben - powers = [0.0]*3 - if self.component_config.configuration.powers_L1.reg_address is not None: - check_data(self.component_config.configuration.powers_L1) - powers[0] = self.client.read_input_registers( - int(self.component_config.configuration.powers_L1.reg_address), - ModbusDataType[self.component_config.configuration.powers_L1.reg_type], - byteorder=self.component_config.configuration.powers_L1.byteorder, - wordorder=self.component_config.configuration.powers_L1.wordorder, unit=unit) - - if self.component_config.configuration.powers_L2.reg_address is not None: - check_data(self.component_config.configuration.powers_L2) - powers[1] = self.client.read_input_registers( - int(self.component_config.configuration.powers_L2.reg_address), - ModbusDataType[self.component_config.configuration.powers_L2.reg_type], - byteorder=self.component_config.configuration.powers_L2.byteorder, - wordorder=self.component_config.configuration.powers_L2.wordorder, unit=unit) - - if self.component_config.configuration.powers_L3.reg_address is not None: - check_data(self.component_config.configuration.powers_L3) - powers[2] = self.client.read_input_registers( - int(self.component_config.configuration.powers_L3.reg_address), - ModbusDataType[self.component_config.configuration.powers_L3.reg_type], - byteorder=self.component_config.configuration.powers_L3.byteorder, - wordorder=self.component_config.configuration.powers_L3.wordorder, unit=unit) - - # Power Factors‚ - # - if (self.component_config.configuration.power_factor_L1.reg_address is not None or - self.component_config.configuration.power_factor_L2.reg_address is not None or - self.component_config.configuration.power_factor_L3.reg_address is not None): - # mind. ein register angegeben - power_factors = [0.0]*3 - if self.component_config.configuration.power_factor_L1.reg_address is not None: - check_data(self.component_config.configuration.power_factor_L1) - power_factors[0] = self.client.read_input_registers( - int(self.component_config.configuration.power_factor_L1.reg_address), - ModbusDataType[self.component_config.configuration.power_factor_L1.reg_type], - byteorder=self.component_config.configuration.power_factor_L1.byteorder, - wordorder=self.component_config.configuration.power_factor_L1.wordorder, unit=unit) - - if self.component_config.configuration.power_factor_L2.reg_address is not None: - check_data(self.component_config.configuration.power_factor_L2) - power_factors[1] = self.client.read_input_registers( - int(self.component_config.configuration.power_factor_L2.reg_address), - ModbusDataType[self.component_config.configuration.power_factor_L2.reg_type], - byteorder=self.component_config.configuration.power_factor_L2.byteorder, - wordorder=self.component_config.configuration.power_factor_L2.wordorder, unit=unit) - - if self.component_config.configuration.power_factor_L3.reg_address is not None: - check_data(self.component_config.configuration.power_factor_L3) - power_factors[2] = self.client.read_input_registers( - int(self.component_config.configuration.power_factor_L3.reg_address), - ModbusDataType[self.component_config.configuration.power_factor_L3.reg_type], - byteorder=self.component_config.configuration.power_factor_L3.byteorder, - wordorder=self.component_config.configuration.power_factor_L3.wordorder, unit=unit) + powers_value = read_phase_values(self.client, unit, config.powers_L1, config.powers_L2, config.powers_L3) + if powers_value is not None: + powers = powers_value + + # Power Factors + power_factors_value = read_phase_values( + self.client, + unit, + config.power_factor_L1, + config.power_factor_L2, + config.power_factor_L3, + ) + if power_factors_value is not None: + power_factors = power_factors_value # Frequency - # - if self.component_config.configuration.frequency.reg_address is not None: - check_data(self.component_config.configuration.frequency) - frequency = self.client.read_input_registers( - int(self.component_config.configuration.frequency.reg_address), ModbusDataType[ - self.component_config.configuration.frequency.reg_type], - byteorder=self.component_config.configuration.frequency.byteorder, - wordorder=self.component_config.configuration.frequency.wordorder, unit=unit) + frequency_value = read_value(self.client, unit, config.frequency) + if frequency_value is not None: + frequency = frequency_value # Imported - # - if self.component_config.configuration.imported.reg_address is not None: - check_data(self.component_config.configuration.imported) - imported = self.client.read_input_registers( - int(self.component_config.configuration.imported.reg_address), ModbusDataType[ - self.component_config.configuration.imported.reg_type], - byteorder=self.component_config.configuration.imported.byteorder, - wordorder=self.component_config.configuration.imported.wordorder, unit=unit) - else: - imported = None + imported = read_value(self.client, unit, config.imported) # Exported - # - if self.component_config.configuration.exported.reg_address is not None: - check_data(self.component_config.configuration.exported) - exported = self.client.read_input_registers( - int(self.component_config.configuration.exported.reg_address), ModbusDataType[ - self.component_config.configuration.exported.reg_type], - byteorder=self.component_config.configuration.exported.byteorder, - wordorder=self.component_config.configuration.exported.wordorder, unit=unit) - else: - exported = None + exported = read_value(self.client, unit, config.exported) # Serial Number - # - if self.component_config.configuration.serial_number.reg_address is not None: - check_data(self.component_config.configuration.serial_number) - serial_number = self.client.read_input_registers( - int(self.component_config.configuration.serial_number.reg_address), ModbusDataType[ - self.component_config.configuration.serial_number.reg_type], - byteorder=self.component_config.configuration.serial_number.byteorder, - wordorder=self.component_config.configuration.serial_number.wordorder, unit=unit) + serial_number_value = read_value(self.client, unit, config.serial_number) + if serial_number_value is not None: + serial_number = serial_number_value if power is not None: self.peak_filter.check_values(power) @@ -228,22 +96,15 @@ def update(self) -> None: counter_state = CounterState( imported=imported, exported=exported, - power=power + power=power, + voltages=voltages if "voltages" in locals() else None, + currents=currents if "currents" in locals() else None, + powers=powers if "powers" in locals() else None, + power_factors=power_factors if "power_factors" in locals() else None, + frequency=frequency if "frequency" in locals() else 50, + serial_number=serial_number if "serial_number" in locals() else None, ) - if "voltages" in locals(): - counter_state.voltages = voltages - if "currents" in locals(): - counter_state.currents = currents - if 'powers' in locals(): - counter_state.powers = powers - if "power_factors" in locals(): - counter_state.power_factors = power_factors - if 'frequency' in locals(): - counter_state.frequency = frequency - if 'serial_number' in locals(): - counter_state.serial_number = serial_number - self.store.set(counter_state) log.debug(f"CounterState updated: {counter_state}") diff --git a/packages/modules/devices/generic/modbus/helper.py b/packages/modules/devices/generic/modbus/helper.py index 8f0f2d0ce6..5cc2b7ab63 100644 --- a/packages/modules/devices/generic/modbus/helper.py +++ b/packages/modules/devices/generic/modbus/helper.py @@ -1,6 +1,36 @@ +from typing import Any + +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ + + def check_data(wert): if (wert.reg_type is None or wert.byteorder is None or wert.wordorder is None): raise ValueError( f"Unvollständige Konfiguration für Universeller-Modbus: Register-Adresse {wert.reg_address}") + + +def read_value(client: ModbusTcpClient_, unit: int, register_config: Any) -> Any: + if register_config.reg_address is None: + return None + + check_data(register_config) + return client.read_input_registers( + register_config.reg_address, + ModbusDataType[register_config.reg_type], + byteorder=register_config.byteorder, + wordorder=register_config.wordorder, + unit=unit, + ) + + +def read_phase_values(client: ModbusTcpClient_, unit: int, *register_configs: Any) -> Any: + values = [0.0] * 3 + has_value = False + for index, register_config in enumerate(register_configs): + value = read_value(client, unit, register_config) + if value is not None: + values[index] = value + has_value = True + return values if has_value else None diff --git a/packages/modules/devices/generic/modbus/inverter.py b/packages/modules/devices/generic/modbus/inverter.py index 94ed28fcbb..414399276f 100644 --- a/packages/modules/devices/generic/modbus/inverter.py +++ b/packages/modules/devices/generic/modbus/inverter.py @@ -6,13 +6,13 @@ from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState from modules.common.utils.peak_filter import PeakFilter -from modules.common.modbus import ModbusDataType, ModbusTcpClient_ -from modules.common.store._inverter import get_inverter_value_store +from modules.common.modbus import ModbusTcpClient_ +from modules.common.store._factory import get_component_value_store from modules.devices.generic.modbus.config import GenericModbusInverterSetup from modules.common.component_type import ComponentType from modules.common.simcount import SimCounter -from modules.devices.generic.modbus.helper import check_data +from modules.devices.generic.modbus.helper import read_phase_values, read_value class KwargsDict(TypedDict): @@ -27,120 +27,56 @@ def __init__(self, component_config: GenericModbusInverterSetup, **kwargs: Any) def initialize(self) -> None: self.client: ModbusTcpClient_ = self.kwargs['client'] - self.store = get_inverter_value_store(self.component_config.id) + self.store = get_component_value_store(self.component_config.type, self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self.peak_filter = PeakFilter(ComponentType.INVERTER, self.component_config.id, self.fault_state) self.sim_counter = SimCounter(self.kwargs['device_id'], self.component_config.id, self.component_config.type) def update(self) -> None: unit = self.component_config.configuration.modbus_id + config = self.component_config.configuration # Power - # - if self.component_config.configuration.power.reg_address is not None: - check_data(self.component_config.configuration.power) - power = self.client.read_input_registers( - int(self.component_config.configuration.power.reg_address), ModbusDataType[ - self.component_config.configuration.power.reg_type], - byteorder=self.component_config.configuration.power.byteorder, - wordorder=self.component_config.configuration.power.wordorder, unit=unit) - else: + power = read_value(self.client, unit, config.power) + if power is None: raise ValueError("Leistungsregister muss angegeben werden.") # Exported - # - if self.component_config.configuration.exported.reg_address is not None: - check_data(self.component_config.configuration.exported) - exported = self.client.read_input_registers( - int(self.component_config.configuration.exported.reg_address), ModbusDataType[ - self.component_config.configuration.exported.reg_type], - byteorder=self.component_config.configuration.exported.byteorder, - wordorder=self.component_config.configuration.exported.wordorder, unit=unit) - else: - exported = None + exported = read_value(self.client, unit, config.exported) # Imported - # - if self.component_config.configuration.imported.reg_address is not None: - check_data(self.component_config.configuration.imported) - imported = self.client.read_input_registers( - int(self.component_config.configuration.imported.reg_address), ModbusDataType[ - self.component_config.configuration.imported.reg_type], - byteorder=self.component_config.configuration.imported.byteorder, - wordorder=self.component_config.configuration.imported.wordorder, unit=unit) - else: - imported = None + imported = read_value(self.client, unit, config.imported) # Currents - # - if (self.component_config.configuration.current_L1.reg_address is not None or - self.component_config.configuration.current_L2.reg_address is not None or - self.component_config.configuration.current_L3.reg_address is not None): - # mind. ein register angegeben - currents = [0.0]*3 - if self.component_config.configuration.current_L1.reg_address is not None: - check_data(self.component_config.configuration.current_L1) - currents[0] = self.client.read_input_registers( - int(self.component_config.configuration.current_L1.reg_address), ModbusDataType[ - self.component_config.configuration.current_L1.reg_type], - byteorder=self.component_config.configuration.current_L1.byteorder, - wordorder=self.component_config.configuration.current_L1.wordorder, unit=unit) - - if self.component_config.configuration.current_L2.reg_address is not None: - check_data(self.component_config.configuration.current_L2) - currents[1] = self.client.read_input_registers( - int(self.component_config.configuration.current_L2.reg_address), ModbusDataType[ - self.component_config.configuration.current_L2.reg_type], - byteorder=self.component_config.configuration.current_L2.byteorder, - wordorder=self.component_config.configuration.current_L2.wordorder, unit=unit) - - if self.component_config.configuration.current_L3.reg_address is not None: - check_data(self.component_config.configuration.current_L3) - currents[2] = self.client.read_input_registers( - int(self.component_config.configuration.current_L3.reg_address), ModbusDataType[ - self.component_config.configuration.current_L3.reg_type], - byteorder=self.component_config.configuration.current_L3.byteorder, - wordorder=self.component_config.configuration.current_L3.wordorder, unit=unit) + currents_value = read_phase_values(self.client, unit, config.current_L1, config.current_L2, config.current_L3) + if currents_value is not None: + currents = currents_value # DC Power - # - if self.component_config.configuration.dc_power.reg_address is not None: - check_data(self.component_config.configuration.dc_power) - dc_power = self.client.read_input_registers( - int(self.component_config.configuration.dc_power.reg_address), ModbusDataType[ - self.component_config.configuration.dc_power.reg_type], - byteorder=self.component_config.configuration.dc_power.byteorder, - wordorder=self.component_config.configuration.dc_power.wordorder, unit=unit) + dc_power_value = read_value(self.client, unit, config.dc_power) + if dc_power_value is not None: + dc_power = dc_power_value # Serial Number - # - if self.component_config.configuration.serial_number.reg_address is not None: - check_data(self.component_config.configuration.serial_number) - serial_number = self.client.read_input_registers( - int(self.component_config.configuration.serial_number.reg_address), ModbusDataType[ - self.component_config.configuration.serial_number.reg_type], - byteorder=self.component_config.configuration.serial_number.byteorder, - wordorder=self.component_config.configuration.serial_number.wordorder, unit=unit) - - if power is not None: + serial_number_value = read_value(self.client, unit, config.serial_number) + if serial_number_value is not None: + serial_number = serial_number_value + + if imported is None or exported is None: self.peak_filter.check_values(power) - if imported is None or exported is None: - imported, exported = self.sim_counter.sim_count(power) + imported, exported = self.sim_counter.sim_count(power) + else: imported, exported = self.peak_filter.check_values(power, imported, exported) inverter_state = InverterState( power=power, exported=exported, imported=imported, + dc_power=dc_power if "dc_power" in locals() else None, + currents=currents if "currents" in locals() else None, + serial_number=serial_number if "serial_number" in locals() else None, ) - if "dc_power" in locals(): - inverter_state.dc_power = dc_power - if "currents" in locals(): - inverter_state.currents = currents - if 'serial_number' in locals(): - inverter_state.serial_number = serial_number - self.store.set(inverter_state) From 39f9aa59822db5519b9f6af1cba312215c9b2be5 Mon Sep 17 00:00:00 2001 From: Alexander Hartung Date: Fri, 24 Jul 2026 09:01:31 +0200 Subject: [PATCH 6/7] Add suggestions --- .../modules/devices/generic/modbus/bat.py | 10 +++++--- .../modules/devices/generic/modbus/counter.py | 23 +++++++++++-------- .../modules/devices/generic/modbus/helper.py | 5 ++-- .../devices/generic/modbus/inverter.py | 10 +++++--- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/packages/modules/devices/generic/modbus/bat.py b/packages/modules/devices/generic/modbus/bat.py index de6c97f781..bb4691abc9 100644 --- a/packages/modules/devices/generic/modbus/bat.py +++ b/packages/modules/devices/generic/modbus/bat.py @@ -74,11 +74,15 @@ def update(self) -> None: imported=imported, exported=exported, power=power, - soc=soc if "soc" in locals() else None, - currents=currents if "currents" in locals() else None, - serial_number=serial_number if "serial_number" in locals() else None, ) + if "soc" in locals(): + bat_state.soc = soc + if "currents" in locals(): + bat_state.currents = currents + if "serial_number" in locals(): + bat_state.serial_number = serial_number + self.store.set(bat_state) diff --git a/packages/modules/devices/generic/modbus/counter.py b/packages/modules/devices/generic/modbus/counter.py index d8d8b85de5..98fd0234ea 100644 --- a/packages/modules/devices/generic/modbus/counter.py +++ b/packages/modules/devices/generic/modbus/counter.py @@ -96,18 +96,23 @@ def update(self) -> None: counter_state = CounterState( imported=imported, exported=exported, - power=power, - voltages=voltages if "voltages" in locals() else None, - currents=currents if "currents" in locals() else None, - powers=powers if "powers" in locals() else None, - power_factors=power_factors if "power_factors" in locals() else None, - frequency=frequency if "frequency" in locals() else 50, - serial_number=serial_number if "serial_number" in locals() else None, + power=power ) - self.store.set(counter_state) + if "voltages" in locals(): + counter_state.voltages = voltages + if "currents" in locals(): + counter_state.currents = currents + if "powers" in locals(): + counter_state.powers = powers + if "power_factors" in locals(): + counter_state.power_factors = power_factors + if "frequency" in locals(): + counter_state.frequency = frequency + if "serial_number" in locals(): + counter_state.serial_number = serial_number - log.debug(f"CounterState updated: {counter_state}") + self.store.set(counter_state) component_descriptor = ComponentDescriptor(configuration_factory=GenericModbusCounterSetup) diff --git a/packages/modules/devices/generic/modbus/helper.py b/packages/modules/devices/generic/modbus/helper.py index 5cc2b7ab63..ed7c3ae68c 100644 --- a/packages/modules/devices/generic/modbus/helper.py +++ b/packages/modules/devices/generic/modbus/helper.py @@ -1,6 +1,7 @@ from typing import Any from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.devices.generic.modbus.config import RegisterConfig def check_data(wert): @@ -11,7 +12,7 @@ def check_data(wert): f"Unvollständige Konfiguration für Universeller-Modbus: Register-Adresse {wert.reg_address}") -def read_value(client: ModbusTcpClient_, unit: int, register_config: Any) -> Any: +def read_value(client: ModbusTcpClient_, unit: int, register_config: RegisterConfig) -> Any: if register_config.reg_address is None: return None @@ -25,7 +26,7 @@ def read_value(client: ModbusTcpClient_, unit: int, register_config: Any) -> Any ) -def read_phase_values(client: ModbusTcpClient_, unit: int, *register_configs: Any) -> Any: +def read_phase_values(client: ModbusTcpClient_, unit: int, *register_configs: RegisterConfig) -> Any: values = [0.0] * 3 has_value = False for index, register_config in enumerate(register_configs): diff --git a/packages/modules/devices/generic/modbus/inverter.py b/packages/modules/devices/generic/modbus/inverter.py index 414399276f..d55cdcb824 100644 --- a/packages/modules/devices/generic/modbus/inverter.py +++ b/packages/modules/devices/generic/modbus/inverter.py @@ -72,11 +72,15 @@ def update(self) -> None: power=power, exported=exported, imported=imported, - dc_power=dc_power if "dc_power" in locals() else None, - currents=currents if "currents" in locals() else None, - serial_number=serial_number if "serial_number" in locals() else None, ) + if "dc_power" in locals(): + inverter_state.dc_power = dc_power + if "currents" in locals(): + inverter_state.currents = currents + if "serial_number" in locals(): + inverter_state.serial_number = serial_number + self.store.set(inverter_state) From 294ebe306c7dd24a136094ad9560271f2d8bf71d Mon Sep 17 00:00:00 2001 From: Alexander Hartung Date: Tue, 28 Jul 2026 07:58:59 +0200 Subject: [PATCH 7/7] Add requested changes --- .../modules/devices/generic/modbus/bat.py | 18 +++++++----- .../modules/devices/generic/modbus/config.py | 15 ++++++---- .../modules/devices/generic/modbus/counter.py | 25 ++++++++++------ .../modules/devices/generic/modbus/device.py | 15 ++++++++-- .../modules/devices/generic/modbus/helper.py | 29 +++++++++++-------- .../devices/generic/modbus/inverter.py | 18 +++++++----- 6 files changed, 76 insertions(+), 44 deletions(-) diff --git a/packages/modules/devices/generic/modbus/bat.py b/packages/modules/devices/generic/modbus/bat.py index bb4691abc9..c38204b6a8 100644 --- a/packages/modules/devices/generic/modbus/bat.py +++ b/packages/modules/devices/generic/modbus/bat.py @@ -7,7 +7,7 @@ from modules.common.fault_state import ComponentInfo, FaultState from modules.common.modbus import ModbusTcpClient_ from modules.common.store._factory import get_component_value_store -from modules.devices.generic.modbus.config import GenericModbusBatSetup +from modules.devices.generic.modbus.config import GenericModbusBatSetup, GenericModbusConfiguration from modules.common.utils.peak_filter import PeakFilter from modules.common.component_type import ComponentType from modules.common.simcount import SimCounter @@ -18,6 +18,7 @@ class KwargsDict(TypedDict): device_id: int client: ModbusTcpClient_ + device_configuration: GenericModbusConfiguration class GenericModbusBat(AbstractBat): @@ -33,34 +34,37 @@ def initialize(self) -> None: self.peak_filter = PeakFilter(ComponentType.BAT, self.component_config.id, self.fault_state) self.sim_counter = SimCounter(self.__device_id, self.component_config.id, self.component_config.type) + self.device_configuration = self.kwargs['device_configuration'] + def update(self) -> None: unit = self.component_config.configuration.modbus_id config = self.component_config.configuration # power - power = read_value(self.client, unit, config.power) + power = read_value(self.client, unit, self.device_configuration, config.power) if power is None: raise ValueError("Leistungsregister muss angegeben werden.") # SOC - soc_value = read_value(self.client, unit, config.soc) + soc_value = read_value(self.client, unit, self.device_configuration, config.soc) if soc_value is not None: soc = soc_value # currents - currents_value = read_phase_values(self.client, unit, config.current_L1, config.current_L2, config.current_L3) + currents_value = read_phase_values(self.client, unit, self.device_configuration, + config.current_L1, config.current_L2, config.current_L3) if currents_value is not None: currents = currents_value # Import - imported = read_value(self.client, unit, config.imported) + imported = read_value(self.client, unit, self.device_configuration, config.imported) # Export - exported = read_value(self.client, unit, config.exported) + exported = read_value(self.client, unit, self.device_configuration, config.exported) # Serial Number - serial_number_value = read_value(self.client, unit, config.serial_number) + serial_number_value = read_value(self.client, unit, self.device_configuration, config.serial_number) if serial_number_value is not None: serial_number = serial_number_value diff --git a/packages/modules/devices/generic/modbus/config.py b/packages/modules/devices/generic/modbus/config.py index c1237e6e6c..6ce3337269 100644 --- a/packages/modules/devices/generic/modbus/config.py +++ b/packages/modules/devices/generic/modbus/config.py @@ -7,9 +7,14 @@ class GenericModbusConfiguration: - def __init__(self, ip_address: Optional[str] = None, port: int = 502): + def __init__(self, ip_address: Optional[str] = None, + port: int = 502, + byteorder: Optional[str] = None, + wordorder: Optional[str] = None) -> None: self.ip_address = ip_address self.port = port + self.byteorder = byteorder + self.wordorder = wordorder class GenericModbus: @@ -29,13 +34,11 @@ def __init__(self, class RegisterConfig: reg_address: Optional[int] = None reg_type: Optional[str] = None - byteorder: Optional[str] = None - wordorder: Optional[str] = None @dataclass class GenericModbusCounterConfiguration: - modbus_id: int = 105 + modbus_id: int = 1 voltage_L1: RegisterConfig = field(default_factory=RegisterConfig) voltage_L2: RegisterConfig = field(default_factory=RegisterConfig) voltage_L3: RegisterConfig = field(default_factory=RegisterConfig) @@ -66,7 +69,7 @@ def __init__(self, @dataclass class GenericModbusBatConfiguration: - modbus_id: int = 100 + modbus_id: int = 1 current_L1: RegisterConfig = field(default_factory=RegisterConfig) current_L2: RegisterConfig = field(default_factory=RegisterConfig) current_L3: RegisterConfig = field(default_factory=RegisterConfig) @@ -88,7 +91,7 @@ def __init__(self, @dataclass class GenericModbusInverterConfiguration: - modbus_id: int = 100 + modbus_id: int = 1 current_L1: RegisterConfig = field(default_factory=RegisterConfig) current_L2: RegisterConfig = field(default_factory=RegisterConfig) current_L3: RegisterConfig = field(default_factory=RegisterConfig) diff --git a/packages/modules/devices/generic/modbus/counter.py b/packages/modules/devices/generic/modbus/counter.py index 98fd0234ea..cb43520a62 100644 --- a/packages/modules/devices/generic/modbus/counter.py +++ b/packages/modules/devices/generic/modbus/counter.py @@ -6,7 +6,7 @@ from modules.common.fault_state import ComponentInfo, FaultState from modules.common.component_type import ComponentDescriptor from modules.common.simcount._simcounter import SimCounter -from modules.devices.generic.modbus.config import GenericModbusCounterSetup +from modules.devices.generic.modbus.config import GenericModbusCounterSetup, GenericModbusConfiguration from modules.common.utils.peak_filter import PeakFilter from modules.common.component_type import ComponentType @@ -21,6 +21,7 @@ class KwargsDict(TypedDict): device_id: int client: ModbusTcpClient_ + device_configuration: GenericModbusConfiguration class GenericModbusCounter(AbstractCounter): @@ -36,27 +37,32 @@ def initialize(self) -> None: self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self.peak_filter = PeakFilter(ComponentType.COUNTER, self.component_config.id, self.fault_state) + self.device_configuration = self.kwargs['device_configuration'] + def update(self) -> None: unit = self.component_config.configuration.modbus_id config = self.component_config.configuration # Power - power = read_value(self.client, unit, config.power) + power = read_value(self.client, unit, self.device_configuration, config.power) if power is None: raise ValueError("Leistungsregister muss angegeben werden.") # Voltages - voltages_value = read_phase_values(self.client, unit, config.voltage_L1, config.voltage_L2, config.voltage_L3) + voltages_value = read_phase_values(self.client, unit, self.device_configuration, + config.voltage_L1, config.voltage_L2, config.voltage_L3) if voltages_value is not None: voltages = voltages_value # Currents - currents_value = read_phase_values(self.client, unit, config.current_L1, config.current_L2, config.current_L3) + currents_value = read_phase_values(self.client, unit, self.device_configuration, + config.current_L1, config.current_L2, config.current_L3) if currents_value is not None: currents = currents_value # Powers - powers_value = read_phase_values(self.client, unit, config.powers_L1, config.powers_L2, config.powers_L3) + powers_value = read_phase_values(self.client, unit, self.device_configuration, + config.powers_L1, config.powers_L2, config.powers_L3) if powers_value is not None: powers = powers_value @@ -64,6 +70,7 @@ def update(self) -> None: power_factors_value = read_phase_values( self.client, unit, + self.device_configuration, config.power_factor_L1, config.power_factor_L2, config.power_factor_L3, @@ -72,18 +79,18 @@ def update(self) -> None: power_factors = power_factors_value # Frequency - frequency_value = read_value(self.client, unit, config.frequency) + frequency_value = read_value(self.client, unit, self.device_configuration, config.frequency) if frequency_value is not None: frequency = frequency_value # Imported - imported = read_value(self.client, unit, config.imported) + imported = read_value(self.client, unit, self.device_configuration, config.imported) # Exported - exported = read_value(self.client, unit, config.exported) + exported = read_value(self.client, unit, self.device_configuration, config.exported) # Serial Number - serial_number_value = read_value(self.client, unit, config.serial_number) + serial_number_value = read_value(self.client, unit, self.device_configuration, config.serial_number) if serial_number_value is not None: serial_number = serial_number_value diff --git a/packages/modules/devices/generic/modbus/device.py b/packages/modules/devices/generic/modbus/device.py index 8672a0777e..573b61c074 100644 --- a/packages/modules/devices/generic/modbus/device.py +++ b/packages/modules/devices/generic/modbus/device.py @@ -25,13 +25,22 @@ def create_device(device_config: GenericModbus): client = None def create_counter_component(component_config: GenericModbusCounterSetup): - return GenericModbusCounter(component_config, device_id=device_config.id, client=client) + return GenericModbusCounter(component_config, + device_id=device_config.id, + client=client, + device_configuration=device_config.configuration) def create_bat_component(component_config: GenericModbusBatSetup): - return GenericModbusBat(component_config, device_id=device_config.id, client=client) + return GenericModbusBat(component_config, + device_id=device_config.id, + client=client, + device_configuration=device_config.configuration) def create_inverter_component(component_config: GenericModbusInverterSetup): - return GenericModbusInverter(component_config, device_id=device_config.id, client=client) + return GenericModbusInverter(component_config, + device_id=device_config.id, + client=client, + device_configuration=device_config.configuration) def update_components(components: Iterable[Union[GenericModbusCounter, GenericModbusBat, GenericModbusInverter]]): for component in components: diff --git a/packages/modules/devices/generic/modbus/helper.py b/packages/modules/devices/generic/modbus/helper.py index ed7c3ae68c..43f6c1f435 100644 --- a/packages/modules/devices/generic/modbus/helper.py +++ b/packages/modules/devices/generic/modbus/helper.py @@ -1,36 +1,41 @@ from typing import Any from modules.common.modbus import ModbusDataType, ModbusTcpClient_ -from modules.devices.generic.modbus.config import RegisterConfig +from modules.devices.generic.modbus.config import RegisterConfig, GenericModbusConfiguration -def check_data(wert): - if (wert.reg_type is None or - wert.byteorder is None or - wert.wordorder is None): +def check_data(register_config: RegisterConfig, device_config: GenericModbusConfiguration): + if (register_config.reg_type is None or + device_config.byteorder is None or + device_config.wordorder is None): raise ValueError( - f"Unvollständige Konfiguration für Universeller-Modbus: Register-Adresse {wert.reg_address}") + f"Unvollständige Konfiguration für Universeller-Modbus: Register-Adresse {register_config.reg_address}") -def read_value(client: ModbusTcpClient_, unit: int, register_config: RegisterConfig) -> Any: +def read_value(client: ModbusTcpClient_, unit: int, + device_config: GenericModbusConfiguration, + register_config: RegisterConfig) -> Any: if register_config.reg_address is None: return None - check_data(register_config) + check_data(register_config, device_config) return client.read_input_registers( register_config.reg_address, ModbusDataType[register_config.reg_type], - byteorder=register_config.byteorder, - wordorder=register_config.wordorder, + byteorder=device_config.byteorder, + wordorder=device_config.wordorder, unit=unit, ) -def read_phase_values(client: ModbusTcpClient_, unit: int, *register_configs: RegisterConfig) -> Any: +def read_phase_values(client: ModbusTcpClient_, + unit: int, + device_config: GenericModbusConfiguration, + *register_configs: RegisterConfig) -> Any: values = [0.0] * 3 has_value = False for index, register_config in enumerate(register_configs): - value = read_value(client, unit, register_config) + value = read_value(client, unit, device_config, register_config) if value is not None: values[index] = value has_value = True diff --git a/packages/modules/devices/generic/modbus/inverter.py b/packages/modules/devices/generic/modbus/inverter.py index d55cdcb824..931b653a48 100644 --- a/packages/modules/devices/generic/modbus/inverter.py +++ b/packages/modules/devices/generic/modbus/inverter.py @@ -8,7 +8,7 @@ from modules.common.utils.peak_filter import PeakFilter from modules.common.modbus import ModbusTcpClient_ from modules.common.store._factory import get_component_value_store -from modules.devices.generic.modbus.config import GenericModbusInverterSetup +from modules.devices.generic.modbus.config import GenericModbusInverterSetup, GenericModbusConfiguration from modules.common.component_type import ComponentType from modules.common.simcount import SimCounter @@ -18,6 +18,7 @@ class KwargsDict(TypedDict): device_id: int client: ModbusTcpClient_ + device_configuration: GenericModbusConfiguration class GenericModbusInverter(AbstractInverter): @@ -32,33 +33,36 @@ def initialize(self) -> None: self.peak_filter = PeakFilter(ComponentType.INVERTER, self.component_config.id, self.fault_state) self.sim_counter = SimCounter(self.kwargs['device_id'], self.component_config.id, self.component_config.type) + self.device_configuration = self.kwargs['device_configuration'] + def update(self) -> None: unit = self.component_config.configuration.modbus_id config = self.component_config.configuration # Power - power = read_value(self.client, unit, config.power) + power = read_value(self.client, unit, self.device_configuration, config.power) if power is None: raise ValueError("Leistungsregister muss angegeben werden.") # Exported - exported = read_value(self.client, unit, config.exported) + exported = read_value(self.client, unit, self.device_configuration, config.exported) # Imported - imported = read_value(self.client, unit, config.imported) + imported = read_value(self.client, unit, self.device_configuration, config.imported) # Currents - currents_value = read_phase_values(self.client, unit, config.current_L1, config.current_L2, config.current_L3) + currents_value = read_phase_values(self.client, unit, self.device_configuration, + config.current_L1, config.current_L2, config.current_L3) if currents_value is not None: currents = currents_value # DC Power - dc_power_value = read_value(self.client, unit, config.dc_power) + dc_power_value = read_value(self.client, unit, self.device_configuration, config.dc_power) if dc_power_value is not None: dc_power = dc_power_value # Serial Number - serial_number_value = read_value(self.client, unit, config.serial_number) + serial_number_value = read_value(self.client, unit, self.device_configuration, config.serial_number) if serial_number_value is not None: serial_number = serial_number_value