Add procedural field post-processing to snapshots
server/data_model.py: - apply_procedural(record) — stub function; add computed fields to the proc dict inside and they appear under record['procedural'] in every record stored and broadcast. - _get(data, *keys) — safe nested dict accessor for use inside apply_procedural. collect.py: - New snapshots: apply_procedural() called after collect_snapshot(), before appending to the store and broadcasting. - Preloaded 24-h history: guarded apply (skip if 'procedural' already present) applied after load_last_24h_records(). - Current-day store loaded from disk: same guarded apply after load_store(), so legacy records sent to clients are enriched on the fly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+8
-1
@@ -53,7 +53,7 @@ from datetime import datetime, timezone
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from server import log_config, network, we_connect
|
from server import log_config, network, we_connect
|
||||||
from server.data_model import ALL_DOMAINS, collect_snapshot
|
from server.data_model import ALL_DOMAINS, collect_snapshot, apply_procedural
|
||||||
from server.storage_helpers import auto_out_path, load_last_24h_records, load_store, save_store
|
from server.storage_helpers import auto_out_path, load_last_24h_records, load_store, save_store
|
||||||
from utils.jay_diff import jay_diff_full
|
from utils.jay_diff import jay_diff_full
|
||||||
|
|
||||||
@@ -95,11 +95,17 @@ def run(args: argparse.Namespace) -> None:
|
|||||||
push_clients, push_lock, push_store_ref = network.start_push_server(args.host, args.port)
|
push_clients, push_lock, push_store_ref = network.start_push_server(args.host, args.port)
|
||||||
push_store_ref["diff_mode"] = args.diff
|
push_store_ref["diff_mode"] = args.diff
|
||||||
push_store_ref["preloaded"] = load_last_24h_records(logs_dir, vin)
|
push_store_ref["preloaded"] = load_last_24h_records(logs_dir, vin)
|
||||||
|
for _r in push_store_ref["preloaded"]:
|
||||||
|
if "procedural" not in _r:
|
||||||
|
apply_procedural(_r)
|
||||||
if push_store_ref["preloaded"]:
|
if push_store_ref["preloaded"]:
|
||||||
log.info("Pre-loaded %d record(s) from the last 24 h", len(push_store_ref["preloaded"]))
|
log.info("Pre-loaded %d record(s) from the last 24 h", len(push_store_ref["preloaded"]))
|
||||||
|
|
||||||
current_day = datetime.now(timezone.utc).date()
|
current_day = datetime.now(timezone.utc).date()
|
||||||
store = load_store(out, vin, args.interval)
|
store = load_store(out, vin, args.interval)
|
||||||
|
for _r in store["records"]:
|
||||||
|
if "procedural" not in _r:
|
||||||
|
apply_procedural(_r)
|
||||||
push_store_ref["current"] = store
|
push_store_ref["current"] = store
|
||||||
last_broadcast: dict = {}
|
last_broadcast: dict = {}
|
||||||
|
|
||||||
@@ -127,6 +133,7 @@ def run(args: argparse.Namespace) -> None:
|
|||||||
try:
|
try:
|
||||||
wc.update()
|
wc.update()
|
||||||
snapshot = collect_snapshot(vehicle, domains)
|
snapshot = collect_snapshot(vehicle, domains)
|
||||||
|
apply_procedural(snapshot)
|
||||||
store["records"].append(snapshot)
|
store["records"].append(snapshot)
|
||||||
save_store(out, store, args.max_records)
|
save_store(out, store, args.max_records)
|
||||||
if args.diff and last_broadcast:
|
if args.diff and last_broadcast:
|
||||||
|
|||||||
@@ -243,3 +243,39 @@ def collect_snapshot(vehicle, domains: list[str]) -> dict:
|
|||||||
if data is not None:
|
if data is not None:
|
||||||
snapshot[domain] = data
|
snapshot[domain] = data
|
||||||
return snapshot
|
return snapshot
|
||||||
|
|
||||||
|
|
||||||
|
# ── procedural fields ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _get(data: dict, *keys):
|
||||||
|
"""Navigate nested dicts safely; returns None if any key is missing."""
|
||||||
|
for k in keys:
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
return None
|
||||||
|
data = data.get(k)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def apply_procedural(record: dict) -> dict:
|
||||||
|
"""Compute derived fields and store them under record['procedural'].
|
||||||
|
|
||||||
|
Called for every new snapshot before storage and broadcast, and for
|
||||||
|
historical records that lack the 'procedural' key before they are sent
|
||||||
|
to clients. Add computed fields to the proc dict below.
|
||||||
|
"""
|
||||||
|
proc: dict = {}
|
||||||
|
|
||||||
|
# ── add computed fields here ──────────────────────────────────────────
|
||||||
|
# Use _get(record, "domain", "statusObject", "field", "value") to
|
||||||
|
# safely read any nested value. Always use {"value": ..., "unit": ...}
|
||||||
|
# format so the GUI picks up the field automatically.
|
||||||
|
#
|
||||||
|
# Example — usable energy estimated from SOC and battery capacity:
|
||||||
|
# soc = _get(record, "charging", "batteryStatus", "currentSOC", "value")
|
||||||
|
# if soc is not None:
|
||||||
|
# proc["energy_stored"] = {"value": round(soc / 100 * 77.0, 1), "unit": "kWh"}
|
||||||
|
# ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
if proc:
|
||||||
|
record["procedural"] = proc
|
||||||
|
return record
|
||||||
|
|||||||
Reference in New Issue
Block a user