std_rs/seq_runner.rs
1//! SNL program launcher for the std module — the Rust stand-in for C's
2//! `seq &program, "macros"`.
3//!
4//! Each `snl` module here is a pure state machine plus a `run(config, db)`
5//! that gives it PVs; this is the one place that turns a program name and a
6//! macro string into the right pair. An IOC binary reaches it with a single
7//! `seqStart` startup command, the same shape
8//! `optics_rs::seq_runner::seq_start` is registered with.
9
10use std::collections::HashMap;
11
12use epics_base_rs::server::database::PvDatabase;
13use epics_base_rs::server::iocsh::macro_defn_pairs;
14
15/// Split a `seq` macro string into definitions.
16///
17/// Through `macro_defn_pairs`, the port's owner of C's `macParseDefns`
18/// grammar (`macUtil.c`), so a quoted value with an embedded comma survives —
19/// a raw `split(',')` tears it. A name given with no `=` is a deletion in
20/// that grammar and is dropped here, since a program can only be started with
21/// definitions.
22pub fn parse_macros(input: &str) -> HashMap<String, String> {
23 macro_defn_pairs(input)
24 .into_iter()
25 .filter_map(|(name, value)| value.map(|v| (name, v)))
26 .collect()
27}
28
29fn require_macro(
30 macros: &HashMap<String, String>,
31 key: &str,
32 program: &str,
33) -> Result<String, String> {
34 macros
35 .get(key)
36 .cloned()
37 .ok_or_else(|| format!("{program}: required macro '{key}' not specified"))
38}
39
40/// Start a std-module SNL program by name.
41///
42/// | Name | Macros | Program |
43/// |------|--------|---------|
44/// | `delayDo` | P, R | `delayDo.st` — wait out an active condition, then process `doSeq` |
45/// | `femto` | P, H, F, G1, G2, G3, NO | `femto.st` — Femto amplifier gain control |
46///
47/// Must be called with the runtime reachable — from st.cmd that means
48/// `CommandContext::bridge()`, because the shell runs on a blocking thread.
49pub fn seq_start(
50 program: &str,
51 macro_str: &str,
52 bridge: &epics_base_rs::runtime::task::BlockingBridge,
53 db: &PvDatabase,
54) -> Result<(), String> {
55 let macros = parse_macros(macro_str);
56
57 match program {
58 "delayDo" => {
59 let config = crate::snl::delay_do::DelayDoConfig::new(
60 &require_macro(¯os, "P", program)?,
61 &require_macro(¯os, "R", program)?,
62 );
63 let db = db.clone();
64 bridge.spawn(async move {
65 if let Err(e) = crate::snl::delay_do::run(config, db).await {
66 eprintln!("delayDo error: {e}");
67 }
68 });
69 }
70 "femto" => {
71 let config = crate::snl::femto::FemtoConfig::new(
72 &require_macro(¯os, "P", program)?,
73 &require_macro(¯os, "H", program)?,
74 &require_macro(¯os, "F", program)?,
75 &require_macro(¯os, "G1", program)?,
76 &require_macro(¯os, "G2", program)?,
77 &require_macro(¯os, "G3", program)?,
78 &require_macro(¯os, "NO", program)?,
79 );
80 let db = db.clone();
81 bridge.spawn(async move {
82 if let Err(e) = crate::snl::femto::run(config, db).await {
83 eprintln!("femto error: {e}");
84 }
85 });
86 }
87 other => return Err(format!("seq_start: unknown std program '{other}'")),
88 }
89
90 Ok(())
91}