use super::{Message, Transport};
use crate::util::pubsub::{PubSubChannel, Subscriber};
use crate::util::unwrap;
use alloc::vec::Vec;
use core::marker::PhantomData;
#[cfg(feature = "defmt")]
use defmt::error;
use embassy_executor::Spawner;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use generic_array::GenericArray;
static INNER_CHANNEL: PubSubChannel<CriticalSectionRawMutex, Vec<u8>> = PubSubChannel::new();
pub type DynChannel = Channel<dyn Transport>;
pub struct Channel<T: ?Sized + 'static> {
inner: &'static T,
}
impl<T: Transport> Channel<T> {
pub fn new(inner: &'static T, spawner: Spawner) -> Self {
#[embassy_executor::task]
async fn task(inner: &'static dyn Transport) {
loop {
let message_bytes = inner.receive().await;
INNER_CHANNEL.publish(message_bytes);
}
}
unwrap!(spawner.spawn(task(inner)));
Self { inner }
}
pub fn as_dyn(&self) -> DynChannel {
Channel { inner: self.inner }
}
}
impl<T: Transport + ?Sized> Channel<T> {
pub fn send<M: Message>(&self, message: M) {
let message_tag = M::TAG;
let message_bytes = message.to_bytes();
let mut bytes = Vec::with_capacity(message_tag.len() + message_bytes.len());
bytes.extend(message_tag);
bytes.extend(message_bytes);
self.inner.send(&bytes);
INNER_CHANNEL.publish(bytes);
}
pub fn receiver<M: Message>(&self) -> Receiver<M> {
Receiver {
subscriber: INNER_CHANNEL.subscriber(),
_phantom: PhantomData,
}
}
}
impl<T: ?Sized> Clone for Channel<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> Copy for Channel<T> {}
pub struct Receiver<M> {
subscriber: Subscriber<'static, CriticalSectionRawMutex, Vec<u8>>,
_phantom: PhantomData<M>,
}
impl<M: Message> Receiver<M> {
pub async fn next(&mut self) -> M {
loop {
let message_bytes = self.subscriber.next_message().await;
if message_bytes[..4] == M::TAG {
#[allow(clippy::single_match)]
match GenericArray::try_from_slice(&message_bytes[4..]) {
Ok(array) => {
if let Some(message) = M::from_bytes(array) {
return message;
}
}
Err(_) => {
#[cfg(feature = "defmt")]
error!(
"invalid message size (expected {} bytes, found {})",
<M::Size as typenum::Unsigned>::USIZE,
message_bytes.len() - 4
)
}
}
}
}
}
}