from __future__ import annotations from dataclasses import dataclass from typing import Literal from gibil.classes.sigen.modbus import RegisterKind SigenDataType = Literal["u16", "u32", "u64", "s16", "s32", "string"] @dataclass(frozen=True) class SigenRegister: name: str kind: RegisterKind address: int count: int data_type: SigenDataType gain: float = 1 unit: str | None = None description: str | None = None def decode(self, registers: list[int] | list[bool]) -> int | float | str: numeric_registers = [int(register) for register in registers[: self.count]] if self.data_type == "string": return self._decode_string(numeric_registers) raw_value = self._combine(numeric_registers) if self.data_type.startswith("s"): bits = 16 * self.count sign_bit = 1 << (bits - 1) if raw_value & sign_bit: raw_value -= 1 << bits if self.gain == 1: return raw_value return raw_value / self.gain def _combine(self, registers: list[int]) -> int: value = 0 for register in registers: value = (value << 16) | (register & 0xFFFF) return value def _decode_string(self, registers: list[int]) -> str: raw_bytes = bytearray() for register in registers: raw_bytes.append((register >> 8) & 0xFF) raw_bytes.append(register & 0xFF) return raw_bytes.rstrip(b"\x00").decode("ascii", errors="replace").strip() PLANT_REGISTERS: dict[str, SigenRegister] = { "plant_system_time": SigenRegister( name="plant_system_time", kind="input", address=30000, count=2, data_type="u32", unit="s", ), "plant_ems_work_mode": SigenRegister( name="plant_ems_work_mode", kind="input", address=30003, count=1, data_type="u16", ), "plant_grid_sensor_status": SigenRegister( name="plant_grid_sensor_status", kind="input", address=30004, count=1, data_type="u16", ), "plant_grid_sensor_active_power": SigenRegister( name="plant_grid_sensor_active_power", kind="input", address=30005, count=2, data_type="s32", gain=1000, unit="kW", ), "plant_ess_soc": SigenRegister( name="plant_ess_soc", kind="input", address=30014, count=1, data_type="u16", gain=10, unit="%", ), "plant_active_power": SigenRegister( name="plant_active_power", kind="input", address=30031, count=2, data_type="s32", gain=1000, unit="kW", ), "plant_sigen_photovoltaic_power": SigenRegister( name="plant_sigen_photovoltaic_power", kind="input", address=30035, count=2, data_type="s32", gain=1000, unit="kW", ), "plant_ess_power": SigenRegister( name="plant_ess_power", kind="input", address=30037, count=2, data_type="s32", gain=1000, unit="kW", ), "plant_running_state": SigenRegister( name="plant_running_state", kind="input", address=30051, count=1, data_type="u16", ), "plant_ess_rated_energy_capacity": SigenRegister( name="plant_ess_rated_energy_capacity", kind="input", address=30083, count=2, data_type="u32", gain=100, unit="kWh", ), "plant_ess_soh": SigenRegister( name="plant_ess_soh", kind="input", address=30087, count=1, data_type="u16", gain=10, unit="%", ), "plant_accumulated_pv_energy": SigenRegister( name="plant_accumulated_pv_energy", kind="input", address=30088, count=4, data_type="u64", gain=100, unit="kWh", ), "plant_daily_consumed_energy": SigenRegister( name="plant_daily_consumed_energy", kind="input", address=30092, count=2, data_type="u32", gain=100, unit="kWh", ), "plant_accumulated_consumed_energy": SigenRegister( name="plant_accumulated_consumed_energy", kind="input", address=30094, count=4, data_type="u64", gain=100, unit="kWh", ), "plant_general_load_power": SigenRegister( name="plant_general_load_power", kind="input", address=30282, count=2, data_type="s32", gain=1000, unit="kW", description="General load power", ), "plant_total_load_power": SigenRegister( name="plant_total_load_power", kind="input", address=30284, count=2, data_type="s32", gain=1000, unit="kW", description="Total load power", ), } PLANT_PARAMETER_REGISTERS: dict[str, SigenRegister] = { "plant_start_stop": SigenRegister( name="plant_start_stop", kind="holding", address=40000, count=1, data_type="u16", description="Start/Stop (0: Stop, 1: Start)", ), "plant_active_power_fixed_target": SigenRegister( name="plant_active_power_fixed_target", kind="holding", address=40001, count=2, data_type="s32", gain=1000, unit="kW", description="Active power fixed adjustment target value", ), "plant_reactive_power_fixed_target": SigenRegister( name="plant_reactive_power_fixed_target", kind="holding", address=40003, count=2, data_type="s32", gain=1000, unit="kvar", description="Reactive power fixed adjustment target value", ), "plant_active_power_percentage_target": SigenRegister( name="plant_active_power_percentage_target", kind="holding", address=40005, count=1, data_type="s16", gain=100, unit="%", description="Active power percentage target. Range: -100.00 to 100.00", ), "plant_qs_ratio_target": SigenRegister( name="plant_qs_ratio_target", kind="holding", address=40006, count=1, data_type="s16", gain=100, unit="%", description="Q/S adjustment target value", ), "plant_power_factor_target": SigenRegister( name="plant_power_factor_target", kind="holding", address=40007, count=1, data_type="s16", gain=1000, description="Power factor adjustment target value", ), "plant_remote_ems_enable": SigenRegister( name="plant_remote_ems_enable", kind="holding", address=40029, count=1, data_type="u16", description="Remote EMS enable (0: disabled, 1: enabled)", ), "plant_independent_phase_power_control_enable": SigenRegister( name="plant_independent_phase_power_control_enable", kind="holding", address=40030, count=1, data_type="u16", description="Independent phase power control enable (0: disabled, 1: enabled)", ), "plant_remote_ems_control_mode": SigenRegister( name="plant_remote_ems_control_mode", kind="holding", address=40031, count=1, data_type="u16", description=( "Remote EMS control mode: 0 PCS remote, 1 standby, " "2 self-consumption, 3 charge grid first, 4 charge PV first, " "5 discharge PV first, 6 discharge ESS first" ), ), "plant_ess_max_charging_limit": SigenRegister( name="plant_ess_max_charging_limit", kind="holding", address=40032, count=2, data_type="u32", gain=1000, unit="kW", description="ESS max charging limit", ), "plant_ess_max_discharging_limit": SigenRegister( name="plant_ess_max_discharging_limit", kind="holding", address=40034, count=2, data_type="u32", gain=1000, unit="kW", description="ESS max discharging limit", ), "plant_pv_max_power_limit": SigenRegister( name="plant_pv_max_power_limit", kind="holding", address=40036, count=2, data_type="u32", gain=1000, unit="kW", description="PV max power limit", ), "plant_grid_point_maximum_export_limitation": SigenRegister( name="plant_grid_point_maximum_export_limitation", kind="holding", address=40038, count=2, data_type="u32", gain=1000, unit="kW", description="Grid point maximum export limitation", ), "plant_grid_maximum_import_limitation": SigenRegister( name="plant_grid_maximum_import_limitation", kind="holding", address=40040, count=2, data_type="u32", gain=1000, unit="kW", description="Grid point maximum import limitation", ), "plant_pcs_maximum_export_limitation": SigenRegister( name="plant_pcs_maximum_export_limitation", kind="holding", address=40042, count=2, data_type="u32", gain=1000, unit="kW", description="PCS maximum export limitation", ), "plant_pcs_maximum_import_limitation": SigenRegister( name="plant_pcs_maximum_import_limitation", kind="holding", address=40044, count=2, data_type="u32", gain=1000, unit="kW", description="PCS maximum import limitation", ), "plant_backup_soc": SigenRegister( name="plant_backup_soc", kind="holding", address=40046, count=1, data_type="u16", gain=10, unit="%", description="ESS backup SOC. Range: 0 to 100.0", ), "plant_charge_cut_off_soc": SigenRegister( name="plant_charge_cut_off_soc", kind="holding", address=40047, count=1, data_type="u16", gain=10, unit="%", description="ESS charge cut-off SOC. Range: 0 to 100.0", ), "plant_discharge_cut_off_soc": SigenRegister( name="plant_discharge_cut_off_soc", kind="holding", address=40048, count=1, data_type="u16", gain=10, unit="%", description="ESS discharge cut-off SOC. Range: 0 to 100.0", ), } INVERTER_REGISTERS: dict[str, SigenRegister] = { "inverter_model_type": SigenRegister( name="inverter_model_type", kind="input", address=30500, count=15, data_type="string", ), "inverter_serial_number": SigenRegister( name="inverter_serial_number", kind="input", address=30515, count=10, data_type="string", ), "inverter_machine_firmware_version": SigenRegister( name="inverter_machine_firmware_version", kind="input", address=30525, count=15, data_type="string", ), "inverter_rated_active_power": SigenRegister( name="inverter_rated_active_power", kind="input", address=30540, count=2, data_type="u32", gain=1000, unit="kW", ), "inverter_running_state": SigenRegister( name="inverter_running_state", kind="input", address=30578, count=1, data_type="u16", ), "inverter_active_power": SigenRegister( name="inverter_active_power", kind="input", address=30587, count=2, data_type="s32", gain=1000, unit="kW", ), "inverter_reactive_power": SigenRegister( name="inverter_reactive_power", kind="input", address=30589, count=2, data_type="s32", gain=1000, unit="kvar", ), "inverter_ess_charge_discharge_power": SigenRegister( name="inverter_ess_charge_discharge_power", kind="input", address=30599, count=2, data_type="s32", gain=1000, unit="kW", ), "inverter_ess_battery_soc": SigenRegister( name="inverter_ess_battery_soc", kind="input", address=30601, count=1, data_type="u16", gain=10, unit="%", ), "inverter_ess_battery_soh": SigenRegister( name="inverter_ess_battery_soh", kind="input", address=30602, count=1, data_type="u16", gain=10, unit="%", ), "inverter_pv_power": SigenRegister( name="inverter_pv_power", kind="input", address=31035, count=2, data_type="s32", gain=1000, unit="kW", ), "inverter_daily_pv_energy": SigenRegister( name="inverter_daily_pv_energy", kind="input", address=31509, count=2, data_type="u32", gain=100, unit="kWh", ), "inverter_accumulated_pv_energy": SigenRegister( name="inverter_accumulated_pv_energy", kind="input", address=31511, count=4, data_type="u64", gain=100, unit="kWh", ), } DEFAULT_PLANT_REGISTER_NAMES = ( "plant_system_time", "plant_ems_work_mode", "plant_grid_sensor_status", "plant_grid_sensor_active_power", "plant_ess_soc", "plant_active_power", "plant_sigen_photovoltaic_power", "plant_ess_power", "plant_running_state", "plant_ess_rated_energy_capacity", "plant_ess_soh", "plant_accumulated_pv_energy", "plant_daily_consumed_energy", "plant_accumulated_consumed_energy", "plant_general_load_power", "plant_total_load_power", ) DEFAULT_INVERTER_REGISTER_NAMES = ( "inverter_model_type", "inverter_serial_number", "inverter_machine_firmware_version", "inverter_rated_active_power", "inverter_running_state", "inverter_active_power", "inverter_reactive_power", "inverter_ess_charge_discharge_power", "inverter_ess_battery_soc", "inverter_ess_battery_soh", "inverter_pv_power", "inverter_daily_pv_energy", "inverter_accumulated_pv_energy", )