ni_syscfg/
session.rs

1use ni_syscfg_sys::*;
2use std::ffi::CString;
3use std::time::Duration;
4
5use crate::error::{api_status, Result};
6use crate::experts::ExpertType;
7use crate::handles::close_handle;
8use crate::hardware_filter::{FilterMode, HardwareFilter};
9use crate::parameters::ApiBool;
10use crate::resources::HardwareResourceList;
11
12#[repr(i32)]
13#[derive(Clone, Copy, Debug)]
14pub enum Locale {
15    Default = NISysCfgLocale_NISysCfgLocaleDefault,
16    ChineseSimplified = NISysCfgLocale_NISysCfgLocaleChineseSimplified,
17    English = NISysCfgLocale_NISysCfgLocaleEnglish,
18    French = NISysCfgLocale_NISysCfgLocaleFrench,
19    German = NISysCfgLocale_NISysCfgLocaleGerman,
20    Japanese = NISysCfgLocale_NISysCfgLocaleJapanese,
21    Korean = NISysCfgLocale_NISysCfgLocaleKorean,
22}
23
24pub struct SessionConfig<'a> {
25    target: &'a str,
26    username: Option<CString>,
27    password: Option<CString>,
28    locale: Locale,
29    force_refresh: bool,
30    timeout: Duration,
31}
32
33impl<'a> SessionConfig<'a> {
34    pub fn new() -> Self {
35        Self {
36            target: "",
37            username: None,
38            password: None,
39            locale: Locale::Default,
40            force_refresh: false,
41            timeout: Duration::from_secs(1),
42        }
43    }
44
45    pub fn target(mut self, target: &'a str) -> Self {
46        self.target = target;
47        self
48    }
49
50    pub fn username(mut self, username: &str) -> Result<Self> {
51        self.username = Some(CString::new(username)?);
52        Ok(self)
53    }
54
55    pub fn password(mut self, password: &str) -> Result<Self> {
56        self.password = Some(CString::new(password)?);
57        Ok(self)
58    }
59
60    pub fn locale(mut self, locale: Locale) -> Self {
61        self.locale = locale;
62        self
63    }
64
65    pub fn force_refresh(mut self, force_refresh: bool) -> Self {
66        self.force_refresh = force_refresh;
67        self
68    }
69
70    pub fn timeout(mut self, timeout: Duration) -> Self {
71        self.timeout = timeout;
72        self
73    }
74
75    pub fn connect(&self) -> Result<Session> {
76        fn optional_cstring_to_ptr(input: &Option<CString>) -> *const i8 {
77            if let Some(inner) = input {
78                inner.as_ptr()
79            } else {
80                std::ptr::null()
81            }
82        }
83
84        let mut handle: NISysCfgSessionHandle = std::ptr::null_mut();
85
86        let username = optional_cstring_to_ptr(&self.username);
87
88        let password = optional_cstring_to_ptr(&self.password);
89
90        unsafe {
91            api_status(NISysCfgInitializeSession(
92                CString::new(self.target)?.as_ptr(),
93                username,
94                password,
95                self.locale as NISysCfgLocale,
96                ApiBool::from(self.force_refresh) as NISysCfgBool,
97                self.timeout.as_millis() as u32,
98                std::ptr::null_mut(),
99                &mut handle,
100            ))?;
101        }
102
103        Ok(Session::new_from_handle(handle))
104    }
105}
106
107/// Provides an interface to an active system configuration session.
108///
109/// This is created when you connect to a target using [SessionConfig]
110/// and allows you to access hardware and software resources through the API.
111pub struct Session {
112    handle: NISysCfgSessionHandle,
113}
114
115impl Session {
116    fn new_from_handle(handle: NISysCfgSessionHandle) -> Self {
117        Self { handle }
118    }
119
120    pub(crate) fn handle(&self) -> &NISysCfgSessionHandle {
121        &self.handle
122    }
123
124    /// Create a new filter for the session to use as part of [find_hardware]
125    pub fn create_filter(&self) -> Result<HardwareFilter> {
126        HardwareFilter::new(&self)
127    }
128
129    /// Find the hardware resources in the system, optionally with the filter settings.
130    ///
131    /// * [`filtering`] can provide a hardware filter or [None] for no filtering.
132    /// * [`experts`] can be a slice of [`ExpertType`] to limit results to specific types or [None] for all supported experts.
133    ///
134    /// # Example Without Filtering
135    /// ```
136    /// use ni_syscfg::SessionConfig;
137    ///
138    /// let session = SessionConfig::new().connect().unwrap();
139    ///
140    /// for hardware in session.find_hardware(None, None).unwrap() {
141    ///   println!("Found {}", hardware.name().unwrap())
142    /// }
143    /// ```
144    ///
145    /// # Example With Filtering
146    /// ```
147    /// use ni_syscfg::{SessionConfig, FilterMode};
148    ///
149    /// let session = SessionConfig::new().connect().unwrap();
150    /// let mut filter = session.create_filter().unwrap();
151    /// filter.set_mode(FilterMode::MatchValuesAny);
152    ///
153    /// for hardware in session.find_hardware(Some(&filter), None).unwrap() {
154    ///   println!("Found {}", hardware.name().unwrap())
155    /// }
156    /// ```
157    ///
158    /// # Example With DAQmx Expert Filter
159    /// ```
160    /// use ni_syscfg::{SessionConfig, ExpertType};
161    ///
162    /// let session = SessionConfig::new().connect().unwrap();
163    ///
164    /// for hardware in session.find_hardware(None, Some(&[ExpertType::NiDaqmx])).unwrap() {
165    ///   println!("Found {}", hardware.name().unwrap())
166    /// }
167    /// ```
168    pub fn find_hardware(
169        &self,
170        filtering: Option<&HardwareFilter>,
171        experts: Option<&[ExpertType]>,
172    ) -> Result<HardwareResourceList> {
173        let mut list_handle: NISysCfgEnumResourceHandle = std::ptr::null_mut();
174
175        let (filter_mode, filter_handle) = if let Some(filter) = filtering {
176            (filter.mode(), filter.handle())
177        } else {
178            (
179                FilterMode::MatchValuesAll,
180                std::ptr::null_mut() as NISysCfgFilterHandle,
181            )
182        };
183
184        let expert_list = if let Some(list) = experts {
185            expert_list_to_text(list)?
186        } else {
187            CString::new("")?
188        };
189
190        unsafe {
191            api_status(NISysCfgFindHardware(
192                self.handle,
193                filter_mode as i32,
194                filter_handle,
195                expert_list.as_ptr(),
196                &mut list_handle,
197            ))?;
198        }
199
200        Ok(HardwareResourceList::from_handle(list_handle, self))
201    }
202}
203
204/// Convert the expert list to a format expect by the API.
205fn expert_list_to_text(list: &[ExpertType]) -> Result<CString> {
206    let list_string = list
207        .iter()
208        .map(|ex| ex.to_programmatic_string())
209        .collect::<Vec<String>>()
210        .join(",");
211    Ok(CString::new(list_string)?)
212}
213
214impl Drop for Session {
215    fn drop(&mut self) {
216        let _ = close_handle(self.handle);
217    }
218}
219
220#[cfg(test)]
221mod tests {
222
223    use super::*;
224
225    #[test]
226    fn expert_list_to_string() {
227        //use a list of unknown so we know what it will produce.
228        let list = vec![
229            ExpertType::Unknown("test1".to_string()),
230            ExpertType::Unknown("test2".to_string()),
231        ];
232
233        let result = expert_list_to_text(&list).unwrap();
234
235        assert_eq!(result.to_str().unwrap(), "test1,test2");
236    }
237
238    #[test]
239    fn expert_list_to_string_single() {
240        //use a list of unknown so we know what it will produce.
241        let list = vec![ExpertType::Unknown("test1".to_string())];
242
243        let result = expert_list_to_text(&list).unwrap();
244
245        assert_eq!(result.to_str().unwrap(), "test1");
246    }
247
248    #[test]
249    fn expert_list_to_string_empty() {
250        //use a list of unknown so we know what it will produce.
251        let list = vec![];
252
253        let result = expert_list_to_text(&list).unwrap();
254
255        assert_eq!(result.to_str().unwrap(), "");
256    }
257}