use crate::error::{Error, Result};
use crate::rtps::submessages::Data;
use crate::rtps::{EntityId, GuidPrefix, Locator, RtpsHeader, SequenceNumber, GUID};
use crate::transport::Transport;
use crate::MAX_PACKET_SIZE;
#[derive(Debug, PartialEq)]
pub struct MicroWriter {
guid: GUID,
topic_name: [u8; 64],
topic_len: usize,
dest_locator: Locator,
sequence_number: SequenceNumber,
}
impl MicroWriter {
pub fn new(
guid_prefix: GuidPrefix,
entity_id: EntityId,
topic_name: &str,
dest_locator: Locator,
) -> Result<Self> {
if topic_name.len() > 63 {
return Err(Error::InvalidParameter);
}
let mut topic_name_buf = [0u8; 64];
topic_name_buf[0..topic_name.len()].copy_from_slice(topic_name.as_bytes());
Ok(Self {
guid: GUID::new(guid_prefix, entity_id),
topic_name: topic_name_buf,
topic_len: topic_name.len(),
dest_locator,
sequence_number: SequenceNumber::MIN,
})
}
pub const fn guid(&self) -> GUID {
self.guid
}
pub fn topic_name(&self) -> &str {
core::str::from_utf8(&self.topic_name[0..self.topic_len]).unwrap_or("")
}
pub const fn sequence_number(&self) -> SequenceNumber {
self.sequence_number
}
pub fn write<T: Transport>(&mut self, payload: &[u8], transport: &mut T) -> Result<()> {
let mut packet = [0u8; MAX_PACKET_SIZE];
let header = RtpsHeader::new(
crate::rtps::ProtocolVersion::RTPS_2_5,
crate::rtps::VendorId::HDDS,
self.guid.prefix,
);
let header_len = header.encode(&mut packet)?;
let data = Data::new(
EntityId::UNKNOWN, self.guid.entity_id,
self.sequence_number,
);
let data_len = data.encode_header(&mut packet[header_len..])?;
let payload_offset = header_len + data_len;
if payload_offset + payload.len() > MAX_PACKET_SIZE {
return Err(Error::BufferTooSmall);
}
packet[payload_offset..payload_offset + payload.len()].copy_from_slice(payload);
let total_len = payload_offset + payload.len();
let octets_to_next = (20 + payload.len()) as u16;
packet[header_len + 2] = (octets_to_next & 0xff) as u8;
packet[header_len + 3] = ((octets_to_next >> 8) & 0xff) as u8;
transport.send(&packet[0..total_len], &self.dest_locator)?;
self.sequence_number.increment();
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cdr::CdrEncoder;
use crate::transport::NullTransport;
#[test]
fn test_writer_creation() {
let writer = MicroWriter::new(
GuidPrefix::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
EntityId::new([0, 0, 0, 0xc2]),
"TestTopic",
Locator::udpv4([239, 255, 0, 1], 7400),
)
.unwrap();
assert_eq!(writer.topic_name(), "TestTopic");
assert_eq!(writer.sequence_number(), SequenceNumber::MIN);
}
#[test]
fn test_writer_write() {
let mut writer = MicroWriter::new(
GuidPrefix::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
EntityId::new([0, 0, 0, 0xc2]),
"TestTopic",
Locator::udpv4([239, 255, 0, 1], 7400),
)
.unwrap();
let mut transport = NullTransport::default();
let mut buf = [0u8; 128];
let mut encoder = CdrEncoder::new(&mut buf);
encoder.encode_f32(23.5).unwrap();
let payload = encoder.finish();
writer.write(payload, &mut transport).unwrap();
assert_eq!(writer.sequence_number(), SequenceNumber::new(2));
}
#[test]
fn test_writer_topic_name_too_long() {
let long_name = "a".repeat(100);
let result = MicroWriter::new(
GuidPrefix::default(),
EntityId::default(),
&long_name,
Locator::default(),
);
assert_eq!(result, Err(Error::InvalidParameter));
}
}