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,76 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from gibil.classes.models import ForecastKind, PowerForecastPoint, PowerForecastRun
|
||||
from gibil.classes.oracle.config import EnergyForecastConfig
|
||||
from gibil.classes.sigen.store import SigenStore
|
||||
|
||||
|
||||
class BaselineUsageOracle:
|
||||
"""Forecasts near-future load from recent high-resolution Sigen history."""
|
||||
|
||||
model_version = "baseline_recent_load_v1"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
sigen_store: SigenStore,
|
||||
config: EnergyForecastConfig,
|
||||
) -> None:
|
||||
self.sigen_store = sigen_store
|
||||
self.config = config
|
||||
|
||||
def forecast(
|
||||
self,
|
||||
target_times: list[datetime],
|
||||
issued_at: datetime | None = None,
|
||||
) -> PowerForecastRun:
|
||||
if issued_at is None:
|
||||
issued_at = datetime.now(timezone.utc)
|
||||
|
||||
lookback = timedelta(minutes=self.config.load_lookback_minutes)
|
||||
summary = self.sigen_store.load_recent_power_summary(lookback=lookback)
|
||||
latest = self.sigen_store.load_latest_snapshot()
|
||||
fallback_load_w = latest.load_power_w if latest else 0.0
|
||||
|
||||
p50 = self._number(summary.get("load_p50_w"), fallback_load_w)
|
||||
p10 = max(0.0, self._number(summary.get("load_p10_w"), p50 * 0.7))
|
||||
p90 = max(
|
||||
self._number(summary.get("load_p90_w"), p50 * 1.5),
|
||||
p50 * 1.25,
|
||||
)
|
||||
|
||||
points = [
|
||||
PowerForecastPoint(
|
||||
target_at=target_at,
|
||||
horizon_minutes=max(
|
||||
0, round((target_at - issued_at).total_seconds() / 60)
|
||||
),
|
||||
expected_power_w=p50,
|
||||
p10_power_w=p10,
|
||||
p50_power_w=p50,
|
||||
p90_power_w=p90,
|
||||
confidence=0.35,
|
||||
source="recent_sigen_load",
|
||||
model_version=self.model_version,
|
||||
metadata={
|
||||
"lookback_minutes": self.config.load_lookback_minutes,
|
||||
"load_avg_w": summary.get("load_avg_w"),
|
||||
"load_max_w": summary.get("load_max_w"),
|
||||
},
|
||||
)
|
||||
for target_at in target_times
|
||||
]
|
||||
|
||||
return PowerForecastRun(
|
||||
issued_at=issued_at,
|
||||
kind=ForecastKind.LOAD,
|
||||
source="baseline_usage_oracle",
|
||||
model_version=self.model_version,
|
||||
points=points,
|
||||
)
|
||||
|
||||
def _number(self, value: object, fallback: float) -> float:
|
||||
if value is None:
|
||||
return float(fallback)
|
||||
return float(value)
|
||||
Reference in New Issue
Block a user