From 205cd566cb0fd8174379bd4e0b24e6bdba43b520 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Mon, 27 Jan 2025 22:24:38 +0100 Subject: [PATCH] split out UdpServer --- ...execute_command.rs => command_executor.rs} | 3 +- src/cp437_font.rs | 1 + src/font_renderer.rs | 2 + src/main.rs | 108 +++--------------- src/udp_server.rs | 102 +++++++++++++++++ 5 files changed, 124 insertions(+), 92 deletions(-) rename src/{execute_command.rs => command_executor.rs} (98%) create mode 100644 src/udp_server.rs diff --git a/src/execute_command.rs b/src/command_executor.rs similarity index 98% rename from src/execute_command.rs rename to src/command_executor.rs index 4b74fcf..51ed9e1 100644 --- a/src/execute_command.rs +++ b/src/command_executor.rs @@ -1,5 +1,5 @@ +use crate::command_executor::ExecutionResult::{Failure, Shutdown, Success}; use crate::cp437_font::Cp437Font; -use crate::execute_command::ExecutionResult::{Failure, Shutdown, Success}; use crate::font_renderer::FontRenderer8x8; use log::{debug, error, info, trace, warn}; use servicepoint::{ @@ -9,6 +9,7 @@ use servicepoint::{ use std::ops::{BitAnd, BitOr, BitXor}; use std::sync::RwLock; +#[derive(Debug)] pub struct CommandExecutor<'t> { display: &'t RwLock, luma: &'t RwLock, diff --git a/src/cp437_font.rs b/src/cp437_font.rs index d259c98..7c1e3ff 100644 --- a/src/cp437_font.rs +++ b/src/cp437_font.rs @@ -3,6 +3,7 @@ use std::ops::Index; const CHAR_COUNT: usize = u8::MAX as usize + 1; +#[derive(Debug)] pub struct Cp437Font { bitmaps: [Bitmap; CHAR_COUNT], } diff --git a/src/font_renderer.rs b/src/font_renderer.rs index e277c3d..a8d7368 100644 --- a/src/font_renderer.rs +++ b/src/font_renderer.rs @@ -15,6 +15,7 @@ use pathfinder_geometry::{ use servicepoint::{Bitmap, Grid, Origin, Pixels, TILE_SIZE}; use std::sync::{Mutex, MutexGuard}; +#[derive(Debug)] struct SendFont(Font); // struct is only using primitives and pointers - lets try if it is only missing the declaration @@ -26,6 +27,7 @@ impl AsRef for SendFont { } } +#[derive(Debug)] pub struct FontRenderer8x8 { font: SendFont, canvas: Mutex, diff --git a/src/main.rs b/src/main.rs index fa0b127..4444d90 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,27 +1,21 @@ #![deny(clippy::all)] use crate::font_renderer::FontRenderer8x8; -use crate::{ - execute_command::{CommandExecutor, ExecutionResult}, - gui::{AppEvents, Gui}, -}; +use crate::udp_server::UdpServer; +use crate::{command_executor::CommandExecutor, gui::Gui}; use clap::Parser; use cli::Cli; -use log::{error, info, warn, LevelFilter}; +use log::{info, LevelFilter}; use servicepoint::*; -use std::io::ErrorKind; -use std::net::UdpSocket; use std::sync::{mpsc, RwLock}; -use std::time::Duration; -use winit::event_loop::{ControlFlow, EventLoop, EventLoopProxy}; +use winit::event_loop::{ControlFlow, EventLoop}; mod cli; +mod command_executor; mod cp437_font; -mod execute_command; mod font_renderer; mod gui; - -const BUF_SIZE: usize = 8985; +mod udp_server; fn main() { let mut cli = Cli::parse(); @@ -32,68 +26,35 @@ fn main() { init_logging(cli.verbose); info!("starting with args: {:?}", &cli); - let socket = UdpSocket::bind(&cli.bind).expect("could not bind socket"); - socket - .set_nonblocking(true) - .expect("could not enter non blocking mode"); - - let display = RwLock::new(Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT)); - let luma = RwLock::new(BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT)); - - let (stop_udp_tx, stop_udp_rx) = mpsc::channel(); - let mut gui = Gui::new(&display, &luma, stop_udp_tx, cli.gui); - let event_loop = EventLoop::with_user_event() .build() .expect("could not create event loop"); event_loop.set_control_flow(ControlFlow::Wait); - let event_proxy = event_loop.create_proxy(); + let display = RwLock::new(Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT)); + let luma = RwLock::new(BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT)); + let (stop_udp_tx, stop_udp_rx) = mpsc::channel(); let font_renderer = cli .font .map(move |font| FontRenderer8x8::from_name(font)) .unwrap_or_else(move || FontRenderer8x8::default()); let command_executor = CommandExecutor::new(&display, &luma, font_renderer); + let mut udp_server = UdpServer::new( + cli.bind, + stop_udp_rx, + command_executor, + event_loop.create_proxy(), + ); + let mut gui = Gui::new(&display, &luma, stop_udp_tx, cli.gui); std::thread::scope(move |scope| { - scope.spawn(move || { - let mut buf = [0; BUF_SIZE]; - while stop_udp_rx.try_recv().is_err() { - receive_into_buf(&socket, &mut buf) - .and_then(move |amount| command_from_slice(&buf[..amount])) - .map(|cmd| { - handle_command(&event_proxy, &command_executor, cmd) - }); - } - }); + scope.spawn(move || udp_server.run()); event_loop .run_app(&mut gui) .expect("could not run event loop"); }); } -fn handle_command( - event_proxy: &EventLoopProxy, - command_executor: &CommandExecutor, - command: Command, -) { - match command_executor.execute(command) { - ExecutionResult::Success => { - event_proxy - .send_event(AppEvents::UdpPacketHandled) - .expect("could not send packet handled event"); - } - ExecutionResult::Failure => { - error!("failed to execute command"); - } - ExecutionResult::Shutdown => { - event_proxy - .send_event(AppEvents::UdpThreadClosed) - .expect("could not send close event"); - } - } -} - fn init_logging(debug: bool) { let filter = if debug { LevelFilter::Debug @@ -105,38 +66,3 @@ fn init_logging(debug: bool) { .parse_default_env() .init(); } - -fn command_from_slice(slice: &[u8]) -> Option { - let packet = servicepoint::Packet::try_from(slice) - .inspect_err(|_| { - warn!("could not load packet with length {}", slice.len()) - }) - .ok()?; - Command::try_from(packet) - .inspect_err(move |err| { - warn!("could not read command for packet: {:?}", err) - }) - .ok() -} - -fn receive_into_buf( - socket: &UdpSocket, - buf: &mut [u8; BUF_SIZE], -) -> Option { - let (amount, _) = match socket.recv_from(buf) { - Err(err) if err.kind() == ErrorKind::WouldBlock => { - std::thread::sleep(Duration::from_millis(1)); - return None; - } - Ok(result) => result, - other => other.unwrap(), - }; - - if amount == buf.len() { - warn!( - "the received package may have been truncated to a length of {}", - amount - ); - } - Some(amount) -} diff --git a/src/udp_server.rs b/src/udp_server.rs new file mode 100644 index 0000000..4c603a1 --- /dev/null +++ b/src/udp_server.rs @@ -0,0 +1,102 @@ +use crate::command_executor::{CommandExecutor, ExecutionResult}; +use crate::gui::AppEvents; +use log::{error, warn}; +use servicepoint::Command; +use std::io::ErrorKind; +use std::net::UdpSocket; +use std::sync::mpsc::Receiver; +use std::time::Duration; +use winit::event_loop::EventLoopProxy; + +const BUF_SIZE: usize = 8985; + +#[derive(Debug)] +pub struct UdpServer<'t> { + socket: UdpSocket, + stop_rx: Receiver<()>, + command_executor: CommandExecutor<'t>, + app_events: EventLoopProxy, + buf: [u8; BUF_SIZE], +} + +impl<'t> UdpServer<'t> { + pub fn new( + bind: String, + stop_rx: Receiver<()>, + command_executor: CommandExecutor<'t>, + app_events: EventLoopProxy, + ) -> Self { + let socket = UdpSocket::bind(bind).expect("could not bind socket"); + socket + .set_nonblocking(true) + .expect("could not enter non blocking mode"); + + Self { + socket, + stop_rx, + command_executor, + app_events, + buf: [0; BUF_SIZE], + } + } + + pub(crate) fn run(&mut self) { + while self.stop_rx.try_recv().is_err() { + self.receive_into_buf() + .and_then(|amount| { + Self::command_from_slice(&self.buf[..amount]) + }) + .map(|cmd| self.handle_command(cmd)); + } + } + + fn command_from_slice(slice: &[u8]) -> Option { + let packet = servicepoint::Packet::try_from(slice) + .inspect_err(|_| { + warn!("could not load packet with length {}", slice.len()) + }) + .ok()?; + Command::try_from(packet) + .inspect_err(move |err| { + warn!("could not read command for packet: {:?}", err) + }) + .ok() + } + + fn receive_into_buf(&mut self) -> Option { + let (amount, _) = match self.socket.recv_from(&mut self.buf) { + Err(err) if err.kind() == ErrorKind::WouldBlock => { + std::thread::sleep(Duration::from_millis(1)); + return None; + } + Ok(result) => result, + other => other.unwrap(), + }; + + if amount == self.buf.len() { + warn!( + "the received package may have been truncated to a length of {}", + amount + ); + } + Some(amount) + } + + fn handle_command(&mut self, command: Command) { + match self.command_executor.execute(command) { + ExecutionResult::Success => { + self.app_events + .send_event(AppEvents::UdpPacketHandled) + .expect("could not send packet handled event"); + } + ExecutionResult::Failure => { + error!("failed to execute command"); + } + ExecutionResult::Shutdown => { + self.app_events + .send_event(AppEvents::UdpThreadClosed) + .expect("could not send close event"); + } + } + } +}