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:
rpotter6298
2026-04-28 08:14:00 +02:00
parent ff0c65a794
commit c8e3016fd6
55 changed files with 6385 additions and 633 deletions
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
"""Debug baseline energy forecast curves."""
from __future__ import annotations
import argparse
from datetime import timezone
from gibil.classes.oracle.builder import EnergyForecastBuilder
from gibil.classes.env_loader import EnvLoader
from gibil.classes.models import PowerForecastPoint
def main() -> None:
EnvLoader().load()
args = parse_args()
solar_run, load_run, net_run = EnergyForecastBuilder.from_env().build()
print(
f"issued_at={net_run.issued_at.astimezone(timezone.utc).isoformat(timespec='seconds')}"
)
print(
f"solar_model={solar_run.model_version} "
f"load_model={load_run.model_version} points={len(net_run.points)}"
)
print(
"target_at solar_p10 solar_p50 solar_p90 "
"load_p10 load_p50 load_p90 net_p10 net_p50 net_p90"
)
solar_by_target = _by_target(solar_run.points)
load_by_target = _by_target(load_run.points)
for point in net_run.points[: args.limit]:
solar_point = solar_by_target[point.target_at]
load_point = load_by_target[point.target_at]
print(
f"{point.target_at.astimezone(timezone.utc).isoformat(timespec='minutes'):25} "
f"{solar_point.p10_power_w:9.0f} "
f"{solar_point.p50_power_w:9.0f} "
f"{solar_point.p90_power_w:9.0f} "
f"{load_point.p10_power_w:8.0f} "
f"{load_point.p50_power_w:8.0f} "
f"{load_point.p90_power_w:8.0f} "
f"{point.p10_net_power_w:7.0f} "
f"{point.p50_net_power_w:7.0f} "
f"{point.p90_net_power_w:7.0f}"
)
def _by_target(points: list[PowerForecastPoint]) -> dict[object, PowerForecastPoint]:
return {point.target_at: point for point in points}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Print baseline solar/load/net forecast curves."
)
parser.add_argument(
"--limit",
type=int,
default=24,
help="Number of forecast points to show. Defaults to 24.",
)
return parser.parse_args()
if __name__ == "__main__":
main()