use crate::{Error, Phidget, Result, ReturnCode};
use phidget_sys::{self as ffi, PhidgetHandle, PhidgetStepperHandle as StepperHandle};
use std::{
ffi::{c_int, c_uint, c_void},
mem, ptr,
time::Duration,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u32)]
pub enum ControlMode {
Step = ffi::PhidgetStepper_ControlMode_CONTROL_MODE_STEP,
Run = ffi::PhidgetStepper_ControlMode_CONTROL_MODE_RUN,
}
impl TryFrom<u32> for ControlMode {
type Error = Error;
fn try_from(val: u32) -> Result<Self> {
use ControlMode::*;
match val {
ffi::PhidgetStepper_ControlMode_CONTROL_MODE_STEP => Ok(Step),
ffi::PhidgetStepper_ControlMode_CONTROL_MODE_RUN => Ok(Run),
_ => Err(ReturnCode::UnknownVal),
}
}
}
pub type AttachCallback = dyn Fn(&mut Stepper) + Send + 'static;
pub type DetachCallback = dyn Fn(&mut Stepper) + Send + 'static;
pub type PositionChangeCallback = dyn Fn(&Stepper, f64) + Send + 'static;
pub type VelocityChangeCallback = dyn Fn(&Stepper, f64) + Send + 'static;
pub type StoppedCallback = dyn Fn(&Stepper) + Send + 'static;
pub struct Stepper {
chan: StepperHandle,
cb: Option<*mut c_void>,
attach_cb: Option<*mut c_void>,
detach_cb: Option<*mut c_void>,
}
impl Stepper {
pub fn new() -> Self {
let mut chan: StepperHandle = ptr::null_mut();
unsafe {
ffi::PhidgetStepper_create(&mut chan);
}
Self::from(chan)
}
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 mut sensor = Self::from(phid as StepperHandle);
cb(&mut sensor);
mem::forget(sensor);
}
}
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 mut sensor = Self::from(phid as StepperHandle);
cb(&mut sensor);
mem::forget(sensor);
}
}
pub fn as_channel(&self) -> &StepperHandle {
&self.chan
}
pub fn enable_failsafe(&self, failsafe_time: Duration) -> Result<()> {
let ms = u32::try_from(failsafe_time.as_millis()).map_err(|_| ReturnCode::InvalidArg)?;
ReturnCode::result(unsafe { ffi::PhidgetStepper_enableFailsafe(self.chan, ms) })?;
Ok(())
}
pub fn min_failsafe_time(&self) -> Result<Duration> {
let mut val: u32 = 0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getMinFailsafeTime(self.chan, &mut val) })?;
Ok(Duration::from_millis(val.into()))
}
pub fn max_failsafe_time(&self) -> Result<Duration> {
let mut val: u32 = 0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getMaxFailsafeTime(self.chan, &mut val) })?;
Ok(Duration::from_millis(val.into()))
}
pub fn reset_failsafe(&self) -> Result<()> {
ReturnCode::result(unsafe { ffi::PhidgetStepper_resetFailsafe(self.chan) })?;
Ok(())
}
pub fn add_position_offset(&self, position_offset: f64) -> Result<()> {
ReturnCode::result(unsafe {
ffi::PhidgetStepper_addPositionOffset(self.chan, position_offset)
})?;
Ok(())
}
pub fn set_acceleration(&self, acceleration: f64) -> Result<()> {
ReturnCode::result(unsafe {
ffi::PhidgetStepper_setAcceleration(self.chan, acceleration)
})?;
Ok(())
}
pub fn acceleration(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getAcceleration(self.chan, &mut value) })?;
Ok(value)
}
pub fn min_acceleration(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe {
ffi::PhidgetStepper_getMinAcceleration(self.chan, &mut value)
})?;
Ok(value)
}
pub fn max_acceleration(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe {
ffi::PhidgetStepper_getMaxAcceleration(self.chan, &mut value)
})?;
Ok(value)
}
pub fn set_control_mode(&self, control_mode: ControlMode) -> Result<()> {
ReturnCode::result(unsafe {
ffi::PhidgetStepper_setControlMode(self.chan, control_mode as c_uint)
})?;
Ok(())
}
pub fn control_mode(&self) -> Result<ControlMode> {
let mut cm: ffi::PhidgetStepper_ControlMode = 0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getControlMode(self.chan, &mut cm) })?;
ControlMode::try_from(cm)
}
pub fn set_current_limit(&self, current_limit: f64) -> Result<()> {
ReturnCode::result(unsafe {
ffi::PhidgetStepper_setCurrentLimit(self.chan, current_limit)
})?;
Ok(())
}
pub fn current_limit(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getCurrentLimit(self.chan, &mut value) })?;
Ok(value)
}
pub fn min_current_limit(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe {
ffi::PhidgetStepper_getMinCurrentLimit(self.chan, &mut value)
})?;
Ok(value)
}
pub fn max_current_limit(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe {
ffi::PhidgetStepper_getMaxCurrentLimit(self.chan, &mut value)
})?;
Ok(value)
}
pub fn set_data_interval(&self, data_interval: u32) -> Result<()> {
ReturnCode::result(unsafe {
ffi::PhidgetStepper_setDataInterval(self.chan, data_interval)
})?;
Ok(())
}
pub fn data_interval(&self) -> Result<u32> {
let mut value = 0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getDataInterval(self.chan, &mut value) })?;
Ok(value)
}
pub fn min_data_interval(&self) -> Result<u32> {
let mut value = 0;
ReturnCode::result(unsafe {
ffi::PhidgetStepper_getMinDataInterval(self.chan, &mut value)
})?;
Ok(value)
}
pub fn max_data_interval(&self) -> Result<u32> {
let mut value = 0;
ReturnCode::result(unsafe {
ffi::PhidgetStepper_getMaxDataInterval(self.chan, &mut value)
})?;
Ok(value)
}
pub fn set_data_rate(&self, data_rate: f64) -> Result<()> {
ReturnCode::result(unsafe { ffi::PhidgetStepper_setDataRate(self.chan, data_rate) })?;
Ok(())
}
pub fn data_rate(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getDataRate(self.chan, &mut value) })?;
Ok(value)
}
pub fn min_data_rate(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getMinDataRate(self.chan, &mut value) })?;
Ok(value)
}
pub fn max_data_rate(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getMaxDataRate(self.chan, &mut value) })?;
Ok(value)
}
pub fn set_engaged(&self, engaged: bool) -> Result<()> {
let value = engaged as c_int;
ReturnCode::result(unsafe { ffi::PhidgetStepper_setEngaged(self.chan, value) })?;
Ok(())
}
pub fn engaged(&self) -> Result<bool> {
let mut value: c_int = 0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getEngaged(self.chan, &mut value) })?;
Ok(value != 0)
}
pub fn set_holding_current_limit(&self, holding_current_limit: f64) -> Result<()> {
ReturnCode::result(unsafe {
ffi::PhidgetStepper_setHoldingCurrentLimit(self.chan, holding_current_limit)
})?;
Ok(())
}
pub fn holding_current_limit(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe {
ffi::PhidgetStepper_getHoldingCurrentLimit(self.chan, &mut value)
})?;
Ok(value)
}
pub fn is_moving(&self) -> Result<bool> {
let mut value = 0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getIsMoving(self.chan, &mut value) })?;
Ok(value != 0)
}
pub fn position(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getPosition(self.chan, &mut value) })?;
Ok(value)
}
pub fn min_position(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getMinPosition(self.chan, &mut value) })?;
Ok(value)
}
pub fn max_position(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getMaxPosition(self.chan, &mut value) })?;
Ok(value)
}
pub fn set_rescale_factor(&self, rescale_factor: f64) -> Result<()> {
ReturnCode::result(unsafe {
ffi::PhidgetStepper_setRescaleFactor(self.chan, rescale_factor)
})?;
Ok(())
}
pub fn rescale_factor(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getRescaleFactor(self.chan, &mut value) })?;
Ok(value)
}
pub fn set_target_position(&self, stepper: f64) -> Result<()> {
ReturnCode::result(unsafe { ffi::PhidgetStepper_setTargetPosition(self.chan, stepper) })?;
Ok(())
}
pub fn target_position(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe {
ffi::PhidgetStepper_getTargetPosition(self.chan, &mut value)
})?;
Ok(value)
}
pub fn set_velocity_limit(&self, velocity_limit: f64) -> Result<()> {
ReturnCode::result(unsafe {
ffi::PhidgetStepper_setVelocityLimit(self.chan, velocity_limit)
})?;
Ok(())
}
pub fn velocity_limit(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe { ffi::PhidgetStepper_getVelocityLimit(self.chan, &mut value) })?;
Ok(value)
}
pub fn min_velocity_limit(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe {
ffi::PhidgetStepper_getMinVelocityLimit(self.chan, &mut value)
})?;
Ok(value)
}
pub fn max_velocity_limit(&self) -> Result<f64> {
let mut value = 0.0;
ReturnCode::result(unsafe {
ffi::PhidgetStepper_getMaxVelocityLimit(self.chan, &mut value)
})?;
Ok(value)
}
unsafe extern "C" fn on_position_change(chan: StepperHandle, ctx: *mut c_void, stepper: f64) {
if !ctx.is_null() {
let cb: &mut Box<PositionChangeCallback> = &mut *(ctx as *mut _);
let sensor = Self::from(chan);
cb(&sensor, stepper);
mem::forget(sensor);
}
}
pub fn set_on_position_change_handler<F>(&mut self, cb: F) -> Result<()>
where
F: Fn(&Stepper, f64) + Send + 'static,
{
let cb: Box<Box<PositionChangeCallback>> = Box::new(Box::new(cb));
let ctx = Box::into_raw(cb) as *mut c_void;
self.cb = Some(ctx);
ReturnCode::result(unsafe {
ffi::PhidgetStepper_setOnPositionChangeHandler(
self.chan,
Some(Self::on_position_change),
ctx,
)
})
}
unsafe extern "C" fn on_stopped(chan: StepperHandle, ctx: *mut c_void) {
if !ctx.is_null() {
let cb: &mut Box<StoppedCallback> = &mut *(ctx as *mut _);
let sensor = Self::from(chan);
cb(&sensor);
mem::forget(sensor);
}
}
pub fn set_on_stopped_handler<F>(&mut self, cb: F) -> Result<()>
where
F: Fn(&Stepper) + Send + 'static,
{
let cb: Box<Box<StoppedCallback>> = Box::new(Box::new(cb));
let ctx = Box::into_raw(cb) as *mut c_void;
self.cb = Some(ctx);
ReturnCode::result(unsafe {
ffi::PhidgetStepper_setOnStoppedHandler(self.chan, Some(Self::on_stopped), ctx)
})
}
unsafe extern "C" fn on_velocity_change(chan: StepperHandle, ctx: *mut c_void, stepper: f64) {
if !ctx.is_null() {
let cb: &mut Box<VelocityChangeCallback> = &mut *(ctx as *mut _);
let sensor = Self::from(chan);
cb(&sensor, stepper);
mem::forget(sensor);
}
}
pub fn set_on_velocity_change_handler<F>(&mut self, cb: F) -> Result<()>
where
F: Fn(&Stepper, f64) + Send + 'static,
{
let cb: Box<Box<VelocityChangeCallback>> = Box::new(Box::new(cb));
let ctx = Box::into_raw(cb) as *mut c_void;
self.cb = Some(ctx);
ReturnCode::result(unsafe {
ffi::PhidgetStepper_setOnVelocityChangeHandler(
self.chan,
Some(Self::on_velocity_change),
ctx,
)
})
}
pub fn set_on_attach_handler<F>(&mut self, cb: F) -> Result<()>
where
F: Fn(&mut Stepper) + 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(self.as_mut_handle(), Some(Self::on_attach), ctx)
})?;
self.attach_cb = Some(ctx);
Ok(())
}
pub fn set_on_detach_handler<F>(&mut self, cb: F) -> Result<()>
where
F: Fn(&mut Stepper) + 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(self.as_mut_handle(), Some(Self::on_detach), ctx)
})?;
self.detach_cb = Some(ctx);
Ok(())
}
}
impl Phidget for Stepper {
fn as_mut_handle(&mut self) -> PhidgetHandle {
self.chan as PhidgetHandle
}
fn as_handle(&self) -> PhidgetHandle {
self.chan as PhidgetHandle
}
}
unsafe impl Send for Stepper {}
impl Default for Stepper {
fn default() -> Self {
Self::new()
}
}
impl From<StepperHandle> for Stepper {
fn from(chan: StepperHandle) -> Self {
Self {
chan,
cb: None,
attach_cb: None,
detach_cb: None,
}
}
}
impl Drop for Stepper {
fn drop(&mut self) {
if let Ok(true) = self.is_open() {
let _ = self.close();
}
unsafe {
ffi::PhidgetStepper_delete(&mut self.chan);
crate::drop_cb::<PositionChangeCallback>(self.cb.take());
crate::drop_cb::<VelocityChangeCallback>(self.cb.take());
crate::drop_cb::<StoppedCallback>(self.cb.take());
crate::drop_cb::<AttachCallback>(self.attach_cb.take());
crate::drop_cb::<DetachCallback>(self.detach_cb.take());
}
}
}