Skip to main content

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(&macros, "P", program)?,
62                &require_macro(&macros, "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(&macros, "P", program)?,
71                &require_macro(&macros, "H", program)?,
72                &require_macro(&macros, "F", program)?,
73                &require_macro(&macros, "G1", program)?,
74                &require_macro(&macros, "G2", program)?,
75                &require_macro(&macros, "G3", program)?,
76                &require_macro(&macros, "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}