Skip to content

Commit

Permalink
Add UDP client
Browse files Browse the repository at this point in the history
  • Loading branch information
krzmaz authored and mrozycki committed Dec 4, 2023
1 parent c242411 commit 5df46ac
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 0 deletions.
8 changes: 8 additions & 0 deletions animator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,14 @@ impl ControllerBuilder {
Ok(self)
}

pub fn udp_lights(mut self, path: &str) -> Result<Self, Box<dyn Error>> {
info!("Using udp light client with endpoint {}", path);
self.client_builder = self
.client_builder
.with(Box::new(client::udp::UdpLightClient::new(path)));
Ok(self)
}

#[cfg(feature = "visualiser")]
pub fn visualiser_lights(mut self) -> Result<Self, Box<dyn Error>> {
info!("Using local visualiser");
Expand Down
7 changes: 7 additions & 0 deletions configurator/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ fn capturer_from_options(
);
Box::new(light_client::tcp::TcpLightClient::new(&tcp_endpoint))
}
udp_endpoint if endpoint.starts_with("udp://") => {
info!(
"Using remote UDP light client at endpoint: {}",
udp_endpoint
);
Box::new(light_client::udp::UdpLightClient::new(&udp_endpoint))
}
_ => {
info!("Using local TTY light client");
Box::new(light_client::tty::TtyLightClient::new()?)
Expand Down
1 change: 1 addition & 0 deletions light-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod feedback;
pub mod http;
pub mod tcp;
pub mod tty;
pub mod udp;
#[cfg(feature = "websocket")]
pub mod websocket;

Expand Down
78 changes: 78 additions & 0 deletions light-client/src/udp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use crate::{LightClient, LightClientError};
use async_trait::async_trait;
use lightfx::Frame;
use log::{debug, error, info};
use std::{error::Error, sync::Arc};
use tokio::{net::UdpSocket, sync::Mutex};

#[derive(Clone)]
pub struct UdpLightClient {
url: String,
socket: Arc<Mutex<Option<UdpSocket>>>,
}

impl UdpLightClient {
pub fn new(url: &str) -> Self {
let url = url.strip_prefix("udp://").unwrap_or(url);
Self {
url: url.to_owned(),
socket: Arc::new(Mutex::new(None)),
}
}

async fn connect(&self) -> Result<(), Box<dyn Error>> {
let mut socket = self.socket.lock().await;
if socket.is_none() {
info!("Connecting to remote lights via UDP");
let s = UdpSocket::bind("0.0.0.0:0").await?;
s.connect(&self.url).await?;
// hopefully this gets us some priority, see:
// https://linuxreviews.org/Type_of_Service_(ToS)_and_DSCP_Values
// this sets `high throughput` and `low delay` along with high precedence
s.set_tos(152)?;
*socket = Some(s);
}

Ok(())
}
}

#[async_trait]
impl LightClient for UdpLightClient {
async fn display_frame(&self, frame: &Frame) -> Result<(), LightClientError> {
let pixels: Vec<_> = frame
.pixels_iter()
.cloned()
.map(crate::gamma_correction)
.flat_map(|pixel| vec![pixel.g, pixel.r, pixel.b])
.collect();

if self.socket.lock().await.is_none() {
if let Err(e) = self.connect().await {
error!("Failed to connect to UDP endpoint: {}", e);
} else {
info!("Successfully connected to UDP endpoint");
}
}

let res = {
let mut stream = self.socket.lock().await;
let Some(stream) = stream.as_mut() else {
debug!("UDP endpoint not connected!");
return Err(LightClientError::ConnectionLost);
};
stream
.send(&[&(pixels.len() as u16).to_le_bytes(), pixels.as_slice()].concat())
.await
};

match res {
Ok(_) => Ok(()),
Err(e) => {
error!("Failed to send frame to light client: {}", e);
*self.socket.lock().await = None;
Err(LightClientError::ConnectionLost)
}
}
}
}
2 changes: 2 additions & 0 deletions webapi/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
error!("Web API built without websocket support, ignoring");
} else if url.starts_with("tcp://") {
builder = builder.tcp_lights(&url)?;
} else if url.starts_with("udp://") {
builder = builder.udp_lights(&url)?;
} else {
error!("Unknown remote client protocol, ignoring");
}
Expand Down

0 comments on commit 5df46ac

Please sign in to comment.