use heapless::index_map::FnvIndexMap;
use canadensis_core::time::{Clock, MicrosecondDuration32, Microseconds32};
use canadensis_core::transfer::{Header, ServiceHeader, Transfer};
use canadensis_core::transport::{TransferId, Transmitter, Transport};
use canadensis_core::{nb, OutOfMemoryError, ServiceId, TransferIdTracker};
use canadensis_encoding::{Request, Serialize};
use crate::serialize::do_serialize;
pub struct Requester<C: Clock, T: Transmitter<C>, R> {
priority: <T::Transport as Transport>::Priority,
timeout: MicrosecondDuration32,
transfer_ids: R,
}
impl<C: Clock, T: Transmitter<C>, R: TransferIdTracker<T::Transport>> Requester<C, T, R> {
pub fn new(
timeout: MicrosecondDuration32,
priority: <T::Transport as Transport>::Priority,
) -> Self {
Requester {
priority,
timeout,
transfer_ids: R::default(),
}
}
pub fn send<Q>(
&mut self,
clock: &mut C,
source: <T::Transport as Transport>::NodeId,
service: ServiceId,
payload: &Q,
destination: <T::Transport as Transport>::NodeId,
transmitter: &mut T,
driver: &mut T::Driver,
) -> nb::Result<<T::Transport as Transport>::TransferId, T::Error>
where
Q: Serialize + Request,
{
let deadline = clock.now() + self.timeout;
do_serialize(payload, |payload_bytes| {
self.send_payload(
payload_bytes,
source,
service,
destination,
deadline,
false,
transmitter,
clock,
driver,
)
})
}
pub fn send_loopback<Q>(
&mut self,
clock: &mut C,
source: <T::Transport as Transport>::NodeId,
service: ServiceId,
payload: &Q,
destination: <T::Transport as Transport>::NodeId,
transmitter: &mut T,
driver: &mut T::Driver,
) -> nb::Result<<T::Transport as Transport>::TransferId, T::Error>
where
Q: Serialize + Request,
{
let deadline = clock.now() + self.timeout;
do_serialize(payload, |payload_bytes| {
self.send_payload(
payload_bytes,
source,
service,
destination,
deadline,
true,
transmitter,
clock,
driver,
)
})
}
fn send_payload(
&mut self,
payload: &[u8],
source: <T::Transport as Transport>::NodeId,
service: ServiceId,
destination: <T::Transport as Transport>::NodeId,
deadline: Microseconds32,
loopback: bool,
transmitter: &mut T,
clock: &mut C,
driver: &mut T::Driver,
) -> nb::Result<<T::Transport as Transport>::TransferId, T::Error> {
let transfer_id = self
.transfer_ids
.next_transfer_id(destination.clone())
.map_err(|oom| nb::Error::Other(oom.into()))?;
let transfer = Transfer {
header: Header::Request(ServiceHeader {
timestamp: deadline,
transfer_id: transfer_id.clone(),
priority: self.priority.clone(),
service,
source,
destination,
}),
loopback,
payload,
};
transmitter.push(transfer, clock, driver)?;
Ok(transfer_id)
}
}
pub struct TransferIdFixedMap<T: Transport, const C: usize> {
ids: FnvIndexMap<T::NodeId, T::TransferId, C>,
}
impl<T: Transport, const C: usize> Default for TransferIdFixedMap<T, C> {
fn default() -> Self {
TransferIdFixedMap {
ids: FnvIndexMap::default(),
}
}
}
impl<T: Transport, const C: usize> TransferIdTracker<T> for TransferIdFixedMap<T, C> {
fn next_transfer_id(
&mut self,
destination: T::NodeId,
) -> Result<T::TransferId, OutOfMemoryError> {
match self.ids.get_mut(&destination) {
Some(entry) => {
let current = entry.clone();
*entry = entry.clone().increment();
Ok(current)
}
None => {
let current = T::TransferId::default();
let next = current.clone().increment();
match self.ids.insert(destination, next) {
Ok(_) => Ok(current),
Err(_) => Err(OutOfMemoryError),
}
}
}
}
}
mod fmt_impl {
use core::fmt::{Debug, Formatter, Result};
use canadensis_core::time::Clock;
use canadensis_core::transport::{Transmitter, Transport};
use crate::requester::Requester;
impl<C, T, R> Debug for Requester<C, T, R>
where
C: Clock,
T: Transmitter<C>,
<T::Transport as Transport>::TransferId: Debug,
<T::Transport as Transport>::Priority: Debug,
<T::Transport as Transport>::NodeId: Debug,
R: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("Requester")
.field("priority", &self.priority)
.field("timeout", &self.timeout)
.field("transfer_ids", &self.transfer_ids)
.finish()
}
}
#[cfg(feature = "defmt")]
impl<C, T, R> defmt::Format for Requester<C, T, R>
where
C: Clock,
T: Transmitter<C>,
<T::Transport as Transport>::TransferId: defmt::Format,
<T::Transport as Transport>::Priority: defmt::Format,
<T::Transport as Transport>::NodeId: defmt::Format,
R: defmt::Format,
{
fn format(&self, f: defmt::Formatter) {
defmt::write!(
f,
"Requester {{ priority: {}, timeout: {}, transfer_ids: {} }}",
self.priority,
self.timeout,
self.transfer_ids
)
}
}
}