Skip to main content

sim_lib_view_agent/
monitor.rs

1//! The live monitor view: a topology Graph plus its run state.
2//!
3//! The monitor renders the same topology as the composer, with status badges on
4//! nodes, per-node counters, edge liveness, and a `scene/timeline` of execution
5//! events. Drilling into a node yields its event log. The run state streams in
6//! through subscriptions; the same state replays a past run (see `replay`).
7
8use sim_kernel::{Expr, Symbol};
9use sim_lib_scene::{data_map, node, sym};
10use sim_lib_topology::Graph;
11use sim_value::build::uint;
12
13use crate::run::RunState;
14
15/// The monitor lens id.
16pub const MONITOR_LENS: &str = "view:agent-monitor";
17
18/// Render a topology and its run state into a live monitor Scene.
19pub fn monitor_view(graph: &Graph, run: &RunState) -> Expr {
20    node(
21        "stack",
22        vec![
23            ("role", sym("monitor")),
24            ("dir", sym("column")),
25            (
26                "children",
27                Expr::List(vec![monitored_graph(graph, run), timeline(run)]),
28            ),
29        ],
30    )
31}
32
33fn monitored_graph(graph: &Graph, run: &RunState) -> Expr {
34    let nodes = graph
35        .nodes
36        .iter()
37        .map(|n| {
38            let id = n.id.as_symbol();
39            let status = run.status(id);
40            node(
41                "node",
42                vec![
43                    ("id", Expr::Symbol(id.clone())),
44                    ("title", Expr::String(id.name.to_string())),
45                    (
46                        "status",
47                        node(
48                            "badge",
49                            vec![
50                                ("status", sym(status.token())),
51                                ("label", Expr::String(status.token().to_owned())),
52                            ],
53                        ),
54                    ),
55                    ("count", uint(run.count(id))),
56                ],
57            )
58        })
59        .collect();
60    let edges = graph
61        .edges
62        .iter()
63        .map(|edge| {
64            let from = edge.from.node.as_symbol();
65            let to = edge.to.node.as_symbol();
66            let live = run.edge_live(from, to);
67            node(
68                "edge",
69                vec![
70                    ("from", Expr::Symbol(from.clone())),
71                    ("to", Expr::Symbol(to.clone())),
72                    ("status", sym(if live { "live" } else { "idle" })),
73                ],
74            )
75        })
76        .collect();
77    node(
78        "graph",
79        vec![
80            ("id", Expr::Symbol(graph.name.clone())),
81            ("nodes", Expr::List(nodes)),
82            ("edges", Expr::List(edges)),
83        ],
84    )
85}
86
87fn timeline(run: &RunState) -> Expr {
88    let markers = run
89        .events
90        .iter()
91        .map(|event| {
92            // Use `event` rather than `kind` here: `kind` is the scene-node tag,
93            // and an execution event marker is plain data, not a scene node.
94            // `data_map` enforces that reserved-key guard.
95            data_map(vec![
96                ("at", uint(event.at)),
97                ("node", Expr::Symbol(event.node.clone())),
98                ("event", Expr::Symbol(event.kind.clone())),
99                ("label", Expr::String(event.message.clone())),
100            ])
101        })
102        .collect();
103    node(
104        "timeline",
105        vec![("lane", sym("execution")), ("events", Expr::List(markers))],
106    )
107}
108
109/// Drill down from a node into its event log.
110pub fn node_detail(run: &RunState, node_id: &Symbol) -> Expr {
111    let rows = run
112        .events_for(node_id)
113        .into_iter()
114        .map(|event| {
115            node(
116                "text",
117                vec![(
118                    "text",
119                    Expr::String(format!("@{} {} {}", event.at, event.kind, event.message)),
120                )],
121            )
122        })
123        .collect();
124    node(
125        "box",
126        vec![
127            ("role", sym("node-detail")),
128            ("node", Expr::Symbol(node_id.clone())),
129            ("children", Expr::List(rows)),
130        ],
131    )
132}