#![cfg_attr(not(doctest), doc = include_str!("../README.md"))]
#![deny(
arithmetic_overflow,
missing_debug_implementations,
unused_unsafe,
unreachable_code,
clippy::panicking_unwrap,
clippy::missing_safety_doc
)]
#![allow(clippy::module_name_repetitions, clippy::option_if_let_else)]
#![cfg_attr(
not(test),
forbid(
clippy::panic,
clippy::todo,
clippy::unimplemented,
clippy::expect_used,
clippy::unwrap_used
)
)]
#[cfg(feature = "hmi")]
pub mod hmi;
#[cfg(feature = "hspo")]
pub mod hspo;
#[cfg(feature = "joint_conv")]
pub mod joints;
#[cfg(feature = "rmi")]
pub mod rmi;
#[cfg(feature = "stmo")]
pub mod stmo;
#[cfg(any(feature = "stmo", feature = "hspo", feature = "rmi", feature = "hmi"))]
mod thread_util;
#[cfg(any(feature = "stmo", feature = "hspo", feature = "rmi", feature = "hmi"))]
pub use thread_util::ThreadConfig;
#[cfg(feature = "py")]
pub mod py {
use super::{hmi, hspo, joints, rmi, stmo, thread_util};
use pyo3::prelude::*;
#[pyo3::pyclass(eq, eq_int, from_py_object)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LogLevel {
Off,
Error,
Warn,
Info,
Debug,
Trace,
}
impl From<LogLevel> for log::LevelFilter {
fn from(l: LogLevel) -> Self {
match l {
LogLevel::Off => log::LevelFilter::Off,
LogLevel::Error => log::LevelFilter::Error,
LogLevel::Warn => log::LevelFilter::Warn,
LogLevel::Info => log::LevelFilter::Info,
LogLevel::Debug => log::LevelFilter::Debug,
LogLevel::Trace => log::LevelFilter::Trace,
}
}
}
fn parse_rust_log_baseline() -> log::LevelFilter {
use log::LevelFilter::*;
let raw = std::env::var("RUST_LOG").ok();
match raw
.as_deref()
.map(str::trim)
.map(str::to_ascii_lowercase)
.as_deref()
{
Some("off") => Off,
Some("error") => Error,
Some("warn") | Some("warning") => Warn,
Some("info") => Info,
Some("debug") => Debug,
Some("trace") => Trace,
_ => Warn,
}
}
#[pyo3::pyfunction]
fn set_log_level(level: LogLevel) -> LogLevel {
let requested: log::LevelFilter = level.into();
let current = log::max_level();
let effective = if requested > current {
requested
} else {
current
};
log::set_max_level(effective);
match effective {
log::LevelFilter::Off => LogLevel::Off,
log::LevelFilter::Error => LogLevel::Error,
log::LevelFilter::Warn => LogLevel::Warn,
log::LevelFilter::Info => LogLevel::Info,
log::LevelFilter::Debug => LogLevel::Debug,
log::LevelFilter::Trace => LogLevel::Trace,
}
}
#[pyo3::pymodule(name = "_fanuc_core")]
fn py_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
let env_baseline = parse_rust_log_baseline();
let _ = env_logger::Builder::new()
.filter_level(log::LevelFilter::Trace)
.try_init();
log::set_max_level(env_baseline);
log::trace!("Initializing Python module fanuc_ucl._fanuc_core");
hmi::py::register_child_module(m)?;
stmo::py::register_child_module(m)?;
hspo::py::register_child_module(m)?;
rmi::py::register_child_module(m)?;
m.add_class::<thread_util::ThreadConfig>()?;
m.add_class::<joints::JointTemplate>()?;
m.add_class::<joints::JointFormat>()?;
m.add_class::<joints::JointType>()?;
m.add_class::<LogLevel>()?;
m.add_function(pyo3::wrap_pyfunction!(set_log_level, m)?)?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ResponseNotFulfilled;
impl std::fmt::Display for ResponseNotFulfilled {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Response has not been fulfilled yet")
}
}
impl std::error::Error for ResponseNotFulfilled {}
mod sealed {
pub trait Sealed {}
}
pub trait ResponseHandle:
sealed::Sealed + Future<Output = Result<Self::Ret, Self::Error>> + Send + Sync + 'static
{
type Ret;
type Error: std::error::Error;
fn is_set(&self) -> bool;
fn get(&self) -> Result<Self::Ret, Self::Error>;
fn timestamp(&self) -> Option<std::time::SystemTime>;
fn wait_timeout(&self, timeout: std::time::Duration) -> Result<Self::Ret, Self::Error>;
fn wait(&self) -> Result<Self::Ret, Self::Error>;
}
#[cfg(feature = "stmo")]
#[macro_export]
#[doc(hidden)]
macro_rules! bincode_enum {
($enm:ty : $int:ty) => {
impl bincode::Encode for $enm {
fn encode<E: bincode::enc::Encoder>(
&self,
encoder: &mut E,
) -> Result<(), bincode::error::EncodeError> {
(*self as $int).encode(encoder)
}
}
impl<Ctx> bincode::Decode<Ctx> for $enm {
fn decode<D: bincode::de::Decoder<Context = Ctx>>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
let variant_int = <$int as bincode::Decode<D::Context>>::decode(decoder)?;
match <$enm>::try_from(variant_int) {
Ok(v) => Ok(v),
_ => Err(bincode::error::DecodeError::UnexpectedVariant {
found: variant_int as u32,
type_name: stringify!($enm),
allowed: &bincode::error::AllowedEnumVariants::Allowed(&[]),
}),
}
}
}
impl<'de, Ctx> bincode::de::BorrowDecode<'de, Ctx> for $enm {
fn borrow_decode<D: bincode::de::Decoder<Context = Ctx>>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
<Self as bincode::Decode<Ctx>>::decode(decoder)
}
}
};
(with_default $enm:ty : $int:ty) => {
impl bincode::Encode for $enm {
fn encode<E: bincode::enc::Encoder>(
&self,
encoder: &mut E,
) -> Result<(), bincode::error::EncodeError> {
(*self as $int).encode(encoder)
}
}
impl<Ctx> bincode::Decode<Ctx> for $enm {
fn decode<D: bincode::de::Decoder<Context = Ctx>>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
let variant_int = <$int as bincode::Decode<D::Context>>::decode(decoder)?;
Ok(<$enm>::try_from(variant_int).unwrap_or_default())
}
}
impl<Ctx> bincode::de::BorrowDecode<'_, Ctx> for $enm {
fn borrow_decode<D: bincode::de::Decoder<Context = Ctx>>(
decoder: &mut D,
) -> Result<Self, bincode::error::DecodeError> {
<Self as bincode::Decode<Ctx>>::decode(decoder)
}
}
};
}