use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock};
use zerodds_iceoryx_cyclone::{
CYCLONE_IOX_SERVICE, CycloneIoxReader, CycloneIoxWriter, DdsPsmxMetadata, iox_event_name,
};
use crate::dds_type::DdsType;
use crate::error::{DdsError, Result};
use crate::publisher::DataWriter;
use crate::subscriber::DataReader;
struct WriterEntry {
writer: Mutex<CycloneIoxWriter>,
guid: [u8; 16],
}
type WriterCell = Arc<WriterEntry>;
type ReaderCell = Arc<Mutex<CycloneIoxReader>>;
fn writers() -> &'static Mutex<HashMap<String, WriterCell>> {
static M: OnceLock<Mutex<HashMap<String, WriterCell>>> = OnceLock::new();
M.get_or_init(|| Mutex::new(HashMap::new()))
}
fn readers() -> &'static Mutex<HashMap<String, ReaderCell>> {
static M: OnceLock<Mutex<HashMap<String, ReaderCell>>> = OnceLock::new();
M.get_or_init(|| Mutex::new(HashMap::new()))
}
fn guid_for(topic: &str) -> [u8; 16] {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for b in topic.bytes() {
h ^= u64::from(b);
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
let lo = h.to_le_bytes();
let hi = h.rotate_left(32).to_le_bytes();
let mut g = [0u8; 16];
g[..8].copy_from_slice(&lo);
g[8..].copy_from_slice(&hi);
g
}
impl<T: DdsType> DataWriter<T> {
pub fn enable_cyclone_iox(&self) -> Result<()> {
let topic = self.topic().name().to_string();
let event = iox_event_name("", &topic);
let w = CycloneIoxWriter::new("zerodds-dcps", CYCLONE_IOX_SERVICE, T::TYPE_NAME, &event);
let guid = self.rtps_guid().unwrap_or_else(|| guid_for(&topic));
writers()
.lock()
.map_err(|_| DdsError::PreconditionNotMet {
reason: "cyclone-iox writer table poisoned",
})?
.insert(
topic,
Arc::new(WriterEntry {
writer: Mutex::new(w),
guid,
}),
);
Ok(())
}
}
impl<T: DdsType> DataReader<T> {
pub fn enable_cyclone_iox(&self) -> Result<()> {
let topic = self.topic().name().to_string();
let event = iox_event_name("", &topic);
let r = CycloneIoxReader::new("zerodds-dcps", CYCLONE_IOX_SERVICE, T::TYPE_NAME, &event);
readers()
.lock()
.map_err(|_| DdsError::PreconditionNotMet {
reason: "cyclone-iox reader table poisoned",
})?
.insert(topic, Arc::new(Mutex::new(r)));
Ok(())
}
}
pub(crate) fn publish<T: DdsType>(topic: &str, sample: &T) {
let entry = writers().lock().ok().and_then(|m| m.get(topic).cloned());
let Some(entry) = entry else { return };
let mut buf = Vec::new();
if sample.encode_xcdr1(&mut buf).is_err() {
return;
}
let meta = DdsPsmxMetadata::serialized(buf.len() as u32, entry.guid);
if let Ok(w) = entry.writer.lock() {
let _ = w.write_raw(&meta, &buf);
}
}
pub(crate) fn take<T: DdsType>(topic: &str) -> Vec<T> {
let mut out = Vec::new();
let r = readers().lock().ok().and_then(|m| m.get(topic).cloned());
let Some(r) = r else { return out };
let Ok(r) = r.lock() else { return out };
while let Some((_meta, payload)) = r.take() {
if let Ok(s) = T::decode_xcdr1(&payload) {
out.push(s);
}
}
out
}