pub struct EpidFastDeviceSupport { /* private fields */ }Expand description
Fast Epid device support using asyn driver for high-speed (1+ kHz) PID.
Ported from devEpidFast.c. The PID computation runs in a background
tokio task driven by asyn interrupt callbacks, not during record
processing. The record merely copies parameters to/from the fast
computation thread.
§Architecture
┌─────────────┐ interrupt ┌──────────────────┐
│ asyn driver │ ──────────────► │ PID callback task │
│ (input ADC) │ (new cval) │ (tokio::spawn) │
└─────────────┘ │ runs do_pid() │
│ writes output │
┌──────────────────────────┤ to output driver │
│ shared EpidFastPvt └──────────────────┘
│ (Arc<Mutex>) ▲
▼ │
┌─────────────┐ read() params │
│ EpidRecord │ ◄─────── copy ──────────┘
│ (process) │ ────────► copy ──────────►
└─────────────┘ resultsThe start_callback_loop() method spawns the background task.
Call it after connecting to the asyn input port.
Implementations§
Source§impl EpidFastDeviceSupport
impl EpidFastDeviceSupport
pub fn new() -> Self
Sourcepub fn pvt(&self) -> Arc<Mutex<EpidFastPvt>>
pub fn pvt(&self) -> Arc<Mutex<EpidFastPvt>>
Get a handle to the shared PID state for callback registration.
Sourcepub fn set_output_reader(
&self,
reader: Arc<Mutex<dyn FnMut() -> Option<f64> + Send>>,
)
pub fn set_output_reader( &self, reader: Arc<Mutex<dyn FnMut() -> Option<f64> + Send>>, )
Wire the output-port readback used for bumpless transfer.
C devEpidFast.c:446-448 reads the actual current output value
(pfloat64Output->read) on the feedback OFF->ON edge to seed
the integral term. Supply a closure that returns the output
port’s current value; without it the bumpless edge falls back
to the last commanded OVAL.
Sourcepub fn set_output_port(&self, sync_io: SyncIOHandle, reason: usize)
pub fn set_output_port(&self, sync_io: SyncIOHandle, reason: usize)
Wire the asyn output port: install both the output writer and the bumpless-transfer output reader from a single asyn Float64 port handle.
This mirrors C devEpidFast.c exactly. init_record
(devEpidFast.c:264-303) connects to one output asyn port and
captures pPvt->pfloat64Output / float64OutputPvt. do_PID
then uses that same interface for both directions:
- the bumpless OFF->ON edge reads the output port’s present
value —
pPvt->pfloat64Output->read(...)(devEpidFast.c:446-448); - the feedback write —
pPvt->pfloat64Output->write(...)(devEpidFast.c:471-473).
sync_io is the asyn Float64 output port handle and reason
the parameter index (asyn drvUser “outputDataString” channel,
devEpidFast.c:296-303). The reader calls read_float64(reason)
on it; a failed read yields None, so do_pid falls back to
the last commanded OVAL safety net.
Sourcepub fn start_callback_loop(
&self,
input_rx: Receiver<f64>,
output_fn: Arc<Mutex<dyn FnMut(f64) + Send>>,
)
pub fn start_callback_loop( &self, input_rx: Receiver<f64>, output_fn: Arc<Mutex<dyn FnMut(f64) + Send>>, )
Start the interrupt-driven PID callback loop.
Spawns a tokio task that receives new readback values from input_rx
and runs do_pid() on each. This is the high-speed PID path that
runs at the interrupt rate (1kHz+), independent of record processing.
input_rx: receives new controlled-variable values from the input driver
output_fn: called with each new output value (writes to output driver)
This bare-closure form leaves the bumpless-transfer output_reader
untouched — wire it separately with set_output_reader or
set_output_port, otherwise the OFF->ON edge falls back to the
last commanded OVAL. When the output is a real asyn Float64
port, prefer start_callback_loop_with_port, which installs
both writer and reader from the same port handle (mirroring C
devEpidFast.c pPvt->pfloat64Output).
Sourcepub fn start_callback_loop_with_port(
&self,
input_rx: Receiver<f64>,
output_sync_io: SyncIOHandle,
output_reason: usize,
)
pub fn start_callback_loop_with_port( &self, input_rx: Receiver<f64>, output_sync_io: SyncIOHandle, output_reason: usize, )
Start the PID callback loop driven by an asyn Float64 output port.
Identical to start_callback_loop but takes the output asyn
port handle directly and installs both the output writer and
the bumpless-transfer output reader from it via
set_output_port — mirroring C devEpidFast.c init_record,
which captures a single pPvt->pfloat64Output interface and uses
it for both read (devEpidFast.c:446-448) and write
(devEpidFast.c:471-473).
output_sync_io is the asyn Float64 output port handle and
output_reason the parameter index for the output channel.
Sourcepub fn start_from_asyn_interrupts(
&self,
interrupt_rx: Receiver<InterruptValue>,
input_reason: usize,
output_fn: Arc<Mutex<dyn FnMut(f64) + Send>>,
)
pub fn start_from_asyn_interrupts( &self, interrupt_rx: Receiver<InterruptValue>, input_reason: usize, output_fn: Arc<Mutex<dyn FnMut(f64) + Send>>, )
Start from an asyn interrupt subscription.
Subscribes to Float64 interrupts from the given broadcast sender and feeds them into the PID callback loop.
This bare-closure form leaves the bumpless-transfer output_reader
untouched — wire it separately with set_output_reader or
set_output_port, or use start_from_asyn_interrupts_with_port
to install both writer and reader from one asyn output port.
Sourcepub fn start_from_asyn_interrupts_with_port(
&self,
interrupt_rx: Receiver<InterruptValue>,
input_reason: usize,
output_sync_io: SyncIOHandle,
output_reason: usize,
)
pub fn start_from_asyn_interrupts_with_port( &self, interrupt_rx: Receiver<InterruptValue>, input_reason: usize, output_sync_io: SyncIOHandle, output_reason: usize, )
Start from an asyn interrupt subscription, driven by an asyn Float64 output port.
Identical to start_from_asyn_interrupts but takes the output
asyn port handle directly and installs both the output writer
and the bumpless-transfer output reader from it via
set_output_port — mirroring C devEpidFast.c init_record,
which captures a single pPvt->pfloat64Output interface and uses
it for both read (devEpidFast.c:446-448) and write
(devEpidFast.c:471-473).
Trait Implementations§
Source§impl Default for EpidFastDeviceSupport
impl Default for EpidFastDeviceSupport
Source§impl DeviceSupport for EpidFastDeviceSupport
impl DeviceSupport for EpidFastDeviceSupport
fn dtyp(&self) -> &str
Source§fn read(&mut self, record: &mut dyn Record) -> CaResult<DeviceReadOutcome>
fn read(&mut self, record: &mut dyn Record) -> CaResult<DeviceReadOutcome>
fn write(&mut self, _record: &mut dyn Record) -> CaResult<()>
fn init(&mut self, _record: &mut (dyn Record + 'static)) -> Result<(), CaError>
Source§fn last_alarm(&self) -> Option<(u16, u16)>
fn last_alarm(&self) -> Option<(u16, u16)>
Source§fn last_timestamp(&self) -> Option<SystemTime>
fn last_timestamp(&self) -> Option<SystemTime>
Source§fn set_process_context(&mut self, _ctx: &ProcessContext)
fn set_process_context(&mut self, _ctx: &ProcessContext)
read()
to push a read-only snapshot of framework-owned CommonFields
state (crate::server::record::ProcessContext) that the device
support needs. Read moreSource§fn set_record_info(&mut self, _name: &str, _scan: ScanType)
fn set_record_info(&mut self, _name: &str, _scan: ScanType)
Source§fn apply_record_info(&mut self, _info: &HashMap<String, String>)
fn apply_record_info(&mut self, _info: &HashMap<String, String>)
info("key", "value") directives from the .db
file to the device support. Default is a no-op; drivers that
react to specific tags (asyn asyn:READBACK, EtherCAT terminal
hints, etc.) override this. Called once after set_record_info
during builder wiring; not called again at runtime.Source§fn io_intr_receiver(&mut self) -> Option<Receiver<()>>
fn io_intr_receiver(&mut self) -> Option<Receiver<()>>
Source§fn write_begin(
&mut self,
_record: &mut (dyn Record + 'static),
) -> Result<Option<Box<dyn WriteCompletion>>, CaError>
fn write_begin( &mut self, _record: &mut (dyn Record + 'static), ) -> Result<Option<Box<dyn WriteCompletion>>, CaError>
Some(handle) if the write was submitted to a worker queue —
the caller should wait on the handle outside any record lock.
Returns None to fall back to synchronous write().Source§fn handle_command(
&mut self,
_record: &mut (dyn Record + 'static),
_command: &str,
_args: &[EpicsValue],
) -> Result<Vec<&'static str>, CaError>
fn handle_command( &mut self, _record: &mut (dyn Record + 'static), _command: &str, _args: &[EpicsValue], ) -> Result<Vec<&'static str>, CaError>
ProcessAction::DeviceCommand. This allows records to request
driver operations (e.g., scaler reset/arm/write_preset) without
holding a direct driver reference. Read more