extern crate canadensis;
extern crate canadensis_can;
extern crate canadensis_data_types;
extern crate canadensis_encoding;
extern crate log;
extern crate simplelog;
use canadensis::node::CoreNode;
use canadensis::requester::TransferIdFixedMap;
use canadensis::{Node, ResponseToken, TransferHandler};
use canadensis_can::driver::{ReceiveDriver, TransmitDriver};
use canadensis_can::{CanNodeId, CanReceiver, CanTransmitter, CanTransport, Frame, Mtu};
use canadensis_core::subscription::Subscription;
use canadensis_core::time::{milliseconds, Clock, Microseconds32};
use canadensis_core::transfer::{MessageTransfer, ServiceTransfer, Transfer};
use canadensis_core::{OutOfMemoryError, Priority};
use canadensis_data_types::uavcan::time::synchronization_1_0::{self, Synchronization};
use canadensis_encoding::Deserialize;
use log::LevelFilter;
use simplelog::{ColorChoice, Config, TerminalMode};
use std::cell::Cell;
use std::collections::vec_deque::VecDeque;
use std::convert::{Infallible, TryFrom};
#[test]
fn can_loopback_time_sync() {
simplelog::TermLogger::init(
LevelFilter::Trace,
Config::default(),
TerminalMode::default(),
ColorChoice::Auto,
)
.unwrap();
let clock_handle = StubClockHandle::new();
let node_id = CanNodeId::try_from(3_u8).unwrap();
let mut node: CoreNode<
StubClock<'_>,
CanTransmitter<StubClock<'_>, LoopbackOnlyDriver>,
CanReceiver<StubClock<'_>, LoopbackOnlyDriver>,
TransferIdFixedMap<CanTransport, 4>,
LoopbackOnlyDriver,
4,
4,
> = CoreNode::new(
clock_handle.clock(),
node_id,
CanTransmitter::new(Mtu::Can8),
CanReceiver::new(node_id),
LoopbackOnlyDriver::default(),
);
node.subscribe_message(synchronization_1_0::SUBJECT, 8, milliseconds(100))
.unwrap();
node.start_publishing(
synchronization_1_0::SUBJECT,
milliseconds(100),
Priority::Nominal,
)
.unwrap();
clock_handle.set_time(10);
node.publish(
synchronization_1_0::SUBJECT,
&Synchronization {
previous_transmission_timestamp_microsecond: 3,
},
)
.unwrap();
let mut collector = LoopbackCollector::default();
clock_handle.set_time(20);
node.receive(&mut collector)
.expect("Unexpected error in receive");
assert_eq!(0, collector.transfers.len());
clock_handle.set_time(30);
let loopback_payload = Synchronization {
previous_transmission_timestamp_microsecond: 129,
};
node.publish_loopback(synchronization_1_0::SUBJECT, &loopback_payload)
.unwrap();
clock_handle.set_time(40);
node.receive(&mut collector)
.expect("Unexpected error in receive");
assert_eq!(1, collector.transfers.len());
let received_loopback = &collector.transfers[0];
assert_eq!(received_loopback.loopback, true);
assert_eq!(received_loopback.header.priority(), &Priority::Nominal);
assert_eq!(received_loopback.header.source(), Some(&node_id));
assert_eq!(
received_loopback.header.timestamp(),
Microseconds32::from_ticks(30)
);
let loopback_deserialized_payload =
Synchronization::deserialize_from_bytes(&received_loopback.payload).unwrap();
assert_eq!(
loopback_deserialized_payload.previous_transmission_timestamp_microsecond,
loopback_payload.previous_transmission_timestamp_microsecond
);
}
#[derive(Default)]
struct LoopbackOnlyDriver {
loopback_frames: VecDeque<Frame>,
}
impl TransmitDriver<StubClock<'_>> for LoopbackOnlyDriver {
type Error = Infallible;
fn try_reserve(&mut self, _frames: usize) -> Result<(), OutOfMemoryError> {
Ok(())
}
fn transmit(
&mut self,
frame: Frame,
clock: &mut StubClock<'_>,
) -> canadensis::nb::Result<Option<Frame>, Self::Error> {
log::trace!("LoopbackOnlyDriver::transmit");
let now = clock.now();
if frame.timestamp() < now {
log::debug!("Frame timed out");
return Ok(None);
}
if !frame.loopback() {
log::debug!("Discarding non-loopback frame");
return Ok(None);
}
let mut loopback_frame = frame;
loopback_frame.set_timestamp(now);
self.loopback_frames.push_back(loopback_frame);
Ok(None)
}
fn flush(&mut self, _clock: &mut StubClock<'_>) -> canadensis::nb::Result<(), Self::Error> {
Ok(())
}
}
impl ReceiveDriver<StubClock<'_>> for LoopbackOnlyDriver {
type Error = Infallible;
fn receive(
&mut self,
_clock: &mut StubClock<'_>,
) -> canadensis::nb::Result<Frame, Self::Error> {
self.loopback_frames
.pop_front()
.map(|frame| {
log::trace!("Receiving loopback frame");
frame
})
.ok_or(canadensis::nb::Error::WouldBlock)
}
fn apply_filters<S>(&mut self, _local_node: Option<CanNodeId>, _subscriptions: S)
where
S: IntoIterator<Item = Subscription>,
{
}
fn apply_accept_all(&mut self) {
}
}
#[derive(Default)]
struct LoopbackCollector {
transfers: Vec<Transfer<Vec<u8>, CanTransport>>,
}
impl TransferHandler<CanTransport> for LoopbackCollector {
fn handle_message<N: Node<Transport = CanTransport>>(
&mut self,
_node: &mut N,
_transfer: &MessageTransfer<Vec<u8>, CanTransport>,
) -> bool {
panic!("handle_message() called (not loopback)");
}
fn handle_request<N: Node<Transport = CanTransport>>(
&mut self,
_node: &mut N,
_token: ResponseToken<CanTransport>,
_transfer: &ServiceTransfer<Vec<u8>, CanTransport>,
) -> bool {
panic!("handle_request() called (not loopback)");
}
fn handle_response<N: Node<Transport = CanTransport>>(
&mut self,
_node: &mut N,
_transfer: &ServiceTransfer<Vec<u8>, CanTransport>,
) -> bool {
panic!("handle_response() called (not loopback)");
}
fn handle_loopback<N: Node<Transport = CanTransport>>(
&mut self,
_node: &mut N,
transfer: &Transfer<Vec<u8>, CanTransport>,
) -> bool {
self.transfers.push(transfer.clone());
true
}
}
struct StubClock<'t> {
time: &'t Cell<u32>,
}
impl Clock for StubClock<'_> {
fn now(&mut self) -> Microseconds32 {
Microseconds32::from_ticks(self.time.get())
}
}
struct StubClockHandle {
time: Cell<u32>,
}
impl StubClockHandle {
pub fn new() -> Self {
StubClockHandle { time: Cell::new(0) }
}
pub fn set_time(&self, time: u32) {
self.time.set(time);
}
pub fn clock(&self) -> StubClock<'_> {
StubClock { time: &self.time }
}
}