Skip to main content

interface/
interfaces.rs

1use crate::artnet::ArtnetInterface;
2use common::fixture::{MAX_CHANNEL, calculate_dmx_values, Fixture};
3use std::io;
4use std::net::UdpSocket;
5use std::sync::mpsc::Receiver;
6use std::thread::sleep;
7use std::time::{Duration, Instant};
8use common::logging::LogLevel::*;
9use common::r_log;
10
11const TARGET: &str = "255.255.255.255:6454";
12const FREQUENCY: u64 = 23;
13
14//TODO
15/// Defines the baseline behavior for any hardware or network DMX output interface.
16///
17/// **Note:** Currently, the system hardcodes ArtNet output. Future refactoring
18/// will allow modular instantiation of multiple interface types via this trait.
19pub trait DmxInterface {
20    /// Dispatches a single DMX universe to the connected lighting fixtures.
21    ///
22    /// # Arguments
23    ///
24    /// * `local_universe_index` - The 0-based index of the universe to output on (useful for multi-universe nodes).
25    /// * `data` - A full 512-byte array representing the computed DMX channel values for this universe.
26    fn send_universe(
27        &self,
28        local_universe_index: u16,
29        data: &[u8; MAX_CHANNEL as usize],
30    ) -> Result<(), io::Error>;
31}
32
33//TODO
34/// Initializes the output interface and continuously streams DMX data on a dedicated thread.
35///
36/// This loop non-blockingly consumes the latest engine state from the provided MPSC receiver,
37/// calculates the raw DMX universe buffers, and dispatches them via the configured interfaces.
38/// It automatically throttles its execution to match the target frame rate defined by [`FREQUENCY`].
39///
40/// # Arguments
41///
42/// * `data_receiver` - A channel receiver providing tuple updates containing the current
43///   total universe count and the latest snapshot of all active fixtures.
44pub fn dmx_output_loop(data_receiver: Receiver<(usize,Vec<Fixture>)>) -> io::Result<()> {
45    let socket = UdpSocket::bind("0.0.0.0:0")?;
46    socket.set_broadcast(true)?;
47
48    let artnet_interface: Box<dyn DmxInterface> =
49        Box::new(ArtnetInterface::new(socket, TARGET.to_string()));
50
51    r_log!(Info,"Starting artnet");
52
53    let mut universe_count: usize = 0;
54    let mut fixtures: Vec<Fixture> = vec![];
55
56    loop {
57        let start = Instant::now();
58
59        while let Ok((new_count, new_fixtures)) = data_receiver.try_recv() {
60            universe_count = new_count;
61            fixtures = new_fixtures;
62        }
63
64
65        let universes = calculate_dmx_values(universe_count, &fixtures);
66
67        for (universe_index, data) in universes.iter().enumerate() {
68            artnet_interface.send_universe(universe_index as u16, data)?;
69        }
70
71        let elapsed = start.elapsed();
72        if elapsed < Duration::from_millis(FREQUENCY) {
73            sleep(Duration::from_millis(FREQUENCY) - elapsed);
74        }
75    }
76}