use std::fmt::{Debug, Display, Formatter};
use std::sync::Arc;
use ahash::HashMap;
use async_broadcast::broadcast;
use smol::lock::MutexGuard;
use crate::devices::{abort, bug_abort};
use crate::messages::{Command, Metadata, Provenance, Receiver, Sender};
#[derive(Debug, Clone, Default)]
pub(crate) struct Dispatcher<const CH: usize> {
serial_number: String,
map: Arc<HashMap<[u8; 2], Command<CH>>>,
}
impl<const CH: usize> Dispatcher<CH> {
pub(crate) fn new(ids: &[Metadata<CH>], serial_number: &String) -> Self {
Self {
serial_number: serial_number.clone(),
map: Arc::new(HashMap::from_iter(ids.iter().map(Command::new))),
}
}
pub(crate) fn serial_number(&self) -> &String {
&self.serial_number
}
#[doc(hidden)]
async fn get(&self, id: &[u8]) -> &Command<CH> {
self.map
.get(id)
.unwrap_or_else(|| abort(format!("{self} does not contain command ID {id:02X?}")))
}
#[doc(hidden)]
fn insert(opt: &mut MutexGuard<Option<Sender>>) -> Receiver {
let (tx, rx) = broadcast(1);
opt.replace(tx);
rx
}
pub(crate) async fn receiver(&self, id: &[u8], channel: usize) -> Provenance {
let mut opt = self.get(id).await.sender(channel).lock().await;
match &*opt {
None => Provenance::New(Self::insert(&mut opt)),
Some(existing) => Provenance::Existing(existing.new_receiver()),
}
}
pub(crate) async fn new_receiver(&self, id: &[u8], channel: usize) -> Provenance {
log::debug!("NEW RECEIVER (requested) ID {id:02X?} CHANNEL {channel}");
loop {
let rx = self.receiver(id, channel).await;
if rx.is_new() {
log::debug!("NEW RECEIVER (success) ID {id:02X?} CHANNEL {channel}");
return rx;
} else {
log::debug!("NEW RECEIVER (waiting) ID {id:02X?} CHANNEL {channel}");
let _ = rx.receive().await;
}
}
}
#[doc(hidden)]
pub(crate) async fn take(&self, id: &[u8], channel: usize) -> Option<Sender> {
self.get(id).await.sender(channel).lock().await.take()
}
pub(crate) async fn length(&self, id: &[u8]) -> usize {
self.get(id).await.length
}
pub(crate) async fn dispatch(&self, data: Arc<[u8]>, channel: usize) {
let id: &[u8] = &data[..2];
if let Some(sender) = self.take(id, channel).await {
sender
.broadcast_direct(data)
.await
.unwrap_or_else(|e| bug_abort(format!("Broadcast failed : {}", e)));
}
}
}
impl<const CH: usize> Display for Dispatcher<CH> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "DISPATCHER {}", self.serial_number)
}
}