from __future__ import annotations import argparse from dataclasses import dataclass from os import environ from sys import stderr from time import sleep from gibil.classes.env_loader import EnvLoader from gibil.classes.sigen.builder import SigenPlantClient from gibil.classes.sigen.store import SigenStore @dataclass(frozen=True) class SigenDaemonConfig: poll_seconds: float @classmethod def from_env(cls) -> "SigenDaemonConfig": return cls( poll_seconds=float(environ.get("SIGEN_POLL_SECONDS", "5")), ) class SigenDaemon: """Polls Sigenergy plant metrics and stores normalized snapshots.""" def __init__( self, config: SigenDaemonConfig, plant_client: SigenPlantClient, sigen_store: SigenStore, ) -> None: self.config = config self.plant_client = plant_client self.sigen_store = sigen_store @classmethod def from_env(cls) -> "SigenDaemon": return cls( config=SigenDaemonConfig.from_env(), plant_client=SigenPlantClient.from_env(), sigen_store=SigenStore.from_env(), ) def initialize(self) -> None: self.sigen_store.initialize() def run_once(self) -> int: snapshot = self.plant_client.fetch_snapshot() return self.sigen_store.save_snapshot(snapshot) def run_forever(self) -> None: self.initialize() while True: try: saved_count = self.run_once() print(f"stored_sigen_plant_snapshots={saved_count}", flush=True) except Exception as error: print(f"sigen_poll_error={error}", file=stderr, flush=True) sleep(self.config.poll_seconds) def main() -> None: try: EnvLoader().load() daemon = SigenDaemon.from_env() args = parse_args() if args.once: daemon.initialize() saved_count = daemon.run_once() print(f"stored_sigen_plant_snapshots={saved_count}", flush=True) return daemon.run_forever() except Exception as error: print(f"sigen_daemon_startup_error={error}", file=stderr) raise SystemExit(1) from error def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description="Poll Sigenergy plant metrics into Astrape's database." ) parser.add_argument( "--once", action="store_true", help="Initialize storage, save one snapshot, and exit.", ) return parser.parse_args() if __name__ == "__main__": main()