97 lines
2.6 KiB
Rust
97 lines
2.6 KiB
Rust
|
use std::{
|
||
|
io::{self, ErrorKind},
|
||
|
net::UdpSocket,
|
||
|
};
|
||
|
|
||
|
use image::{Rgb, RgbImage};
|
||
|
use thiserror::Error;
|
||
|
|
||
|
use crate::{Layout, Strandifier, StrandifierError};
|
||
|
|
||
|
#[derive(Debug, Error)]
|
||
|
#[allow(clippy::module_name_repetitions)]
|
||
|
pub enum SenderError {
|
||
|
#[error("IO error")]
|
||
|
Io(#[from] io::Error),
|
||
|
#[error("Strandifier failed")]
|
||
|
Strandifier(#[from] StrandifierError),
|
||
|
#[error("Controller failed to confirm frame")]
|
||
|
ConfirmationTimeout,
|
||
|
#[error("Invalid confirmation packet: {0:?}")]
|
||
|
ConfirmationInvalid(Vec<u8>),
|
||
|
|
||
|
#[error("Confirmation packet for wrong frame number: expected {expected}, got {got}")]
|
||
|
ConfirmationMismatched { expected: u32, got: u32 },
|
||
|
}
|
||
|
|
||
|
/// Sends a strand's data to the controller.
|
||
|
///
|
||
|
/// # Errors
|
||
|
///
|
||
|
/// Returns an error if network communication fails.
|
||
|
pub fn send_strand<I: IntoIterator<Item = Rgb<u8>>>(
|
||
|
socket: &UdpSocket,
|
||
|
strand_num: u32,
|
||
|
frame_num: u32,
|
||
|
pixels: I,
|
||
|
) -> Result<(), SenderError> {
|
||
|
const MAGIC: u32 = u32::from_be_bytes(*b"PIXL");
|
||
|
|
||
|
let pixels = pixels.into_iter().map(|c| {
|
||
|
let mut bytes = [0; 4];
|
||
|
bytes[1..].copy_from_slice(&c.0);
|
||
|
|
||
|
u32::from_be_bytes(bytes)
|
||
|
});
|
||
|
|
||
|
let buf: Vec<_> = [MAGIC, strand_num, frame_num]
|
||
|
.into_iter()
|
||
|
.chain(pixels)
|
||
|
.flat_map(u32::to_be_bytes)
|
||
|
.collect();
|
||
|
|
||
|
socket.send(&buf)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
/// Sends an entire image frame to the controller.
|
||
|
///
|
||
|
/// # Errors
|
||
|
///
|
||
|
/// Returns an error if the image does not have the correct dimensions, if network communication
|
||
|
/// fails or if the controller does not confirm reception of the frame.
|
||
|
pub fn send_frame(
|
||
|
socket: &UdpSocket,
|
||
|
layout: Layout,
|
||
|
frame_num: u32,
|
||
|
image: &RgbImage,
|
||
|
) -> Result<(), SenderError> {
|
||
|
for strand_num in 0..layout.num_strands() {
|
||
|
let data = Strandifier::make_strand(layout, image, strand_num)?;
|
||
|
send_strand(socket, strand_num, frame_num, data)?;
|
||
|
}
|
||
|
|
||
|
let mut buf = vec![0; 100];
|
||
|
let len = match socket.recv(&mut buf) {
|
||
|
Ok(len) => len,
|
||
|
//Err(e) if e.kind() == ErrorKind::TimedOut => return Err(SenderError::ConfirmationTimeout),
|
||
|
Err(e) => return Err(SenderError::ConfirmationTimeout),
|
||
|
Err(e) => return Err(SenderError::Io(e)),
|
||
|
};
|
||
|
let buf = &buf[..len];
|
||
|
let response_frame_num = u32::from_be_bytes(
|
||
|
buf.try_into()
|
||
|
.map_err(|_| SenderError::ConfirmationInvalid(buf.to_vec()))?,
|
||
|
);
|
||
|
|
||
|
if response_frame_num != frame_num {
|
||
|
return Err(SenderError::ConfirmationMismatched {
|
||
|
expected: frame_num,
|
||
|
got: response_frame_num,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
Ok(())
|
||
|
}
|