use std::time::{Duration, Instant};
use super::{instruction_id, packet_id};
use crate::bus::StatusPacket;
use crate::{Bus, ReadError, Response, TransferError, WriteError};
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Ping {
pub model: u16,
pub firmware: u8,
}
impl<'a> TryFrom<StatusPacket<'a>> for Response<Ping> {
type Error = crate::InvalidParameterCount;
fn try_from(status_packet: StatusPacket<'a>) -> Result<Self, Self::Error> {
let parameters = status_packet.parameters();
crate::InvalidParameterCount::check(parameters.len(), 3)?;
Ok(Self {
motor_id: status_packet.packet_id(),
alert: status_packet.alert(),
data: Ping {
model: crate::endian::read_u16_le(¶meters[0..]),
firmware: crate::endian::read_u8_le(¶meters[2..]),
},
})
}
}
impl<ReadBuffer, WriteBuffer> Bus<ReadBuffer, WriteBuffer>
where
ReadBuffer: AsRef<[u8]> + AsMut<[u8]>,
WriteBuffer: AsRef<[u8]> + AsMut<[u8]>,
{
pub fn ping(&mut self, motor_id: u8) -> Result<Response<Ping>, TransferError> {
let response = self.transfer_single(motor_id, instruction_id::PING, 0, 3, |_| ())?;
Ok(response.try_into()?)
}
pub fn scan(&mut self) -> Result<Vec<Result<Response<Ping>, ReadError>>, WriteError> {
let mut result = Vec::with_capacity(253);
match self.scan_cb(|x| result.push(Ok(x))) {
Ok(()) => (),
Err(TransferError::WriteError(e)) => return Err(e),
Err(TransferError::ReadError(e)) => {
result.push(Err(e));
}
}
Ok(result)
}
pub fn scan_cb<F>(&mut self, mut on_response: F) -> Result<(), TransferError>
where
F: FnMut(Response<Ping>),
{
self.write_instruction(packet_id::BROADCAST, instruction_id::PING, 0, |_| ())?;
let response_time = crate::bus::message_transfer_time(14, self.baud_rate());
let timeout = response_time * 253 + Duration::from_millis(34);
let deadline = Instant::now() + timeout;
loop {
let response = self.read_status_response_deadline(deadline);
match response {
Ok(response) => {
let response = response.try_into()?;
on_response(response);
},
Err(e) => {
if let ReadError::Io(e) = &e {
if e.kind() == std::io::ErrorKind::TimedOut {
trace!("Ping response timed out.");
return Ok(());
}
}
return Err(e.into())
},
}
}
}
}