phidget/lib.rs
1// phidget-rs/src/lib.rs
2//
3// Copyright (c) 2023, Frank Pagliughi
4//
5// This file is part of the 'phidget-rs' library.
6//
7// Licensed under the MIT license:
8// <LICENSE or http://opensource.org/licenses/MIT>
9// This file may not be copied, modified, or distributed except according
10// to those terms.
11//
12
13//! Safe Rust bindings to the phidget22 library.
14//!
15//! # Basic usage
16//!
17//! This example shows how to access a simple Digital Input, connected to the first available channel of a Vint HUB.
18//! See the `examples` directory for more thorough code snippets.
19//! ```rust,no_run
20//! use phidget::{DigitalOutput, Phidget};
21//! # use std::time::Duration;
22//!
23//! // Create a handle to a Digital Output device
24//! let mut out = DigitalOutput::new();
25//! // Before opening the device, set its VINT hub port
26//! out.set_is_hub_port_device(true).unwrap();
27//! out.set_hub_port(0).unwrap();
28//!
29//! // Start connection. Make sure to handle the result
30//! // to check the device is available
31//! out.open_wait_default().unwrap();
32//!
33//! // Control the output device
34//! loop {
35//! println!("Turn on LED");
36//! out.set_state(1).unwrap();
37//! std::thread::sleep(Duration::from_secs(3));
38//!
39//! println!("Turn off LED");
40//! out.set_state(0).unwrap();
41//! std::thread::sleep(Duration::from_secs(3));
42//! }
43//! ```
44//!
45//! # Callbacks
46//! In order to activate an output phidget device depending on the state of other sensors,
47//! for instance by turning on an LED whenever another sensor detects something,
48//! you need to set a callback listening for sensor value changes, and keep a valid handle to the output device to set its state.
49//!
50//! The problem is, Phidget callbacks do run in a different thread. A Phidget handle can already be sent
51//! to a different thread, as it implements [Send], but it doesn't implement [Sync].
52//! Hence, if you desire to access the same handle from different callbacks, it has to be wrapped in a
53//! Sync container, such as a [Mutex](std::sync::Mutex).
54//!
55//! ```rust,no_run
56//! # use phidget::{Phidget, DigitalOutput, DigitalInput};
57//! # use std::sync::Mutex;
58//! # fn main()
59//! # {
60//! # // Open a digitalInput to detect a button
61//! let mut button = DigitalInput::new();
62//! # button.set_channel(0).unwrap();
63//! // Open the digital output where
64//! // a LED is connected to.
65//! // In this example, it is initialized
66//! // and wrapped in a Mutex
67//! let led = Mutex::new({
68//! let mut tmp = DigitalOutput::new();
69//! tmp.set_channel(1).unwrap();
70//! tmp.open_wait_default().unwrap();
71//! tmp
72//! });
73//!
74//! // Make the button alternate the LED state
75//! button.set_on_state_change_handler(move |_, s: u8| {
76//! let lock = led.lock().unwrap();
77//! match s {
78//! // Access the device inside the Mutex and change its state
79//! 0 => lock.set_state(0).unwrap(),
80//! _ => lock.set_state(1).unwrap()
81//! }
82//! }).unwrap();
83//! # }
84//! ```
85
86// Platform dependent, whether this is necessary
87#![allow(clippy::unnecessary_cast)]
88// Lints
89#![deny(
90 missing_docs,
91 missing_copy_implementations,
92 trivial_casts,
93 unstable_features,
94 unused_import_braces,
95 unused_qualifications
96)]
97
98use std::{
99 ffi::CStr,
100 os::raw::{c_char, c_uint, c_void},
101 ptr,
102 time::Duration,
103};
104
105pub use phidget_sys::{
106 self as ffi, PHIDGET_CHANNEL_ANY, PHIDGET_HUBPORTSPEED_AUTO, PHIDGET_HUBPORT_ANY,
107 PHIDGET_SERIALNUMBER_ANY, PHIDGET_TIMEOUT_DEFAULT, PHIDGET_TIMEOUT_INFINITE,
108};
109
110/// The error types for the crate
111pub mod errors;
112pub use crate::errors::*;
113
114/// The enumerated types for the crate
115pub mod types;
116pub use crate::types::*;
117
118/// The main Phidget trait
119pub mod phidget;
120pub use crate::phidget::{
121 AttachCallback, DetachCallback, GenericPhidget, Phidget, PhidgetFilter, PhidgetInfo, PhidgetRef,
122};
123
124/// Network API
125pub mod net;
126pub use crate::net::ServerType;
127
128/// Module containing all implemented devices
129pub mod devices;
130pub use crate::devices::{
131 current_input::CurrentInput, digital_input::DigitalInput, digital_output::DigitalOutput,
132 hub::Hub, humidity_sensor::HumiditySensor, pressure_sensor::PressureSensor,
133 temperature_sensor::TemperatureSensor, voltage_input::VoltageInput,
134 voltage_output::VoltageOutput, voltage_ratio_input::VoltageRatioInput,
135};
136
137pub mod manager;
138pub use crate::manager::{ManagerAttachCallback, ManagerDetachCallback, PhidgetManager};
139
140/// An infinite timeout (wait forever)
141pub const TIMEOUT_INFINITE: Duration = Duration::from_millis(PHIDGET_TIMEOUT_INFINITE as u64);
142
143/// The default timeout for the library
144pub const TIMEOUT_DEFAULT: Duration = Duration::from_millis(PHIDGET_TIMEOUT_DEFAULT as u64);
145
146/////////////////////////////////////////////////////////////////////////////
147/// Gets a string from a phidget22 call.
148/// This can be any function that takes a pointer to a c-str as the lone
149/// argument.
150pub(crate) fn get_ffi_string<F>(mut f: F) -> Result<String>
151where
152 F: FnMut(*mut *const c_char) -> c_uint,
153{
154 unsafe {
155 let mut ver: *const c_char = ptr::null_mut();
156 ReturnCode::result(f(&mut ver))?;
157 if ver.is_null() {
158 return Err(ReturnCode::NoMemory);
159 }
160 let s = CStr::from_ptr(ver);
161 Ok(s.to_string_lossy().into())
162 }
163}
164
165/// Release the memory held in a double-boxed callback function/lambda.
166pub(crate) fn drop_cb<P: ?Sized>(cb: Option<*mut c_void>) {
167 if let Some(ctx) = cb {
168 let _: Box<Box<P>> = unsafe { Box::from_raw(ctx as *mut _) };
169 }
170}
171
172/////////////////////////////////////////////////////////////////////////////
173
174/// The the full version of the phidget22 library as a string.
175/// This is something like, "Phidget22 - Version 1.14 - Built Mar 31 2023 22:44:59"
176pub fn library_version() -> Result<String> {
177 get_ffi_string(|s| unsafe { ffi::Phidget_getLibraryVersion(s) })
178}
179
180/// Gets just the version number of the phidget22 library as a string.
181/// This is something like, "1.14"
182pub fn library_version_number() -> Result<String> {
183 get_ffi_string(|s| unsafe { ffi::Phidget_getLibraryVersionNumber(s) })
184}
185
186/// Closes all channels, and stops all threads. The library is reset to a
187/// newly loaded state. All channel handles have been freed.
188///
189/// This function is intended for use in special cases where the library
190/// cannot be unloaded between program runs, such as LabVIEW and Unity
191/// Editor.
192///
193/// # Safety
194///
195/// This invalidates all Phidget objects that are running.
196///
197pub unsafe fn reset_library() -> Result<()> {
198 ReturnCode::result(unsafe { ffi::Phidget_resetLibrary() })
199}
200
201/////////////////////////////////////////////////////////////////////////////
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn test_library_queries() {
209 assert!(library_version().is_ok());
210 assert!(library_version_number().is_ok());
211 }
212}