Files
next-tjwater-server/scripts/materialize_burst_demo_scada.py
T

89 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""Materialize selected burst demo months from one normal day and event runs."""
from __future__ import annotations
import argparse
import asyncio
import json
import os
import sys
from datetime import datetime
from pathlib import Path
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--manifest", type=Path,
default=Path("resources/benchmarks/tjwater2-v2-burst-demo.json"),
)
parser.add_argument(
"--generation-state", type=Path,
default=Path("data/benchmark-generation/tjwater2-20261105/state.json"),
)
parser.add_argument(
"--state", type=Path,
default=Path("data/benchmark-generation/tjwater2-20261105/demo-scada-state.json"),
)
args = parser.parse_args()
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
os.umask(0o077)
from psycopg.conninfo import make_conninfo
from scripts.benchmark.calibration import digest, save_json
from scripts.benchmark.generation import write_demo_scada_window
from scripts.benchmark.simulation_design import load_simulation_design
from scripts.check_burst_simulation_design import resolve_project
manifest = json.loads(args.manifest.read_text(encoding="utf-8"))
generation = json.loads(args.generation_state.read_text(encoding="utf-8"))
if manifest["project_code"] != generation["target"] or generation.get("status") != "prepared":
raise ValueError("manifest and isolated generation state do not match")
design = load_simulation_design(Path("resources/benchmarks/tjwater2-v2-burst-balanced.json"))
source_biz, source_iot = asyncio.run(resolve_project(design))
business_dsn = make_conninfo(source_biz, dbname=generation["target"])
timescale_dsn = make_conninfo(source_iot, dbname=generation["target"])
state_path = args.state.resolve()
state_path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
manifest_sha256 = digest(args.manifest)
state = json.loads(state_path.read_text(encoding="utf-8")) if state_path.exists() else {
"version": "burst-demo-scada-state@1",
"target": generation["target"],
"manifest_sha256": manifest_sha256,
"cases": {},
"status": "materializing",
}
if state["target"] != generation["target"] or state["manifest_sha256"] != manifest_sha256:
raise ValueError("demo state belongs to different inputs")
save_json(state_path, state)
normal_source_start = datetime.fromisoformat(manifest["normal_source_start"])
for case in manifest["cases"]:
source_case = generation["cases"].get(case["case_key"])
if not source_case or source_case.get("status") != "scada_materialized":
raise ValueError(f"event source is unavailable: {case['case_key']}")
summary = write_demo_scada_window(
business_dsn=business_dsn,
timescale_dsn=timescale_dsn,
normal_source_start=normal_source_start,
target_start=datetime.fromisoformat(case["target_start"]),
day_count=int(manifest["day_count"]),
event_start=datetime.fromisoformat(case["event_start"]),
event_run_id=source_case["analysis_run_id"],
noise_seed=manifest["noise_seed"],
)
state["cases"][case["demo_id"]] = {
"case_key": case["case_key"],
"entry_mode": case["entry_mode"],
**summary,
}
save_json(state_path, state)
print({"demo_id": case["demo_id"], "rows": summary["rows"]}, flush=True)
state["status"] = "completed"
save_json(state_path, state)
print(json.dumps({"status": state["status"], "cases": state["cases"]}, ensure_ascii=False), flush=True)
return 0
if __name__ == "__main__":
raise SystemExit(main())