Add new daemons and debug scripts for Sigenergy and Oracle functionalities
- Implement `sigen_daemon.py` to poll Sigenergy plant metrics and store snapshots. - Create `web_daemon.py` for serving a web interface with various endpoints. - Add debug scripts: - `debug_duplicates.py` to find duplicate target times in forecast data. - `debug_energy_forecast.py` to print baseline energy forecast curves. - `debug_oracle_evaluations.py` to run the oracle evaluator. - `debug_sigen.py` to inspect stored Sigenergy plant snapshots. - `debug_weather.py` to trace resolved truth data. - `modbus_test.py` for exploring Sigenergy plants or inverters over Modbus TCP. - Introduce `oracle_evaluator.py` for evaluating stored oracle predictions against actuals. - Add TCN training scripts in `tcn` directory for training usage sequence models.
This commit is contained in:
@@ -0,0 +1,530 @@
|
||||
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",
|
||||
)
|
||||
Reference in New Issue
Block a user