use crate::try_lock;
pub mod crc32;
pub mod protocol;
pub mod publisher;
pub mod subscriber;
pub mod topics;
pub mod ttl_ringbuffer;
pub mod udp;
use crate::pubsub::topics::*;
use core::fmt;
use core::sync::atomic::{AtomicBool, Ordering};
use protocol::ProtocolFrame;
#[derive(Debug, PartialEq, Eq)]
pub enum PubSubError {
InitFailed,
NetworkError,
InvalidParameter,
ResourceExhausted,
InvalidFrameFormat,
CrcCheckFailed,
TopicNotFound,
SubscriptionNotFound,
UnsupportedOperation,
}
impl fmt::Display for PubSubError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PubSubError::InitFailed => write!(f, "PubSub initialization failed"),
PubSubError::NetworkError => write!(f, "Network error"),
PubSubError::InvalidParameter => write!(f, "Invalid parameter"),
PubSubError::ResourceExhausted => write!(f, "Resource exhausted"),
PubSubError::InvalidFrameFormat => write!(f, "Invalid frame format"),
PubSubError::CrcCheckFailed => write!(f, "CRC check failed"),
PubSubError::TopicNotFound => write!(f, "Topic not found"),
PubSubError::SubscriptionNotFound => write!(f, "Subscription not found"),
PubSubError::UnsupportedOperation => write!(f, "Unsupported operation"),
}
}
}
pub type Result<T> = core::result::Result<T, PubSubError>;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum UdpMode {
Unicast,
Broadcast,
Multicast,
}
type PubSubCallback = fn(topic_id: u16, data: &[u8]) -> bool;
type SubscriptionId = usize;
pub const WILDCARD_TOPIC_ID: u16 = 0xFFFF;
#[derive(Debug, Clone)]
pub struct PubSubConfig {
pub udp_mode: UdpMode,
pub multicast_addr: Option<std::net::IpAddr>,
pub port: u16,
pub max_topics: usize,
pub max_subscribers_per_topic: usize,
pub buffer_size: usize,
pub enable_nack: bool,
pub retransmit_timeout: core::time::Duration,
pub max_retransmits: usize,
pub heartbeat_interval: core::time::Duration,
pub frame_pool_size: usize,
}
impl Default for PubSubConfig {
fn default() -> Self {
Self {
udp_mode: UdpMode::Unicast,
multicast_addr: None,
port: 5555,
max_topics: 32,
max_subscribers_per_topic: 16,
buffer_size: 4096,
enable_nack: true,
retransmit_timeout: core::time::Duration::from_millis(100),
max_retransmits: 3,
heartbeat_interval: core::time::Duration::from_secs(10),
frame_pool_size: 128,
}
}
}
static mut PUB_SUB_INSTANCE: Option<PubSub> = None;
static PUB_SUB_INIT_LOCK: AtomicBool = AtomicBool::new(false);
pub struct PubSub {
config: PubSubConfig,
subscribers: subscriber::SubscriberManager,
publisher: publisher::Publisher,
udp_socket: udp::UdpSocket,
is_running: bool,
}
impl PubSub {
pub fn new(config: PubSubConfig) -> Result<Self> {
let udp_socket = udp::UdpSocket::new(
config.udp_mode,
config.multicast_addr,
config.port,
config.buffer_size,
)?;
let subscribers = subscriber::SubscriberManager::new(
config.max_topics,
config.max_subscribers_per_topic,
)?;
let publisher = publisher::Publisher::new(
config.enable_nack,
config.retransmit_timeout,
config.max_retransmits,
)?;
Ok(Self {
config,
subscribers,
publisher,
udp_socket,
is_running: false,
})
}
pub fn get_actual_port(&self) -> Result<u16> {
self.udp_socket.get_port()
}
pub fn init(&mut self) -> Result<()> {
self.udp_socket.init()?;
self.is_running = true;
Ok(())
}
#[cfg(feature = "posix")]
pub fn start_receiver(&mut self) -> Result<()> {
if !self.is_running {
return Err(PubSubError::InitFailed);
}
let pubsub = std::sync::Arc::new(std::sync::Mutex::new(self.clone()));
std::thread::spawn(move || {
let mut pubsub = try_lock!(pubsub);
pubsub.receive_loop();
});
Ok(())
}
pub fn clone(&self) -> Self {
Self {
config: self.config.clone(),
subscribers: self.subscribers.clone(),
publisher: self.publisher.clone(),
udp_socket: self.udp_socket.clone(),
is_running: self.is_running,
}
}
pub fn receive_loop(&mut self) {
let mut buf = alloc::vec::Vec::with_capacity(self.config.buffer_size);
buf.resize(self.config.buffer_size, 0);
let mut retransmit_frames = alloc::vec::Vec::new();
loop {
retransmit_frames.clear();
if let Ok(frames) = self.publisher.check_timeouts() {
retransmit_frames.extend(frames);
}
for frame in &retransmit_frames {
let bytes = frame.to_bytes();
if self.udp_socket.send(&bytes).is_err() {
}
}
match self.udp_socket.recv(&mut buf) {
Ok(len) if len > 0 => {
self.handle_received_data(&buf[..len]);
}
Err(_) => {
continue;
}
_ => {
continue;
}
}
}
}
fn handle_received_data(&mut self, data: &[u8]) {
match ProtocolFrame::from_bytes(data) {
Ok(frame) => {
match frame.frame_type() {
protocol::FrameType::Data => {
self.handle_data_frame(frame);
}
protocol::FrameType::Nack => {
self.handle_nack_frame(frame);
}
protocol::FrameType::Heartbeat => {
self.handle_heartbeat_frame(frame);
}
}
}
Err(e) => {
match e {
PubSubError::CrcCheckFailed => {
}
_ => {
}
}
}
}
}
fn handle_data_frame(&mut self, frame: protocol::ProtocolFrame) {
let topic_id = frame.topic_id();
let seq_num = frame.seq_num();
let payload = frame.payload();
if let Some(missing_seq_num) = self.subscribers.check_seq_num(topic_id, seq_num) {
let nack_frame = protocol::ProtocolFrame::new_nack_frame(missing_seq_num, topic_id);
let nack_bytes = nack_frame.to_bytes();
if self.udp_socket.send(&nack_bytes).is_err() {
}
} else {
if let Err(_e) = self.subscribers.handle_data(topic_id, payload) {
}
}
}
fn handle_nack_frame(&mut self, frame: protocol::ProtocolFrame) {
let topic_id = frame.topic_id();
let seq_num = frame.seq_num();
match self.publisher.handle_nack(seq_num, topic_id) {
Ok(frames) => {
for frame in frames {
let bytes = frame.to_bytes();
if self.udp_socket.send(&bytes).is_err() {
}
}
}
Err(_) => {
}
}
}
fn handle_heartbeat_frame(&mut self, _frame: protocol::ProtocolFrame) {
let heartbeat_timeout = self.config.heartbeat_interval.as_millis() as u64 * 3;
if let Err(_e) = self.subscribers.cleanup_inactive(heartbeat_timeout) {
}
}
pub fn subscribe(&mut self, topic_id: u16, callback: PubSubCallback) -> Result<SubscriptionId> {
self.subscribers.subscribe(topic_id, callback)
}
pub fn unsubscribe(&mut self, subscription_id: SubscriptionId) -> Result<()> {
self.subscribers.unsubscribe(subscription_id)
}
pub fn publish(&mut self, topic_id: u16, data: &[u8]) -> Result<()> {
let frame = self.publisher.create_frame(topic_id, data)?;
let bytes = frame.to_bytes();
self.udp_socket.send(&bytes)?;
Ok(())
}
pub fn register_topic(&mut self, topic_name: &'static str, topic_id: u16) -> Result<()> {
self.subscribers.register_topic(topic_name, topic_id)
}
pub fn get_topic_id(&self, topic_name: &str) -> Option<u16> {
self.subscribers.get_topic_id(topic_name)
}
pub fn get_topic_name(&self, topic_id: u16) -> Option<&'static str> {
self.subscribers.get_topic_name(topic_id)
}
pub fn shutdown(&mut self) -> Result<()> {
self.udp_socket.close()?;
self.is_running = false;
Ok(())
}
}
pub fn init(config: PubSubConfig) -> Result<()> {
unsafe {
while PUB_SUB_INIT_LOCK.swap(true, Ordering::Acquire) {
core::hint::spin_loop();
}
struct LockGuard;
impl Drop for LockGuard {
fn drop(&mut self) {
PUB_SUB_INIT_LOCK.store(false, Ordering::Release);
}
}
let _guard = LockGuard;
let pubsub_ptr = core::ptr::addr_of_mut!(PUB_SUB_INSTANCE);
if (*pubsub_ptr).is_some() {
return Ok(());
}
let mut pubsub = PubSub::new(config)?;
pubsub.init()?;
register_predefined_topics(&mut pubsub)?;
*pubsub_ptr = Some(pubsub);
Ok(())
}
}
fn register_predefined_topics(pubsub: &mut PubSub) -> Result<()> {
let wal_topics = get_all_wal_topics();
for (i, topic) in wal_topics.iter().enumerate() {
pubsub.register_topic(topic, i as u16 + 1)?;
}
let core_topics = get_core_topics();
for (i, topic) in core_topics.iter().enumerate() {
pubsub.register_topic(topic, i as u16 + 11)?;
}
Ok(())
}
pub fn subscribe(topic_id: u16, callback: PubSubCallback) -> Result<SubscriptionId> {
unsafe {
let pubsub_ptr = core::ptr::addr_of_mut!(PUB_SUB_INSTANCE);
if let Some(ref mut pubsub) = *pubsub_ptr {
pubsub.subscribe(topic_id, callback)
} else {
Err(PubSubError::InitFailed)
}
}
}
pub fn unsubscribe(subscription_id: SubscriptionId) -> Result<()> {
unsafe {
let pubsub_ptr = core::ptr::addr_of_mut!(PUB_SUB_INSTANCE);
if let Some(ref mut pubsub) = *pubsub_ptr {
pubsub.unsubscribe(subscription_id)
} else {
Err(PubSubError::InitFailed)
}
}
}
pub fn publish(topic_id: u16, data: &[u8]) -> Result<()> {
unsafe {
let pubsub_ptr = core::ptr::addr_of_mut!(PUB_SUB_INSTANCE);
if let Some(ref mut pubsub) = *pubsub_ptr {
pubsub.publish(topic_id, data)
} else {
Err(PubSubError::InitFailed)
}
}
}
#[cfg(feature = "posix")]
pub fn start_receiver() -> Result<()> {
unsafe {
let pubsub_ptr = core::ptr::addr_of_mut!(PUB_SUB_INSTANCE);
if let Some(ref mut pubsub) = *pubsub_ptr {
pubsub.start_receiver()
} else {
Err(PubSubError::InitFailed)
}
}
}
pub fn register_topic(topic_name: &'static str, topic_id: u16) -> Result<()> {
unsafe {
let pubsub_ptr = core::ptr::addr_of_mut!(PUB_SUB_INSTANCE);
if let Some(ref mut pubsub) = *pubsub_ptr {
pubsub.register_topic(topic_name, topic_id)
} else {
Err(PubSubError::InitFailed)
}
}
}
pub fn get_topic_id(topic_name: &str) -> Option<u16> {
unsafe {
let pubsub_ptr = core::ptr::addr_of!(PUB_SUB_INSTANCE);
if let Some(ref pubsub) = *pubsub_ptr {
pubsub.get_topic_id(topic_name)
} else {
None
}
}
}
pub fn get_topic_name(topic_id: u16) -> Option<&'static str> {
unsafe {
let pubsub_ptr = core::ptr::addr_of!(PUB_SUB_INSTANCE);
if let Some(ref pubsub) = *pubsub_ptr {
pubsub.get_topic_name(topic_id)
} else {
None
}
}
}
pub(crate) fn get_global_pubsub() -> Option<&'static mut PubSub> {
unsafe {
let pubsub_ptr = core::ptr::addr_of_mut!(PUB_SUB_INSTANCE);
(*pubsub_ptr).as_mut()
}
}
pub fn shutdown() -> Result<()> {
unsafe {
while PUB_SUB_INIT_LOCK.swap(true, Ordering::Acquire) {
core::hint::spin_loop();
}
struct LockGuard;
impl Drop for LockGuard {
fn drop(&mut self) {
PUB_SUB_INIT_LOCK.store(false, Ordering::Release);
}
}
let _guard = LockGuard;
let pubsub_ptr = core::ptr::addr_of_mut!(PUB_SUB_INSTANCE);
if (*pubsub_ptr).is_some() {
let pubsub = (*pubsub_ptr).as_mut().unwrap();
let result = pubsub.shutdown();
*pubsub_ptr = None;
result
} else {
Ok(())
}
}
}