visa_rs/
handler.rs

1//!
2//! Defines [`Callback`] trait used in [`Instrument::install_handler`](crate::Instrument::install_handler),
3//! which returns a [`Handler`] to manage lifetime of data passed
4//!
5//!
6//!
7
8use std::{
9    ptr::NonNull,
10    sync::mpsc::{Receiver, Sender},
11};
12use visa_sys as vs;
13
14use crate::{
15    enums::event,
16    session::{AsRawSs, BorrowedSs, FromRawSs},
17    Instrument, Result, SUCCESS,
18};
19
20/// Defines the ability for being passed to [`Instrument::install_handler`](crate::Instrument::install_handler)
21pub trait Callback {
22    type Output;
23    fn call(&mut self, instr: &Instrument, event: &event::Event) -> Self::Output;
24}
25
26impl<F, Out> Callback for F
27where
28    F: FnMut(&Instrument, &event::Event) -> Out,
29{
30    type Output = Out;
31    fn call(&mut self, instr: &Instrument, event: &event::Event) -> Self::Output {
32        self(instr, event)
33    }
34}
35
36struct CallbackPack<F: Callback> {
37    sender: Sender<F::Output>,
38    core: F,
39}
40
41impl<F: Callback> CallbackPack<F> {
42    fn from_callback(f: F) -> (Self, Receiver<F::Output>) {
43        let (sender, receiver) = std::sync::mpsc::channel();
44        (Self { sender, core: f }, receiver)
45    }
46    fn call(&mut self, instr: &Instrument, event: &event::Event) -> vs::ViStatus {
47        //Normally, an application should always return VI_SUCCESS from all callback handlers. If a specific handler does not want other handlers to be invoked for the given event for the given session, it should return VI_SUCCESS_NCHAIN. No return value from a handler on one session will affect callbacks on other sessions. Future versions of VISA (or specific implementations of VISA) may take actions based on other return values, so a user should return VI_SUCCESS from handlers unless there is a specific reason to do otherwise.
48        self.sender
49            .send(self.core.call(instr, event))
50            .expect("receiver side should be valid");
51        SUCCESS
52    }
53}
54
55struct CallbackWrapper<F: Callback> {
56    f: NonNull<CallbackPack<F>>,
57    //? not sure if reproduce from F would get the same fn pointer, so better hold it
58    hold: unsafe extern "system" fn(
59        vs::ViSession,
60        vs::ViEventType,
61        vs::ViEvent,
62        *mut std::ffi::c_void,
63    ) -> vs::ViStatus,
64}
65fn split_pack<C: Callback>(
66    pack: CallbackPack<C>,
67) -> (
68    std::ptr::NonNull<CallbackPack<C>>,
69    unsafe extern "system" fn(
70        vs::ViSession,
71        vs::ViEventType,
72        vs::ViEvent,
73        *mut std::ffi::c_void,
74    ) -> vs::ViStatus,
75) {
76    use std::ffi::c_void;
77    let data = Box::into_raw(Box::new(pack));
78    unsafe extern "system" fn trampoline<T: Callback>(
79        instr: vs::ViSession,
80        event_type: vs::ViEventType,
81        event: vs::ViEvent,
82        user_data: *mut c_void,
83    ) -> vs::ViStatus {
84        let pack: &mut CallbackPack<T> = &mut *(user_data as *mut CallbackPack<T>);
85        let instr = Instrument::from_raw_ss(instr);
86        let event = event::Event::new(event, event_type);
87        let ret = pack.call(&instr, &event);
88        std::mem::forget(event); // The VISA system automatically invokes the viClose() operation on the event context when a user handler returns. Because the event context must still be valid after the user handler returns (so that VISA can free it up), an application should not invoke the viClose() operation on an event context passed to a user handler.
89        std::mem::forget(instr); // ? no sure yet, in official example session not closed
90
91        ret
92    }
93
94    (
95        NonNull::new(data).expect("impossible to pass in a null ptr"),
96        trampoline::<C>,
97    )
98}
99impl<F: Callback> CallbackWrapper<F> {
100    pub(crate) fn new(f: F) -> (Self, Receiver<F::Output>) {
101        let (pack, receiver) = CallbackPack::from_callback(f);
102        let (data, fun) = split_pack(pack);
103        (Self { f: data, hold: fun }, receiver)
104    }
105}
106
107/// Lifetime manager for [`Callback`], will uninstall the callback when dropped.
108///
109/// Internally hold a [`Receiver`] (accessed by [`Self::receiver`]) to receive output of callback from visa.
110pub struct Handler<'b, F: Callback> {
111    instr: BorrowedSs<'b>,
112    rec: Receiver<F::Output>,
113    event_kind: event::EventKind,
114    callback: CallbackWrapper<F>,
115}
116
117impl<'b, F: Callback> Handler<'b, F> {
118    pub(crate) fn new(
119        instr: BorrowedSs<'b>,
120        event_kind: event::EventKind,
121        callback: F,
122    ) -> Result<Self> {
123        let (callback, rec) = CallbackWrapper::new(callback);
124        super::wrap_raw_error_in_unsafe!(vs::viInstallHandler(
125            instr.as_raw_ss(),
126            event_kind as _,
127            Some(callback.hold),
128            callback.f.as_ptr() as _
129        ))?;
130        Ok(Self {
131            instr,
132            rec,
133            event_kind,
134            callback,
135        })
136    }
137}
138
139impl<'b, F: Callback> Drop for Handler<'b, F> {
140    fn drop(&mut self) {
141        unsafe {
142            vs::viUninstallHandler(
143                self.instr.as_raw_ss(),
144                self.event_kind as _,
145                Some(self.callback.hold),
146                self.callback.f.as_ptr() as _,
147            );
148            drop(Box::from_raw(self.callback.f.as_ptr()));
149        }
150    }
151}
152
153impl<'b, F: Callback> Handler<'b, F> {
154    pub fn uninstall(self) {}
155}
156
157impl<'b, F: Callback> AsRef<Receiver<F::Output>> for Handler<'b, F> {
158    fn as_ref(&self) -> &Receiver<F::Output> {
159        &self.rec
160    }
161}
162
163impl<'b, F: Callback> Handler<'b, F> {
164    pub fn receiver(&self) -> &Receiver<F::Output> {
165        self.as_ref()
166    }
167}