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;
14use epics_base_rs::server::snl::spawn_program;
15
16/// Split a `seq` macro string into definitions.
17///
18/// Through `macro_defn_pairs`, the port's owner of C's `macParseDefns`
19/// grammar (`macUtil.c`), so a quoted value with an embedded comma survives —
20/// a raw `split(',')` tears it. A name given with no `=` is a deletion in
21/// that grammar and is dropped here, since a program can only be started with
22/// definitions.
23pub fn parse_macros(input: &str) -> HashMap<String, String> {
24 macro_defn_pairs(input)
25 .into_iter()
26 .filter_map(|(name, value)| value.map(|v| (name, v)))
27 .collect()
28}
29
30fn require_macro(
31 macros: &HashMap<String, String>,
32 key: &str,
33 program: &str,
34) -> Result<String, String> {
35 macros
36 .get(key)
37 .cloned()
38 .ok_or_else(|| format!("{program}: required macro '{key}' not specified"))
39}
40
41/// Start a std-module SNL program by name.
42///
43/// | Name | Macros | Program |
44/// |------|--------|---------|
45/// | `delayDo` | P, R | `delayDo.st` — wait out an active condition, then process `doSeq` |
46/// | `femto` | P, H, F, G1, G2, G3, NO | `femto.st` — Femto amplifier gain control |
47///
48/// Must be called with the runtime reachable — from st.cmd that means
49/// `CommandContext::bridge()`, because the shell runs on a blocking thread.
50pub fn seq_start(
51 program: &str,
52 macro_str: &str,
53 bridge: &epics_base_rs::runtime::task::BlockingBridge,
54 db: &PvDatabase,
55) -> Result<(), String> {
56 let macros = parse_macros(macro_str);
57
58 match program {
59 "delayDo" => {
60 let config = crate::snl::delay_do::DelayDoConfig::new(
61 &require_macro(¯os, "P", program)?,
62 &require_macro(¯os, "R", program)?,
63 );
64 spawn_program(bridge, db, "delayDo", move |db| {
65 crate::snl::delay_do::run(config, db)
66 });
67 }
68 "femto" => {
69 let config = crate::snl::femto::FemtoConfig::new(
70 &require_macro(¯os, "P", program)?,
71 &require_macro(¯os, "H", program)?,
72 &require_macro(¯os, "F", program)?,
73 &require_macro(¯os, "G1", program)?,
74 &require_macro(¯os, "G2", program)?,
75 &require_macro(¯os, "G3", program)?,
76 &require_macro(¯os, "NO", program)?,
77 );
78 spawn_program(bridge, db, "femto", move |db| {
79 crate::snl::femto::run(config, db)
80 });
81 }
82 other => return Err(format!("seq_start: unknown std program '{other}'")),
83 }
84
85 Ok(())
86}