//! Structured JSON event publisher — one line per event on stdout. //! //! Format is the ADR-100 runtime contract: `{ts, level, event, fields}`. use serde::Serialize; use serde_json::Value; use std::time::{SystemTime, UNIX_EPOCH}; #[derive(Debug, Serialize)] pub struct Event<'a> { pub ts: f64, pub level: &'a str, pub event: &'a str, pub fields: Value, } impl<'a> Event<'a> { pub fn health_ok(cog_id: &'a str, backend: &str, output_confidence: f32) -> Self { Self { ts: now_secs(), level: "info", event: "health.ok", fields: serde_json::json!({ "cog": cog_id, "backend": backend, "synthetic_output_confidence": output_confidence, }), } } pub fn run_started(cog_id: &'a str, cfg: &crate::config::CogConfig) -> Self { Self { ts: now_secs(), level: "info", event: "run.started", fields: serde_json::json!({ "cog": cog_id, "sensing_url": cfg.sensing_url, "model_path": cfg.model_path, "poll_ms": cfg.poll_ms, }), } } pub fn pose_frame(tick: u64, n_persons: usize, persons: Value) -> Self { Self { ts: now_secs(), level: "info", event: "pose.frame", fields: serde_json::json!({ "tick": tick, "n_persons": n_persons, "persons": persons, }), } } } pub fn emit_event(ev: &Event<'_>) { if let Ok(line) = serde_json::to_string(ev) { println!("{line}"); } } fn now_secs() -> f64 { SystemTime::now() .duration_since(UNIX_EPOCH) .map(|d| d.as_secs_f64()) .unwrap_or(0.0) }