use crate::{ChannelClass, DeviceClass, DeviceId, Error, Result, ReturnCode};
use phidget_sys::{self as ffi, PhidgetHandle};
use std::{
ffi::{c_char, c_int, c_void, CStr, CString},
ptr,
time::Duration,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub type AttachCallback = dyn Fn(&PhidgetRef) + Send + 'static;
pub type DetachCallback = dyn Fn(&PhidgetRef) + Send + 'static;
unsafe extern "C" fn on_attach(phid: PhidgetHandle, ctx: *mut c_void) {
if !ctx.is_null() {
let cb: &mut Box<AttachCallback> = &mut *(ctx as *mut _);
let ph = PhidgetRef::from(phid);
cb(&ph);
}
}
unsafe extern "C" fn on_detach(phid: PhidgetHandle, ctx: *mut c_void) {
if !ctx.is_null() {
let cb: &mut Box<DetachCallback> = &mut *(ctx as *mut _);
let ph = PhidgetRef::from(phid);
cb(&ph);
}
}
pub fn set_on_attach_handler<P, F>(ph: &mut P, cb: F) -> Result<*mut c_void>
where
P: Phidget,
F: Fn(&PhidgetRef) + Send + 'static,
{
let cb: Box<Box<AttachCallback>> = Box::new(Box::new(cb));
let ctx = Box::into_raw(cb) as *mut c_void;
ReturnCode::result(unsafe {
ffi::Phidget_setOnAttachHandler(ph.as_mut_handle(), Some(on_attach), ctx)
})?;
Ok(ctx)
}
pub fn set_on_detach_handler<P, F>(ph: &mut P, cb: F) -> Result<*mut c_void>
where
P: Phidget,
F: Fn(&PhidgetRef) + Send + 'static,
{
let cb: Box<Box<DetachCallback>> = Box::new(Box::new(cb));
let ctx = Box::into_raw(cb) as *mut c_void;
ReturnCode::result(unsafe {
ffi::Phidget_setOnDetachHandler(ph.as_mut_handle(), Some(on_detach), ctx)
})?;
Ok(ctx)
}
#[derive(Debug, Clone)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub struct PhidgetInfo {
pub channel_name: String,
pub channel_class: ChannelClass,
pub channel: i32,
pub device_name: String,
pub device_class: DeviceClass,
pub device_id: DeviceId,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "String::is_empty")
)]
pub device_label: String,
pub serial_number: i32,
pub is_hub_port_device: bool,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub hub_port: Option<i32>,
pub device_sku: String,
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
pub struct PhidgetFilter {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub channel: Option<i32>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub serial_number: Option<i32>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub is_hub_port_device: Option<bool>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub hub_port: Option<i32>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub device_label: Option<String>,
}
impl From<PhidgetInfo> for PhidgetFilter {
fn from(info: PhidgetInfo) -> Self {
Self {
channel: Some(info.channel),
serial_number: Some(info.serial_number),
is_hub_port_device: Some(info.is_hub_port_device),
hub_port: info.hub_port,
device_label: Some(info.device_label),
}
}
}
pub trait Phidget {
fn as_handle(&self) -> PhidgetHandle;
fn as_mut_handle(&mut self) -> PhidgetHandle;
fn open(&mut self) -> Result<()> {
ReturnCode::result(unsafe { ffi::Phidget_open(self.as_mut_handle()) })
}
fn open_wait(&mut self, to: Duration) -> Result<()> {
let ms = u32::try_from(to.as_millis()).map_err(|_| ReturnCode::InvalidArg)?;
ReturnCode::result(unsafe { ffi::Phidget_openWaitForAttachment(self.as_mut_handle(), ms) })
}
fn open_wait_default(&mut self) -> Result<()> {
self.open_wait(crate::TIMEOUT_DEFAULT)
}
fn close(&mut self) -> Result<()> {
ReturnCode::result(unsafe { ffi::Phidget_close(self.as_mut_handle()) })
}
fn is_open(&self) -> Result<bool> {
let mut open: c_int = 0;
ReturnCode::result(unsafe { ffi::Phidget_getIsOpen(self.as_handle(), &mut open) })?;
Ok(open != 0)
}
fn is_attached(&self) -> Result<bool> {
let mut attached: c_int = 0;
ReturnCode::result(unsafe { ffi::Phidget_getAttached(self.as_handle(), &mut attached) })?;
Ok(attached != 0)
}
fn is_local(&self) -> Result<bool> {
let mut local: c_int = 0;
ReturnCode::result(unsafe { ffi::Phidget_getIsLocal(self.as_handle(), &mut local) })?;
Ok(local != 0)
}
fn set_local(&mut self, local: bool) -> Result<()> {
let local = c_int::from(local);
ReturnCode::result(unsafe { ffi::Phidget_setIsLocal(self.as_mut_handle(), local) })
}
fn is_remote(&self) -> Result<bool> {
let mut rem: c_int = 0;
ReturnCode::result(unsafe { ffi::Phidget_getIsRemote(self.as_handle(), &mut rem) })?;
Ok(rem != 0)
}
fn set_remote(&mut self, rem: bool) -> Result<()> {
let rem = c_int::from(rem);
ReturnCode::result(unsafe { ffi::Phidget_setIsRemote(self.as_mut_handle(), rem) })
}
fn data_interval(&self) -> Result<Duration> {
let mut ms: u32 = 0;
ReturnCode::result(unsafe { ffi::Phidget_getDataInterval(self.as_handle(), &mut ms) })?;
Ok(Duration::from_millis(ms.into()))
}
fn set_data_interval(&mut self, interval: Duration) -> Result<()> {
let ms = u32::try_from(interval.as_millis()).map_err(|_| ReturnCode::InvalidArg)?;
ReturnCode::result(unsafe { ffi::Phidget_setDataInterval(self.as_mut_handle(), ms) })
}
fn min_data_interval(&self) -> Result<Duration> {
let mut ms: u32 = 0;
ReturnCode::result(unsafe { ffi::Phidget_getMinDataInterval(self.as_handle(), &mut ms) })?;
Ok(Duration::from_millis(ms.into()))
}
fn max_data_interval(&self) -> Result<Duration> {
let mut ms: u32 = 0;
ReturnCode::result(unsafe { ffi::Phidget_getMaxDataInterval(self.as_handle(), &mut ms) })?;
Ok(Duration::from_millis(ms.into()))
}
fn data_rate(&self) -> Result<f64> {
let mut freq: f64 = 0.0;
ReturnCode::result(unsafe { ffi::Phidget_getDataRate(self.as_handle(), &mut freq) })?;
Ok(freq)
}
fn set_data_rate(&mut self, freq: f64) -> Result<()> {
ReturnCode::result(unsafe { ffi::Phidget_setDataRate(self.as_mut_handle(), freq) })
}
fn min_data_rate(&self) -> Result<f64> {
let mut freq: f64 = 0.0;
ReturnCode::result(unsafe { ffi::Phidget_getMinDataRate(self.as_handle(), &mut freq) })?;
Ok(freq)
}
fn max_data_rate(&self) -> Result<f64> {
let mut freq: f64 = 0.0;
ReturnCode::result(unsafe { ffi::Phidget_getMaxDataRate(self.as_handle(), &mut freq) })?;
Ok(freq)
}
fn device_channel_count(&self, cls: ChannelClass) -> Result<u32> {
let mut n: u32 = 0;
let cls = cls as ffi::Phidget_ChannelClass;
ReturnCode::result(unsafe {
ffi::Phidget_getDeviceChannelCount(self.as_handle(), cls, &mut n)
})?;
Ok(n)
}
fn channel_class(&self) -> Result<ChannelClass> {
let mut cls = ffi::Phidget_ChannelClass_PHIDCHCLASS_NOTHING;
ReturnCode::result(unsafe { ffi::Phidget_getChannelClass(self.as_handle(), &mut cls) })?;
ChannelClass::try_from(cls)
}
fn channel_class_name(&self) -> Result<String> {
crate::get_ffi_string(|s| unsafe { ffi::Phidget_getChannelClassName(self.as_handle(), s) })
}
fn channel_name(&self) -> Result<String> {
crate::get_ffi_string(|s| unsafe { ffi::Phidget_getChannelName(self.as_handle(), s) })
}
fn device_class(&self) -> Result<DeviceClass> {
let mut cls = ffi::Phidget_DeviceClass_PHIDCLASS_NOTHING;
ReturnCode::result(unsafe { ffi::Phidget_getDeviceClass(self.as_handle(), &mut cls) })?;
DeviceClass::try_from(cls)
}
fn device_class_name(&self) -> Result<String> {
crate::get_ffi_string(|s| unsafe { ffi::Phidget_getDeviceClassName(self.as_handle(), s) })
}
fn device_id(&self) -> Result<DeviceId> {
let mut id = ffi::Phidget_DeviceID_PHIDID_NOTHING;
ReturnCode::result(unsafe { ffi::Phidget_getDeviceID(self.as_handle(), &mut id) })?;
DeviceId::try_from(id)
}
fn device_name(&self) -> Result<String> {
crate::get_ffi_string(|s| unsafe { ffi::Phidget_getDeviceName(self.as_handle(), s) })
}
fn info(&self) -> Result<PhidgetInfo> {
let info = PhidgetInfo {
channel_name: self.channel_name()?,
channel_class: self.channel_class()?,
channel: self.channel()?,
device_name: self.device_name()?,
device_class: self.device_class()?,
device_id: self.device_id()?,
device_label: self.device_label()?,
serial_number: self.serial_number()?,
is_hub_port_device: self.is_hub_port_device()?,
hub_port: self.hub_port().ok(),
device_sku: self.device_sku()?,
};
Ok(info)
}
fn set_filter(&mut self, filter: &PhidgetFilter) -> Result<()> {
if let Some(chan) = filter.channel {
self.set_channel(chan)?;
}
if let Some(sn) = filter.serial_number {
self.set_serial_number(sn)?;
}
if let Some(ihpd) = filter.is_hub_port_device {
self.set_is_hub_port_device(ihpd)?;
}
if let Some(hub_port) = filter.hub_port {
self.set_hub_port(hub_port)?;
}
if let Some(ref label) = filter.device_label {
self.set_device_label(label)?;
}
Ok(())
}
fn is_hub_port_device(&self) -> Result<bool> {
let mut on: c_int = 0;
ReturnCode::result(unsafe { ffi::Phidget_getIsHubPortDevice(self.as_handle(), &mut on) })?;
Ok(on != 0)
}
fn set_is_hub_port_device(&mut self, on: bool) -> Result<()> {
let on = c_int::from(on);
ReturnCode::result(unsafe { ffi::Phidget_setIsHubPortDevice(self.as_mut_handle(), on) })
}
fn hub_port(&self) -> Result<i32> {
let mut port: c_int = 0;
ReturnCode::result(unsafe { ffi::Phidget_getHubPort(self.as_handle(), &mut port) })?;
Ok(port as i32)
}
fn set_hub_port(&mut self, port: i32) -> Result<()> {
ReturnCode::result(unsafe { ffi::Phidget_setHubPort(self.as_mut_handle(), port as c_int) })
}
fn channel(&self) -> Result<i32> {
let mut ch: c_int = 0;
ReturnCode::result(unsafe { ffi::Phidget_getChannel(self.as_handle(), &mut ch) })?;
Ok(ch as i32)
}
fn set_channel(&mut self, chan: i32) -> Result<()> {
ReturnCode::result(unsafe { ffi::Phidget_setChannel(self.as_mut_handle(), chan as c_int) })
}
fn serial_number(&self) -> Result<i32> {
let mut n = 0;
ReturnCode::result(unsafe {
ffi::Phidget_getDeviceSerialNumber(self.as_handle(), &mut n)
})?;
Ok(n)
}
fn set_serial_number(&mut self, sn: i32) -> Result<()> {
ReturnCode::result(unsafe { ffi::Phidget_setDeviceSerialNumber(self.as_mut_handle(), sn) })
}
fn device_label(&self) -> Result<String> {
unsafe {
let mut lbl: *const c_char = ptr::null();
ReturnCode::result(ffi::Phidget_getDeviceLabel(self.as_handle(), &mut lbl))?;
Ok(match lbl.is_null() {
true => String::new(),
false => CStr::from_ptr(lbl).to_string_lossy().into_owned(),
})
}
}
fn set_device_label(&mut self, label: &str) -> Result<()> {
let label = CString::new(label)?;
ReturnCode::result(unsafe {
ffi::Phidget_setDeviceLabel(self.as_mut_handle(), label.as_ptr())
})
}
fn write_device_label(&mut self, label: &str) -> Result<()> {
let label = CString::new(label)?;
ReturnCode::result(unsafe {
ffi::Phidget_writeDeviceLabel(self.as_mut_handle(), label.as_ptr())
})
}
fn device_sku(&self) -> Result<String> {
unsafe {
let mut sku: *const c_char = ptr::null();
ReturnCode::result(ffi::Phidget_getDeviceSKU(self.as_handle(), &mut sku))?;
Ok(match sku.is_null() {
true => String::new(),
false => CStr::from_ptr(sku).to_string_lossy().into_owned(),
})
}
}
}
#[allow(missing_copy_implementations)]
pub struct PhidgetRef(PhidgetHandle);
impl PhidgetRef {
pub fn new(phid: PhidgetHandle) -> Self {
Self(phid)
}
}
impl Phidget for PhidgetRef {
fn as_mut_handle(&mut self) -> PhidgetHandle {
self.0
}
fn as_handle(&self) -> PhidgetHandle {
self.0
}
}
impl From<PhidgetHandle> for PhidgetRef {
fn from(phid: PhidgetHandle) -> Self {
Self::new(phid)
}
}
#[allow(missing_copy_implementations)]
pub struct GenericPhidget(PhidgetHandle);
impl Phidget for GenericPhidget {
fn as_mut_handle(&mut self) -> PhidgetHandle {
self.0
}
fn as_handle(&self) -> PhidgetHandle {
self.0
}
}
unsafe impl Send for GenericPhidget {}
impl Drop for GenericPhidget {
fn drop(&mut self) {
if let Ok(true) = self.is_open() {
let _ = self.close();
}
unsafe { ffi::Phidget_release(&mut self.0) };
}
}
impl TryFrom<&PhidgetRef> for GenericPhidget {
type Error = Error;
fn try_from(phid: &PhidgetRef) -> Result<Self> {
unsafe {
ReturnCode::result(ffi::Phidget_retain(phid.0))?;
}
Ok(Self(phid.0))
}
}
impl TryFrom<PhidgetRef> for GenericPhidget {
type Error = Error;
fn try_from(phid: PhidgetRef) -> Result<Self> {
Self::try_from(&phid)
}
}