1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
use super::{instruction_id, packet_id, BulkWriteData};
use crate::endian::{write_u16_le, write_u8_le};
use crate::{Bus, WriteError};
impl<ReadBuffer, WriteBuffer> Bus<ReadBuffer, WriteBuffer>
where
ReadBuffer: AsRef<[u8]> + AsMut<[u8]>,
WriteBuffer: AsRef<[u8]> + AsMut<[u8]>,
{
/// Synchronously write arbitrary data ranges to multiple motors.
///
/// Each motor will perform the write as soon as it receives the command.
/// This gives much shorter delays than executing a regular [`Self::write`] for each motor individually.
/// Unlike the sync write instruction, a bulk write allows you to write a different amount of data to a different address for each motor.
///
/// The data for multi-byte registers should serialized as little-endian.
///
/// # Panics
/// The protocol forbids specifying the same motor ID multiple times.
/// This function panics if the same motor ID is used for more than one write.
///
/// This function also panics if the data length for a motor exceeds the capacity of a `u16`.
///
/// # Example
/// ```no_run
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use dynamixel2::Bus;
/// use dynamixel2::instructions::BulkWriteData;
/// use std::time::Duration;
///
/// let mut bus = Bus::open("/dev/ttyUSB0", 57600)?;
/// bus.bulk_write(&[
/// // Write a u32 value of 2000 to register 116 of motor 1.
/// BulkWriteData {
/// motor_id: 1,
/// address: 116,
/// data: 2000u32.to_le_bytes().as_slice(),
/// },
/// // Write a u16 value of 300 to register 102 of motor 2.
/// BulkWriteData {
/// motor_id: 2,
/// address: 102,
/// data: 300u16.to_le_bytes().as_slice(),
/// },
/// ])?;
/// # Ok(())
/// # }
/// ```
pub fn bulk_write<'a, I, T>(&mut self, writes: &'a I) -> Result<(), WriteError>
where
&'a I: IntoIterator,
<&'a I as IntoIterator>::IntoIter: Clone,
<&'a I as IntoIterator>::Item: std::borrow::Borrow<BulkWriteData<T>>,
T: AsRef<[u8]>,
{
use std::borrow::Borrow;
let writes = writes.into_iter();
let mut parameter_count = 0;
for write in writes.clone() {
let write = write.borrow();
let data = write.data.as_ref();
if data.len() > u16::MAX.into() {
panic!(
"bulk_write: data length ({}) for motor {} exceeds maximum size of {}",
data.len(),
write.motor_id,
u16::MAX
);
}
parameter_count += 5 + data.len();
}
self.write_instruction(packet_id::BROADCAST, instruction_id::BULK_WRITE, parameter_count, |buffer| {
let mut offset = 0;
for write in writes {
let write = write.borrow();
let data = write.data.as_ref();
let buffer = &mut buffer[offset..];
offset += 5 + data.len();
write_u8_le(&mut buffer[0..], write.motor_id);
write_u16_le(&mut buffer[1..], write.address);
write_u16_le(&mut buffer[3..], data.len() as u16);
buffer[5..][..data.len()].copy_from_slice(data);
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
/// Ensure that `bulk_write` accepts a slice of `BulkWriteData`.
///
/// This is a compile test. It only tests that the test code compiles.
#[allow(dead_code)]
fn bulk_write_accepts_slice(bus: &mut Bus<Vec<u8>, Vec<u8>>) -> Result<(), Box<dyn std::error::Error>> {
bus.bulk_write(&[
BulkWriteData {
motor_id: 1,
address: 116,
data: 2000u32.to_le_bytes().as_slice(),
},
BulkWriteData {
motor_id: 2,
address: 102,
data: 300u16.to_le_bytes().as_slice(),
},
])?;
Ok(())
}
/// Ensure that `bulk_write` accepts a reference to a Vec of `BulkWriteData`.
///
/// This is a compile test. It only tests that the test code compiles.
#[allow(dead_code)]
fn bulk_write_accepts_vec_ref(bus: &mut Bus<Vec<u8>, Vec<u8>>) -> Result<(), Box<dyn std::error::Error>> {
bus.bulk_write(&vec![
BulkWriteData {
motor_id: 1,
address: 116,
data: 2000u32.to_le_bytes().as_slice(),
},
BulkWriteData {
motor_id: 2,
address: 102,
data: 300u16.to_le_bytes().as_slice(),
},
])?;
Ok(())
}
/// Ensure that `bulk_write` accepts a reference to a Vec and doesn't clone the data in the vector.
///
/// This is a compile test. It only tests that the test code compiles.
#[allow(dead_code)]
fn bulk_write_accepts_vec_ref_no_clone(bus: &mut Bus<Vec<u8>, Vec<u8>>) -> Result<(), Box<dyn std::error::Error>> {
/// Non-clonable wrapper around `&[u8]` to ensure `bulk_write` doesn't clone data from vec references.
struct Data<'a> {
data: &'a [u8],
}
impl AsRef<[u8]> for Data<'_> {
fn as_ref(&self) -> &[u8] {
self.data
}
}
impl<'a> Data<'a> {
fn new<const N: usize>(data: &'a [u8; N]) -> Self {
Self {
data: data.as_slice(),
}
}
}
bus.bulk_write(&vec![
BulkWriteData {
motor_id: 1,
address: 116,
data: Data::new(&2000u32.to_le_bytes()),
},
BulkWriteData {
motor_id: 2,
address: 102,
data: Data::new(&300u16.to_le_bytes()),
},
])?;
Ok(())
}
}