diff --git a/Cargo.toml b/Cargo.toml index 7341881..c4cb8f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ log = "0.4.8" [features] default = ["std", "serial2"] alloc = [] -std = [] +std = ["alloc"] rs4xx = ["serial2/rs4xx"] [workspace] diff --git a/dynamixel2-cli/src/bin/dynamixel2/main.rs b/dynamixel2-cli/src/bin/dynamixel2/main.rs index af461c8..5fb83e5 100644 --- a/dynamixel2-cli/src/bin/dynamixel2/main.rs +++ b/dynamixel2-cli/src/bin/dynamixel2/main.rs @@ -1,6 +1,6 @@ use std::path::Path; use std::time::{Duration, Instant}; -use dynamixel2::transport::serial2::Serial2Port; +use dynamixel2::serial2::SerialPort; mod logging; mod options; @@ -152,7 +152,7 @@ fn do_main(options: Options) -> Result<(), ()> { Ok(()) } -fn open_bus(options: &Options) -> Result, Vec, Serial2Port>, ()> { +fn open_bus(options: &Options) -> Result, Vec, SerialPort>, ()> { let bus = dynamixel2::Bus::open(&options.serial_port, options.baud_rate) .map_err(|e| log::error!("Failed to open serial port: {}: {}", options.serial_port.display(), e))?; log::debug!( diff --git a/src/bus.rs b/src/bus.rs index ddfcba6..857ac78 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -1,6 +1,6 @@ use core::time::Duration; use crate::endian::{read_u16_le, read_u32_le, read_u8_le}; -use crate::transport::Transport; +use crate::serial_port::SerialPort; use crate::{ReadError, TransferError, WriteError}; #[cfg(feature = "serial2")] @@ -13,24 +13,24 @@ use crate::messaging::Messenger; use crate::packet::{Packet, STATUS_HEADER_SIZE}; /// Dynamixel Protocol 2 communication bus. -pub struct Bus { +pub struct Bus { messenger: Messenger, } // impl core::fmt::Debug for Bus where - T: Transport + core::fmt::Debug, + T: SerialPort + core::fmt::Debug, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Bus") - .field("transport", &self.messenger.transport) + .field("serial_port", &self.messenger.serial_port) .field("baud_rate", &self.messenger.baud_rate) .finish_non_exhaustive() } } #[cfg(feature = "serial2")] -impl Bus, Vec, crate::transport::serial2::Serial2Port> { +impl Bus, Vec, serial2::SerialPort> { /// Open a serial port with the given baud rate. /// /// This will allocate a new read and write buffer of 128 bytes each. @@ -48,14 +48,14 @@ impl Bus, Vec, crate::transport::serial2::Serial2Port> { /// /// This will allocate a new read and write buffer of 128 bytes each. /// Use [`Self::with_buffers()`] if you want to use a custom buffers. - pub fn new(serial_port: serial2::SerialPort) -> Result> { + pub fn new(serial_port: serial2::SerialPort) -> std::io::Result { let messenger = Messenger::with_buffers(serial_port, vec![0; 128], vec![0; 128])?; Ok(Self { messenger }) } } #[cfg(feature = "serial2")] -impl Bus +impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, @@ -79,18 +79,18 @@ impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Create a new bus using pre-allocated buffers. /// /// The serial port must already be configured in raw mode with the correct baud rate, /// character size (8), parity (disabled) and stop bits (1). pub fn with_buffers( - transport: T, + serial_port: T, read_buffer: ReadBuffer, write_buffer: WriteBuffer, - ) -> Result> { - let messenger = Messenger::with_buffers(transport, read_buffer, write_buffer)?; + ) -> Result { + let messenger = Messenger::with_buffers(serial_port, read_buffer, write_buffer)?; Ok(Self { messenger }) } @@ -100,16 +100,16 @@ where /// and may disrupt the communication with the motors. /// In general, it should be safe to read and write to the bus manually in between instructions, /// if the response from the motors has already been received. - pub fn transport(&self) -> &T { - &self.messenger.transport + pub fn serial_port(&self) -> &T { + &self.messenger.serial_port } /// Consume this bus object to get ownership of the serial port. /// /// This discards any data in internal the read buffer of the bus object. /// This is normally not a problem, since all data in the read buffer is also discarded when transmitting a new command. - pub fn into_transport(self) -> T { - self.messenger.transport + pub fn into_serial_port(self) -> T { + self.messenger.serial_port } /// Get the baud rate of the bus. diff --git a/src/device.rs b/src/device.rs index 672a322..c27bba9 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,31 +1,31 @@ use crate::endian::read_u16_le; use crate::instructions::instruction_id; use crate::messaging::Messenger; -use crate::{InvalidParameterCount, Packet, ReadError, Transport, WriteError}; +use crate::{InvalidParameterCount, Packet, ReadError, SerialPort, WriteError}; use core::time::Duration; #[cfg(feature = "alloc")] use alloc::{borrow::ToOwned, vec::Vec}; /// Dynamixel [`Device`] for communicating with a [`Bus`]. -pub struct Device { +pub struct Device { messenger: Messenger, } impl core::fmt::Debug for Device where - T: Transport + core::fmt::Debug, + T: SerialPort + core::fmt::Debug, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Device") - .field("transport", &self.messenger.transport) + .field("serial_port", &self.messenger.serial_port) .field("baud_rate", &self.messenger.baud_rate) .finish_non_exhaustive() } } #[cfg(feature = "serial2")] -impl Device, Vec, crate::transport::serial2::Serial2Port> { +impl Device, Vec, serial2::SerialPort> { /// Open a serial port with the given baud rate. /// /// This will allocate a new read and write buffer of 128 bytes each. @@ -43,14 +43,14 @@ impl Device, Vec, crate::transport::serial2::Serial2Port> { /// /// This will allocate a new read and write buffer of 128 bytes each. /// Use [`Self::with_buffers()`] if you want to use a custom buffers. - pub fn new(serial_port: serial2::SerialPort) -> Result> { + pub fn new(serial_port: serial2::SerialPort) -> std::io::Result { let messenger = Messenger::with_buffers(serial_port, vec![0; 128], vec![0; 128])?; Ok(Self { messenger }) } } #[cfg(feature = "serial2")] -impl Device +impl Device where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, @@ -73,15 +73,15 @@ impl Device where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Create a new device using pre-allocated buffers. pub fn with_buffers( - transport: impl Into, + serial_port: T, read_buffer: ReadBuffer, write_buffer: WriteBuffer, - ) -> Result> { - let messenger = Messenger::with_buffers(transport, read_buffer, write_buffer)?; + ) -> Result { + let messenger = Messenger::with_buffers(serial_port, read_buffer, write_buffer)?; Ok(Device { messenger }) } @@ -91,16 +91,16 @@ where /// and may disrupt the communication with the motors. /// In general, it should be safe to read and write to the device manually in between instructions, /// if the response from the motors has already been received. - pub fn transport(&self) -> &T { - &self.messenger.transport + pub fn serial_port(&self) -> &T { + &self.messenger.serial_port } /// Consume this device object to get ownership of the serial port. /// /// This discards any data in internal the read buffer of the device object. /// This is normally not a problem, since all data in the read buffer is also discarded when transmitting a new command. - pub fn into_transport(self) -> T { - self.messenger.transport + pub fn into_serial_port(self) -> T { + self.messenger.serial_port } /// Get the baud rate of the device. diff --git a/src/error.rs b/src/error.rs index a4d7ab4..b9dee9f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,16 +1,6 @@ use crate::instructions::packet_id::BROADCAST; use core::fmt::{Debug, Display, Formatter, Result as FmtResult}; -/// An error that can occur while initializing a bus. -#[derive(Debug)] -pub enum InitializeError { - /// Failed to get the configuration of the serial port. - GetConfiguration(E), - - /// Failed to get the baud rate from the configuration of the serial port. - GetBaudRate(E), -} - /// An error that can occur during a read/write transfer. #[derive(Debug)] pub enum TransferError { @@ -56,9 +46,6 @@ pub enum ReadError { /// Failed to read from the serial port. Io(E), - /// A timeout occurred while waiting for a response. - Timeout, - /// The received message is invalid. InvalidMessage(InvalidMessage), @@ -301,8 +288,6 @@ impl InvalidParameterCount { } } -#[cfg(feature = "std")] -impl std::error::Error for InitializeError {} #[cfg(feature = "std")] impl std::error::Error for TransferError {} #[cfg(feature = "std")] @@ -458,18 +443,6 @@ impl From for InvalidMessage { } } -impl Display for InitializeError -where - E: Display, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { - match self { - Self::GetConfiguration(e) => write!(f, "failed to get configuration of serial port: {e}"), - Self::GetBaudRate(e) => write!(f, "failed to get baud rate of serial port: {e}"), - } - } -} - impl Display for TransferError where E: Display, @@ -521,7 +494,6 @@ where e.required_size, e.total_size ), Self::Io(e) => write!(f, "failed to read from serial port: {}", e), - Self::Timeout => write!(f, "timeout while waiting for response"), Self::InvalidMessage(e) => write!(f, "{}", e), Self::MotorError(e) => write!(f, "{}", e), } diff --git a/src/instructions/action.rs b/src/instructions/action.rs index 3c19eeb..bc7cc72 100644 --- a/src/instructions/action.rs +++ b/src/instructions/action.rs @@ -1,12 +1,12 @@ use super::{instruction_id, packet_id}; -use crate::transport::Transport; +use crate::serial_port::SerialPort; use crate::{Bus, Response, TransferError, WriteError}; impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Send an action command to trigger a previously registered instruction. /// diff --git a/src/instructions/bulk_read.rs b/src/instructions/bulk_read.rs index 79d1b30..991c346 100644 --- a/src/instructions/bulk_read.rs +++ b/src/instructions/bulk_read.rs @@ -1,6 +1,6 @@ use super::{instruction_id, packet_id, BulkReadData}; use crate::endian::{write_u16_le, write_u8_le}; -use crate::transport::Transport; +use crate::serial_port::SerialPort; use crate::{Bus, ReadError, Response, WriteError}; #[cfg(feature = "alloc")] @@ -11,7 +11,7 @@ impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Synchronously read arbitrary data ranges from multiple motors in one command. /// diff --git a/src/instructions/bulk_write.rs b/src/instructions/bulk_write.rs index 890c244..1b1fdf7 100644 --- a/src/instructions/bulk_write.rs +++ b/src/instructions/bulk_write.rs @@ -1,13 +1,13 @@ use super::{instruction_id, packet_id, BulkWriteData}; use crate::endian::{write_u16_le, write_u8_le}; -use crate::transport::Transport; +use crate::serial_port::SerialPort; use crate::{Bus, WriteError}; impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Synchronously write arbitrary data ranges to multiple motors. /// @@ -93,7 +93,7 @@ where mod tests { use super::*; - type Bus = crate::Bus, Vec, crate::transport::serial2::Serial2Port>; + type Bus = crate::Bus, Vec, serial2::SerialPort>; /// Ensure that `bulk_write` accepts a slice of `BulkWriteData`. /// diff --git a/src/instructions/clear.rs b/src/instructions/clear.rs index 8b0252a..c7c5d55 100644 --- a/src/instructions/clear.rs +++ b/src/instructions/clear.rs @@ -1,5 +1,5 @@ use super::{instruction_id, packet_id}; -use crate::transport::Transport; +use crate::serial_port::SerialPort; use crate::{Bus, Response, TransferError, WriteError}; /// The parameters for the CLEAR command to clear the revolution counter. @@ -9,7 +9,7 @@ impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Clear the multi-revolution counter of a motor. /// diff --git a/src/instructions/factory_reset.rs b/src/instructions/factory_reset.rs index cbdbf2b..b51f663 100644 --- a/src/instructions/factory_reset.rs +++ b/src/instructions/factory_reset.rs @@ -1,5 +1,5 @@ use super::{instruction_id, packet_id}; -use crate::transport::Transport; +use crate::serial_port::SerialPort; use crate::{Bus, Response, TransferError, WriteError}; /// The kind of factory reset to perform. @@ -20,7 +20,7 @@ impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Reset the settings of a motor to the factory defaults. /// diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index e46eee7..86c0682 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -38,7 +38,7 @@ mod sync_read; mod sync_write; mod write; -use crate::Transport; +use crate::SerialPort; pub use factory_reset::FactoryResetKind; pub use ping::Ping; @@ -113,7 +113,7 @@ fn read_response_if_not_broadcast( where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { if motor_id == packet_id::BROADCAST { Ok(crate::Response { diff --git a/src/instructions/ping.rs b/src/instructions/ping.rs index 09ecd53..00ae9a1 100644 --- a/src/instructions/ping.rs +++ b/src/instructions/ping.rs @@ -2,7 +2,7 @@ use core::time::Duration; use super::{instruction_id, packet_id}; use crate::bus::StatusPacket; -use crate::transport::Transport; +use crate::serial_port::SerialPort; use crate::{Bus, ReadError, Response, TransferError}; use crate::packet::Packet; @@ -42,7 +42,7 @@ impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Ping a specific motor by ID. /// @@ -89,11 +89,13 @@ where let response = response.try_into()?; on_response(response); }, - Err(ReadError::Timeout) => { + Err(ReadError::Io(e)) if T::is_timeout_error(&e) => { trace!("Ping response timed out."); return Ok(()); }, - Err(e) => return Err(e.into()), + Err(e) => { + return Err(e.into()); + } } } } diff --git a/src/instructions/read.rs b/src/instructions/read.rs index af10621..d669bfc 100644 --- a/src/instructions/read.rs +++ b/src/instructions/read.rs @@ -1,6 +1,6 @@ use super::instruction_id; use crate::endian::write_u16_le; -use crate::transport::Transport; +use crate::serial_port::SerialPort; use crate::{bus::StatusPacket, Bus, Response, TransferError}; use crate::packet::Packet; @@ -11,7 +11,7 @@ impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Read an arbitrary number of bytes from multiple motors. fn read_raw(&mut self, motor_id: u8, address: u16, count: u16) -> Result, TransferError> { diff --git a/src/instructions/reboot.rs b/src/instructions/reboot.rs index 6c885b9..0ccf950 100644 --- a/src/instructions/reboot.rs +++ b/src/instructions/reboot.rs @@ -1,12 +1,12 @@ use super::{instruction_id, packet_id}; -use crate::transport::Transport; +use crate::serial_port::SerialPort; use crate::{Bus, Response, TransferError, WriteError}; impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Send a reboot command to a specific motor. /// diff --git a/src/instructions/reg_write.rs b/src/instructions/reg_write.rs index 72dac9d..7d2b8c0 100644 --- a/src/instructions/reg_write.rs +++ b/src/instructions/reg_write.rs @@ -2,13 +2,13 @@ use super::{instruction_id, read_response_if_not_broadcast}; use crate::{Bus, Response, TransferError}; use crate::endian::{write_u16_le, write_u32_le}; -use crate::transport::Transport; +use crate::serial_port::SerialPort; impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Register a write of an arbitrary number of bytes, to be triggered later by an `action` command. /// diff --git a/src/instructions/sync_read.rs b/src/instructions/sync_read.rs index 9879aa8..838a837 100644 --- a/src/instructions/sync_read.rs +++ b/src/instructions/sync_read.rs @@ -1,6 +1,6 @@ use super::{instruction_id, packet_id}; use crate::endian::write_u16_le; -use crate::transport::Transport; +use crate::serial_port::SerialPort; use crate::{Bus, ReadError, Response, WriteError}; use crate::packet::Packet; @@ -11,7 +11,7 @@ impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Synchronously read an arbitrary number of bytes from multiple motors in one command. /// diff --git a/src/instructions/sync_write.rs b/src/instructions/sync_write.rs index 30c8a37..30d63bc 100644 --- a/src/instructions/sync_write.rs +++ b/src/instructions/sync_write.rs @@ -1,13 +1,13 @@ use super::{instruction_id, packet_id, SyncWriteData}; use crate::endian::{write_u16_le, write_u32_le}; -use crate::transport::Transport; +use crate::serial_port::SerialPort; use crate::{Bus, WriteError}; impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Synchronously write an arbitrary number of bytes to multiple motors. /// diff --git a/src/instructions/write.rs b/src/instructions/write.rs index 0828618..f146a5a 100644 --- a/src/instructions/write.rs +++ b/src/instructions/write.rs @@ -1,13 +1,13 @@ use super::{instruction_id, read_response_if_not_broadcast}; use crate::endian::{write_u16_le, write_u32_le}; -use crate::transport::Transport; +use crate::serial_port::SerialPort; use crate::{Bus, Response, TransferError}; impl Bus where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Write an arbitrary number of bytes to a specific motor. /// diff --git a/src/lib.rs b/src/lib.rs index 37938c6..f9d99cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,26 +26,31 @@ #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "serial2")] +/// Public re-export of the serial2 crate. +pub use serial2; + #[macro_use] mod log; pub mod checksum; pub mod instructions; -mod bytestuff; -mod endian; - mod bus; pub use bus::*; mod device; -mod error; -pub mod transport; pub use device::*; -mod packet; -pub use packet::Packet; -mod messaging; -pub use transport::Transport; +mod serial_port; +pub use serial_port::SerialPort; +mod error; pub use error::*; + +mod packet; +pub use packet::Packet; + +mod bytestuff; +mod endian; +mod messaging; diff --git a/src/messaging.rs b/src/messaging.rs index 7808a67..3bdaedd 100644 --- a/src/messaging.rs +++ b/src/messaging.rs @@ -1,12 +1,12 @@ use crate::checksum::calculate_checksum; use crate::endian::{read_u16_le, write_u16_le}; use crate::packet::{Packet, HEADER_PREFIX, INSTRUCTION_HEADER_SIZE, STATUS_HEADER_SIZE}; -use crate::{bytestuff, ReadError, Transport, WriteError}; +use crate::{bytestuff, ReadError, SerialPort, WriteError}; use core::time::Duration; pub struct Messenger { /// The underlying stream (normally a serial port). - pub(crate) transport: T, + pub(crate) serial_port: T, /// The baud rate of the serial port, if known. pub(crate) baud_rate: u32, @@ -28,25 +28,25 @@ impl Messenger where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Create a new [`Messenger`] using pre-allocated buffers. /// /// The serial port must already be configured in raw mode with the correct baud rate, /// character size (8), parity (disabled) and stop bits (1). pub fn with_buffers( - transport: impl Into, + serial_port: impl Into, read_buffer: ReadBuffer, write_buffer: WriteBuffer, - ) -> Result> { - let transport = transport.into(); - let baud_rate = transport.baud_rate()?; - Ok(Self::with_buffers_and_baud_rate(transport, read_buffer, write_buffer, baud_rate)) + ) -> Result { + let serial_port = serial_port.into(); + let baud_rate = serial_port.baud_rate()?; + Ok(Self::with_buffers_and_baud_rate(serial_port, read_buffer, write_buffer, baud_rate)) } /// Create a new bus using pre-allocated buffers. pub fn with_buffers_and_baud_rate( - transport: impl Into, + serial_port: impl Into, read_buffer: ReadBuffer, mut write_buffer: WriteBuffer, baud_rate: u32, @@ -57,7 +57,7 @@ where write_buffer.as_mut()[..4].copy_from_slice(&HEADER_PREFIX); Self { - transport: transport.into(), + serial_port: serial_port.into(), baud_rate, read_buffer, read_len: 0, @@ -68,7 +68,7 @@ where /// Set the baud rate of the underlying serial port. pub fn set_baud_rate(&mut self, baud_rate: u32) -> Result<(), T::Error> { - self.transport.set_baud_rate(baud_rate)?; + self.serial_port.set_baud_rate(baud_rate)?; self.baud_rate = baud_rate; Ok(()) } @@ -131,12 +131,12 @@ where // and read() can potentially read more than one reply per syscall. self.read_len = 0; self.used_bytes = 0; - self.transport.discard_input_buffer().map_err(WriteError::DiscardBuffer)?; + self.serial_port.discard_input_buffer().map_err(WriteError::DiscardBuffer)?; // Send message. let stuffed_message = &buffer[..checksum_index + 2]; trace!("sending instruction: {:02X?}", stuffed_message); - self.transport.write_all(stuffed_message).map_err(WriteError::Write)?; + self.serial_port.write_all(stuffed_message).map_err(WriteError::Write)?; Ok(()) } @@ -145,7 +145,7 @@ where // Check that the read buffer is large enough to hold atleast a status packet header. crate::error::BufferTooSmallError::check(P::HEADER_SIZE, self.read_buffer.as_mut().len())?; - self.transport.set_timeout(timeout).map_err(ReadError::Io)?; + let deadline = self.serial_port.make_deadline(timeout); let stuffed_message_len = loop { self.remove_garbage(); @@ -168,7 +168,8 @@ where } // Try to read more data into the buffer. - let new_data = self.transport.read(&mut self.read_buffer.as_mut()[self.read_len..])?; + let new_data = self.serial_port.read(&mut self.read_buffer.as_mut()[self.read_len..], &deadline) + .map_err(ReadError::Io)?; if new_data == 0 { continue; } @@ -210,7 +211,7 @@ impl Messenger where ReadBuffer: AsRef<[u8]> + AsMut<[u8]>, WriteBuffer: AsRef<[u8]> + AsMut<[u8]>, - T: Transport, + T: SerialPort, { /// Remove leading garbage data from the read buffer. fn remove_garbage(&mut self) { diff --git a/src/serial_port/mod.rs b/src/serial_port/mod.rs new file mode 100644 index 0000000..fb0f1ab --- /dev/null +++ b/src/serial_port/mod.rs @@ -0,0 +1,38 @@ +//! [`SerialPort`] trait to support reading/writing from different serial port implementations. + +use core::time::Duration; + +#[cfg(feature = "serial2")] +pub mod serial2; + +/// [`SerialPort`]s are used to communicate with the hardware by reading and writing data. +/// +/// The implementor of the trait must also configure the serial line to use 8 bits characters, 1 stop bit, no parity and no flow control. +pub trait SerialPort { + /// The error type returned by the serial port when reading, writing or setting the baud rate. + type Error; + + /// A point in time that can be used as a deadline for a I/O operations. + type Instant; + + /// Get the current baud rate of the serial port. + fn baud_rate(&self) -> Result; + + /// Set the baud rate of the serial port. + fn set_baud_rate(&mut self, baud_rate: u32) -> Result<(), Self::Error>; + + /// Discard the input buffer of the serial port. Maybe a no-op on some platforms. + fn discard_input_buffer(&mut self) -> Result<(), Self::Error>; + + /// Returns available bytes to read, blocking until at least one byte is available or the deadline expires. + fn read(&mut self, buffer: &mut [u8], deadline: &Self::Instant) -> Result; + + /// Write all bytes in the buffer to the serial port. + fn write_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error>; + + /// Make a deadline to expire after the given timeout. + fn make_deadline(&self, timeout: Duration) -> Self::Instant; + + /// Check if an error indicates a timeout. + fn is_timeout_error(error: &Self::Error) -> bool; +} diff --git a/src/serial_port/serial2.rs b/src/serial_port/serial2.rs new file mode 100644 index 0000000..e110a31 --- /dev/null +++ b/src/serial_port/serial2.rs @@ -0,0 +1,44 @@ +//! Trait implementation using the `serial2` crate. + +use std::time::{Duration, Instant}; + +impl crate::SerialPort for serial2::SerialPort { + type Error = std::io::Error; + + type Instant = std::time::Instant; + + fn baud_rate(&self) -> Result { + self.get_configuration()? + .get_baud_rate() + } + + fn set_baud_rate(&mut self, baud_rate: u32) -> Result<(), Self::Error> { + let mut settings = self.get_configuration()?; + settings.set_baud_rate(baud_rate)?; + self.set_configuration(&settings)?; + Ok(()) + } + + fn discard_input_buffer(&mut self) -> Result<(), Self::Error> { + serial2::SerialPort::discard_input_buffer(self) + } + + fn read(&mut self, buffer: &mut [u8], deadline: &Self::Instant) -> Result { + let timeout = deadline.checked_duration_since(Instant::now()) + .ok_or(std::io::ErrorKind::TimedOut)?; + self.set_read_timeout(timeout)?; + serial2::SerialPort::read(self, buffer) + } + + fn write_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + serial2::SerialPort::write_all(self, buffer) + } + + fn make_deadline(&self, timeout: Duration) -> Self::Instant { + Instant::now() + timeout + } + + fn is_timeout_error(error: &Self::Error) -> bool { + error.kind() == std::io::ErrorKind::TimedOut + } +} diff --git a/src/transport/mod.rs b/src/transport/mod.rs deleted file mode 100644 index f47dbeb..0000000 --- a/src/transport/mod.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! The [`Transport`] trait is used to implementing the Dynamixel Protocol 2.0 communication interface. - -#[cfg(feature = "serial2")] -pub mod serial2; - -use crate::ReadError; -use core::time::Duration; - -/// [`Transport`]s are used to communicate with the hardware via reading and writing data. -/// The Dynamixel Protocol 2.0 uses 8 bits char size, 1 stop bit, no parity. -pub trait Transport { - /// The error type returned by the transport when reading, writing or setting the baud rate. - type Error; - /// Get the current baud rate of the transport. - fn baud_rate(&self) -> Result>; - /// Set the baud rate of the transport. - fn set_baud_rate(&mut self, baud_rate: u32) -> Result<(), Self::Error>; - /// Discard the input buffer of the transport. Maybe a no-op on some platforms. - fn discard_input_buffer(&mut self) -> Result<(), Self::Error>; - /// Sets the timeout deadline and starts a timer. After the timeout duration elapses, the `[Self::read`] method will return with a timeout error. - fn set_timeout(&mut self, timeout: Duration) -> Result<(), Self::Error>; - /// Returns available bytes to read, blocking until at least one byte is available or the timeout duration elapses. The timeout must be set prior to calling with [`Self::set_timeout`]. - fn read(&mut self, buffer: &mut [u8]) -> Result>; - /// Write all bytes in the buffer to the transport. - fn write_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error>; -} diff --git a/src/transport/serial2.rs b/src/transport/serial2.rs deleted file mode 100644 index addc08c..0000000 --- a/src/transport/serial2.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! Serial port transport implementation using the `serial2` crate. - -use crate::ReadError; -use std::time::{Duration, Instant}; - -/// Re-exported `serial2` crate in case you need to modify serial port settings. -pub use serial2; - -/// A wrapper around a `serial2::SerialPort` that implements the `Transport` trait. -pub struct Serial2Port { - /// The serial port. - pub port: serial2::SerialPort, - /// The deadline for the next read operation. - pub deadline: Option, -} - -impl Serial2Port { - /// Create a new `Serial2Port` from a `serial2::SerialPort`. - pub fn new(port: serial2::SerialPort) -> Self { - Self { - port, - deadline: None, - } - } -} - -impl From for Serial2Port { - fn from(port: serial2::SerialPort) -> Self { - Self::new(port) - } -} - -impl core::fmt::Debug for Serial2Port { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> std::fmt::Result { - #[derive(Debug)] - #[allow(dead_code)] // Dead code analysis ignores derive debug impls, but that is the whole point of this struct. - enum Raw { - #[cfg(unix)] - Fd(std::os::unix::io::RawFd), - #[cfg(windows)] - Handle(std::os::windows::io::RawHandle), - } - - #[cfg(unix)] - let raw = { - use std::os::unix::io::AsRawFd; - Raw::Fd(self.port.as_raw_fd()) - }; - #[cfg(windows)] - let raw = { - use std::os::windows::io::AsRawHandle; - Raw::Handle(self.port.as_raw_handle()) - }; - write!(f, "SerialPort({:?})", raw) - } -} - -impl crate::Transport for Serial2Port { - type Error = std::io::Error; - - fn baud_rate(&self) -> Result> { - self.port.get_configuration() - .map_err(crate::InitializeError::GetConfiguration)? - .get_baud_rate() - .map_err(crate::InitializeError::GetBaudRate) - } - - fn set_baud_rate(&mut self, baud_rate: u32) -> Result<(), Self::Error> { - let mut settings = self.port.get_configuration()?; - settings.set_baud_rate(baud_rate)?; - self.port.set_configuration(&settings)?; - Ok(()) - } - - fn discard_input_buffer(&mut self) -> Result<(), Self::Error> { - self.port.discard_input_buffer() - } - - fn set_timeout(&mut self, timeout: Duration) -> Result<(), Self::Error> { - self.deadline = Some(Instant::now() + timeout); - Ok(()) - } - - fn read(&mut self, buffer: &mut [u8]) -> Result> { - let timeout = self.deadline.ok_or(ReadError::Timeout)?.checked_duration_since(Instant::now()).ok_or(ReadError::Timeout)?; - self.port.set_read_timeout(timeout).map_err(ReadError::Io)?; - match self.port.read(buffer) { - Err(e) if e.kind() == std::io::ErrorKind::TimedOut => Err(ReadError::Timeout), - Err(e) => Err(ReadError::Io(e)), - Ok(count) => Ok(count), - } - } - - fn write_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.port.write_all(buffer) - } -} diff --git a/tests/device.rs b/tests/device.rs index df45221..e3a0c3b 100644 --- a/tests/device.rs +++ b/tests/device.rs @@ -1,24 +1,25 @@ -mod mock_transport; - -use crate::mock_transport::MockSerialPort; -use dynamixel2::{Bus, Device, Instructions, ReadError, TransferError, Transport}; +use assert2::{assert, let_assert}; +use dynamixel2::{Bus, Device, Instructions, ReadError, SerialPort}; use log::{info, trace}; +use std::sync::Arc; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering::Relaxed; -use std::sync::Arc; use std::thread; use std::time::Duration; use test_log::test; +mod mock_serial_port; +use crate::mock_serial_port::MockSerialPort; + type ReadBuffer = Vec; type WriteBuffer = Vec; type T = MockSerialPort; fn setup_bus() -> (Bus, Device) { - let transport = MockSerialPort::new(56700); - let device_transport = transport.device_port(); + let serial_port = MockSerialPort::new(56700); + let device_serial_port = serial_port.device_port(); ( - Bus::with_buffers(transport, vec![0; 1024], vec![0; 1024]).unwrap(), - Device::with_buffers(device_transport, vec![0; 1024], vec![0; 1024]).unwrap(), + Bus::with_buffers(serial_port, vec![0; 1024], vec![0; 1024]).unwrap(), + Device::with_buffers(device_serial_port, vec![0; 1024], vec![0; 1024]).unwrap(), ) } @@ -61,24 +62,21 @@ fn test_packet_response() { let kill_device = Arc::new(AtomicBool::new(false)); let (mut bus, mut device) = setup_bus(); let bus_t = thread::spawn(move || { - bus.write_u8(1, 5, 1)?; - let res = bus.read_u8(1, 5)?; - assert_eq!(res.data, 1); - Ok::<(), TransferError<::Error>>(()) + assert!(let Ok(_) = bus.write_u8(1, 5, 1)); + let_assert!(Ok(response) = bus.read_u8(1, 5)); + assert!(response.data == 1); }); let device_t = thread::spawn({ let kill_device = kill_device.clone(); move || { let mut control_table = ControlTable::new(10); while !kill_device.load(Relaxed) { - let res = device.read(Duration::from_secs(1)); - let packet = match res { - Ok(p) => p, - Err(ReadError::Timeout) => continue, - Err(e) => { - return Err(e.into()); - }, + let packet = device.read(Duration::from_millis(50)); + let packet = match packet { + Err(ReadError::Io(e)) if T::is_timeout_error(&e) => continue, + x => x, }; + let_assert!(Ok(packet) = packet); let id = packet.id; if id != DEVICE_ID { continue; @@ -87,28 +85,26 @@ fn test_packet_response() { Instructions::Ping => {}, Instructions::Read { address, length } => { if let Some(data) = control_table.read(address, length) { - device.write_status(DEVICE_ID, 0, length as usize, |buffer| { + assert!(let Ok(()) = device.write_status(DEVICE_ID, 0, length as usize, |buffer| { buffer.copy_from_slice(data); - })?; + })); } else { - device.write_status_error(DEVICE_ID, 0x07)?; + assert!(let Ok(()) = device.write_status_error(DEVICE_ID, 0x07)); } }, Instructions::Write { address, parameters } => { if control_table.write(address, parameters) { - device.write_status_ok(DEVICE_ID)?; + let_assert!(Ok(()) = device.write_status_ok(DEVICE_ID)); } else { - device.write_status_error(DEVICE_ID, 0x07)?; + let_assert!(Ok(()) = device.write_status_error(DEVICE_ID, 0x07)); } }, i => todo!("impl {:?}", i), } } - - Ok::<(), TransferError<::Error>>(()) } }); - bus_t.join().unwrap().unwrap(); + bus_t.join().unwrap(); kill_device.store(true, Relaxed); - device_t.join().unwrap().unwrap(); + device_t.join().unwrap(); } diff --git a/tests/mock_transport.rs b/tests/mock_serial_port.rs similarity index 59% rename from tests/mock_transport.rs rename to tests/mock_serial_port.rs index 8fd2344..7d1d170 100644 --- a/tests/mock_transport.rs +++ b/tests/mock_serial_port.rs @@ -1,13 +1,13 @@ -use dynamixel2::{InitializeError, ReadError, Transport}; +use dynamixel2::SerialPort; use std::collections::VecDeque; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; + #[derive(Default, Clone)] pub struct MockSerialPort { pub read_buffer: Arc>>, pub write_buffer: Arc>>, pub baud_rate: u32, - pub deadline: Option, } impl MockSerialPort { @@ -16,7 +16,6 @@ impl MockSerialPort { read_buffer: Arc::new(Mutex::new(VecDeque::new())), write_buffer: Arc::new(Mutex::new(VecDeque::new())), baud_rate, - deadline: None, } } @@ -25,38 +24,33 @@ impl MockSerialPort { read_buffer: self.write_buffer.clone(), write_buffer: self.read_buffer.clone(), baud_rate: self.baud_rate, - deadline: self.deadline, } } } -impl Transport for MockSerialPort { - type Error = (); +impl SerialPort for MockSerialPort { + type Error = std::io::Error; + + type Instant = std::time::Instant; - fn baud_rate(&self) -> Result> { + fn baud_rate(&self) -> Result { Ok(self.baud_rate) } - fn set_baud_rate(&mut self, baud_rate: u32) -> Result<(), ()> { + fn set_baud_rate(&mut self, baud_rate: u32) -> Result<(), Self::Error> { self.baud_rate = baud_rate; Ok(()) } - fn discard_input_buffer(&mut self) -> Result<(), ()> { + fn discard_input_buffer(&mut self) -> Result<(), Self::Error> { self.read_buffer.lock().unwrap().clear(); Ok(()) } - fn set_timeout(&mut self, timeout: Duration) -> Result<(), Self::Error> { - self.deadline = Some(Instant::now() + timeout); - Ok(()) - } - - fn read(&mut self, buffer: &mut [u8]) -> Result> { - let deadline = self.deadline.ok_or(ReadError::Timeout)?; + fn read(&mut self, buffer: &mut [u8], deadline: &Self::Instant) -> Result { let mut data = loop { - if Instant::now() > deadline { - return Err(ReadError::Timeout); + if Instant::now() > *deadline { + return Err(std::io::ErrorKind::TimedOut.into()); } if let Ok(data) = self.read_buffer.try_lock() { break data; @@ -67,11 +61,19 @@ impl Transport for MockSerialPort { Ok(len) } - fn write_all(&mut self, buffer: &[u8]) -> Result<(), ()> { + fn write_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { let mut data = self.write_buffer.lock().unwrap(); for &byte in buffer { data.push_back(byte); } Ok(()) } + + fn make_deadline(&self, timeout: Duration) -> Self::Instant { + Instant::now() + timeout + } + + fn is_timeout_error(error: &Self::Error) -> bool { + error.kind() == std::io::ErrorKind::TimedOut + } }