Skip to main content

std_rs/device_support/
epid_soft_callback.rs

1use epics_base_rs::error::CaResult;
2use epics_base_rs::server::device_support::{DeviceReadOutcome, DeviceSupport};
3use epics_base_rs::server::record::{ProcessAction, Record};
4use epics_base_rs::types::EpicsValue;
5
6use crate::records::epid::EpidRecord;
7
8/// Async Soft Channel device support for the epid record.
9///
10/// Same PID algorithm as `EpidSoftDeviceSupport`, but with an
11/// asynchronous readback trigger via the TRIG link.
12///
13/// Processing flow:
14/// 1. First pass (triggered=false): Write TVAL to TRIG link via
15///    ProcessAction::WriteDbLink, and request a re-process via
16///    ProcessAction::ReprocessAfter(1ms). The TRIG write triggers
17///    the readback hardware to update the INP PV.
18/// 2. Second pass (triggered=true): INP has been updated by the
19///    triggered readback. Run PID with the fresh CVAL.
20///
21/// Ported from `devEpidSoftCallback.c`.
22pub struct EpidSoftCallbackDeviceSupport {
23    /// Whether the trigger has been sent and we're on the second pass.
24    triggered: bool,
25}
26
27impl Default for EpidSoftCallbackDeviceSupport {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33impl EpidSoftCallbackDeviceSupport {
34    pub fn new() -> Self {
35        Self { triggered: false }
36    }
37}
38
39impl DeviceSupport for EpidSoftCallbackDeviceSupport {
40    fn dtyp(&self) -> &str {
41        "Epid Async Soft"
42    }
43
44    fn read(&mut self, record: &mut dyn Record) -> CaResult<DeviceReadOutcome> {
45        let epid = record
46            .as_any_mut()
47            .and_then(|a| a.downcast_mut::<EpidRecord>())
48            .expect("EpidSoftCallbackDeviceSupport requires an EpidRecord");
49
50        if !self.triggered {
51            // First pass: queue TRIG write and request re-process.
52            if !epid.trig.is_empty() {
53                let actions = vec![
54                    ProcessAction::WriteDbLink {
55                        link_field: "TRIG",
56                        value: EpicsValue::Double(epid.tval),
57                    },
58                    // Re-process after a short delay to allow triggered device to update
59                    ProcessAction::ReprocessAfter(std::time::Duration::from_millis(1)),
60                ];
61                self.triggered = true;
62                return Ok(DeviceReadOutcome::computed_with(actions));
63            }
64            // No TRIG link — fall through to synchronous PID
65        }
66
67        // Second pass (or no TRIG link): execute PID
68        self.triggered = false;
69        super::epid_soft::EpidSoftDeviceSupport::do_pid(epid);
70        Ok(DeviceReadOutcome::computed())
71    }
72
73    fn write(&mut self, _record: &mut dyn Record) -> CaResult<()> {
74        Ok(())
75    }
76}