use std::net::SocketAddr;
use crate::protocol::dds::message::Submessage;
use crate::protocol::dds::transport::error::TransportError;
use crate::protocol::dds::transport::UdpTransport;
use crate::protocol::dds::types::guid::Guid;
use crate::protocol::dds::types::sequence::SequenceNumber;
use super::error::StatelessError;
pub struct ReaderConfig {
pub guid: Guid,
pub accepted_writer_guids: Vec<Guid>,
}
impl ReaderConfig {
pub fn new(guid: Guid) -> Self {
Self {
guid,
accepted_writer_guids: Vec::new(),
}
}
pub fn accept_writer(mut self, writer_guid: Guid) -> Self {
self.accepted_writer_guids.push(writer_guid);
self
}
}
pub struct ReceivedSample {
pub writer_guid: Guid,
pub sequence_number: SequenceNumber,
pub data: Vec<u8>,
pub has_inline_qos: bool,
}
pub struct StatelessReader {
config: ReaderConfig,
transport: UdpTransport,
highest_received_sn: Option<SequenceNumber>,
}
impl StatelessReader {
pub fn new(config: ReaderConfig, transport: UdpTransport) -> Self {
Self {
config,
transport,
highest_received_sn: None,
}
}
pub fn recv(&mut self) -> Result<Option<ReceivedSample>, StatelessError> {
let mut buf = vec![0u8; 65536];
let result: Result<Option<ReceivedSample>, StatelessError> = {
match self.transport.recv_into(&mut buf) {
Ok((msg, _from_addr)) => {
let mut found: Option<ReceivedSample> = None;
for sub in msg.submessages.iter() {
if let Submessage::Data(data) = sub {
let writer_guid = Guid {
prefix: msg.header.guid_prefix,
entity_id: data.writer_id,
};
if !self.config.accepted_writer_guids.is_empty()
&& !self.config.accepted_writer_guids.contains(&writer_guid)
{
continue;
}
found = Some(ReceivedSample {
writer_guid,
sequence_number: data.writer_sn,
data: data.serialized_payload.to_vec(),
has_inline_qos: data.inline_qos_flag,
});
break;
}
}
Ok(found)
}
Err(e) => {
if let TransportError::Io(ref io_err) = e {
if matches!(
io_err.kind(),
std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut
) {
return Ok(None);
}
}
Err(StatelessError::Transport(e))
}
}
};
if let Ok(Some(ref sample)) = result {
let sn = sample.sequence_number;
self.highest_received_sn =
Some(
self.highest_received_sn
.map_or(sn, |h| if sn > h { sn } else { h }),
);
}
result
}
pub fn set_read_timeout(
&mut self,
dur: Option<std::time::Duration>,
) -> Result<(), StatelessError> {
self.transport
.set_read_timeout(dur)
.map_err(StatelessError::Transport)
}
pub fn own_guid(&self) -> &Guid {
&self.config.guid
}
pub fn highest_received_sn(&self) -> Option<SequenceNumber> {
self.highest_received_sn
}
pub fn local_addr(&self) -> Result<SocketAddr, StatelessError> {
self.transport
.local_addr()
.map_err(StatelessError::Transport)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::protocol::dds::stateless::writer::{StatelessWriter, WriterConfig};
use crate::protocol::dds::transport::TransportConfig;
use crate::protocol::dds::types::guid::{EntityId, GuidPrefix};
use crate::protocol::dds::types::locator::Locator;
use std::net::SocketAddr;
use std::time::Duration;
fn make_guid(prefix_byte: u8, key: u8) -> Guid {
Guid {
prefix: GuidPrefix([prefix_byte; 12]),
entity_id: EntityId {
entity_key: [0x00, 0x00, key],
entity_kind: 0x02,
},
}
}
fn make_writer(guid: Guid) -> StatelessWriter {
let cfg = TransportConfig::unicast(SocketAddr::from(([127, 0, 0, 1], 0)));
let transport = UdpTransport::new(cfg).unwrap();
StatelessWriter::new(WriterConfig::new(guid).with_history_capacity(8), transport)
}
fn make_reader(guid: Guid) -> StatelessReader {
let cfg = TransportConfig {
read_timeout: Some(Duration::from_millis(300)),
..TransportConfig::unicast(SocketAddr::from(([127, 0, 0, 1], 0)))
};
let transport = UdpTransport::new(cfg).unwrap();
StatelessReader::new(ReaderConfig::new(guid), transport)
}
fn locator_from_reader(reader: &StatelessReader) -> Locator {
let addr = reader.local_addr().unwrap();
Locator::udp_v4(addr.port() as u32, [127, 0, 0, 1])
}
#[test]
fn reader_loopback_three_samples() {
let writer_guid = make_guid(0x11, 0x01);
let reader_guid = make_guid(0x22, 0x07);
let mut writer = make_writer(writer_guid);
let mut reader = make_reader(reader_guid);
writer.add_reader_locator(locator_from_reader(&reader));
let payloads: [&[u8]; 3] = [b"alpha", b"beta", b"gamma"];
for payload in &payloads {
writer.write(payload).unwrap();
}
let mut received = Vec::new();
for _ in 0..3 {
if let Some(sample) = reader.recv().unwrap() {
received.push(sample);
}
}
assert_eq!(received.len(), 3);
assert_eq!(received[0].data, b"alpha");
assert_eq!(received[1].data, b"beta");
assert_eq!(received[2].data, b"gamma");
assert_eq!(received[0].sequence_number, SequenceNumber::new(1));
assert_eq!(received[1].sequence_number, SequenceNumber::new(2));
assert_eq!(received[2].sequence_number, SequenceNumber::new(3));
}
#[test]
fn reader_filters_writer_guid() {
let writer_a_guid = make_guid(0xAA, 0x01);
let writer_b_guid = make_guid(0xBB, 0x01);
let reader_guid = make_guid(0xCC, 0x07);
let mut writer_a = make_writer(writer_a_guid);
let mut writer_b = make_writer(writer_b_guid);
let reader_cfg = {
let cfg = TransportConfig {
read_timeout: Some(Duration::from_millis(300)),
..TransportConfig::unicast(SocketAddr::from(([127, 0, 0, 1], 0)))
};
let transport = UdpTransport::new(cfg).unwrap();
let config = ReaderConfig::new(reader_guid).accept_writer(writer_a_guid);
StatelessReader::new(config, transport)
};
let mut reader = reader_cfg;
let reader_locator = locator_from_reader(&reader);
writer_a.add_reader_locator(reader_locator);
writer_b.add_reader_locator(reader_locator);
writer_b.write(b"from_b").unwrap();
writer_a.write(b"from_a").unwrap();
let first = reader.recv().unwrap();
assert!(
first.is_none(),
"expected B's message to be filtered (got Some)"
);
let second = reader.recv().unwrap();
assert!(second.is_some(), "expected A's message to be accepted");
let sample = second.unwrap();
assert_eq!(sample.data, b"from_a");
assert_eq!(sample.writer_guid, writer_a_guid);
}
#[test]
fn reader_highest_sn_tracking() {
let writer_guid = make_guid(0x33, 0x01);
let reader_guid = make_guid(0x44, 0x07);
let mut writer = make_writer(writer_guid);
let mut reader = make_reader(reader_guid);
writer.add_reader_locator(locator_from_reader(&reader));
writer.write(b"one").unwrap();
writer.write(b"two").unwrap();
writer.write(b"three").unwrap();
assert!(reader.highest_received_sn().is_none());
reader.recv().unwrap();
reader.recv().unwrap();
reader.recv().unwrap();
assert_eq!(reader.highest_received_sn(), Some(SequenceNumber::new(3)));
}
}