use iceoryx2::port::publisher::Publisher;
use iceoryx2::prelude::*;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use super::{serialize_to_payload, AppId, Event, IpcError, IpcPayload, IpcResult};
#[derive(Debug)]
pub struct EventPublisher {
publisher: Publisher<ipc::Service, IpcPayload, ()>,
app_id: AppId,
is_active: Arc<AtomicBool>,
}
impl EventPublisher {
pub fn new(app_id: AppId) -> IpcResult<Self> {
let service_name = "e_midi_events".to_string();
println!(
"[IPC PUBLISHER DEBUG] Using service name: {} (app_id: {:?})",
service_name, app_id
);
let node = NodeBuilder::new().create::<ipc::Service>().map_err(|e| {
eprintln!("[IPC PUBLISHER ERROR] Node creation failed: {:?}", e);
IpcError::NodeCreation("Node creation failed".to_string())
})?;
let service = node
.service_builder(&ServiceName::new(&service_name).map_err(|e| {
eprintln!("[IPC PUBLISHER ERROR] Invalid service name: {:?}", e);
IpcError::ServiceCreation("Invalid service name".to_string())
})?)
.publish_subscribe::<IpcPayload>()
.max_publishers(16)
.max_subscribers(16)
.open_or_create()
.map_err(|e| {
eprintln!("[IPC PUBLISHER ERROR] Failed to create service: {:?}", e);
IpcError::ServiceCreation("Failed to create service".to_string())
})?;
let publisher = service.publisher_builder().create().map_err(|e| {
eprintln!("[IPC PUBLISHER ERROR] Failed to create publisher: {:?}", e);
IpcError::PublisherCreation("Failed to create publisher".to_string())
})?;
Ok(Self {
publisher,
app_id,
is_active: Arc::new(AtomicBool::new(true)),
})
}
pub fn publish(&self, event: Event) -> IpcResult<()> {
if !self.is_active.load(Ordering::Relaxed) {
return Err(IpcError::SendError("Publisher is not active".to_string()));
}
let payload = serialize_to_payload(&event)?;
match self.publisher.send_copy(payload) {
Ok(_) => {
println!("[IPC PUBLISHER DEBUG] Successfully sent event: {:?}", event);
}
Err(e) => {
eprintln!(
"[IPC PUBLISHER ERROR] Failed to send event: {:?} (error: {:?})",
event, e
);
return Err(IpcError::SendError(format!(
"Failed to send event: {:?}",
e
)));
}
}
Ok(())
}
pub fn publish_batch(&self, events: Vec<Event>) -> IpcResult<()> {
if !self.is_active.load(Ordering::Relaxed) {
return Err(IpcError::SendError("Publisher is not active".to_string()));
}
let payload = serialize_to_payload(&events)?;
self.publisher
.send_copy(payload)
.map_err(|_| IpcError::SendError("Failed to send batch".to_string()))?;
Ok(())
}
pub fn is_active(&self) -> bool {
self.is_active.load(Ordering::Relaxed)
}
pub fn deactivate(&self) {
self.is_active.store(false, Ordering::Relaxed);
}
pub fn app_id(&self) -> AppId {
self.app_id
}
}
impl Drop for EventPublisher {
fn drop(&mut self) {
self.deactivate();
}
}
unsafe impl Send for EventPublisher {}
unsafe impl Sync for EventPublisher {}
impl EventPublisher {
pub fn heartbeat(&self) -> IpcResult<()> {
self.publish(Event::system_heartbeat(self.app_id))
}
pub fn midi_started(&self, song_index: usize, song_name: String) -> IpcResult<()> {
self.publish(Event::midi_playback_started(song_index, song_name))
}
pub fn midi_stopped(&self) -> IpcResult<()> {
self.publish(Event::midi_playback_stopped())
}
pub fn midi_tempo_changed(&self, new_tempo: u32) -> IpcResult<()> {
self.publish(Event::midi_tempo_changed(new_tempo))
}
pub fn midi_progress(&self, progress_ms: u32, total_ms: u32) -> IpcResult<()> {
self.publish(Event::midi_progress_update(progress_ms, total_ms))
}
}