split out UdpServer
This commit is contained in:
		
							parent
							
								
									9f75acfff7
								
							
						
					
					
						commit
						205cd566cb
					
				
					 5 changed files with 124 additions and 92 deletions
				
			
		|  | @ -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<Bitmap>, | ||||
|     luma: &'t RwLock<BrightnessGrid>, | ||||
|  | @ -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], | ||||
| } | ||||
|  |  | |||
|  | @ -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<Font> for SendFont { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct FontRenderer8x8 { | ||||
|     font: SendFont, | ||||
|     canvas: Mutex<Canvas>, | ||||
|  |  | |||
							
								
								
									
										108
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										108
									
								
								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<AppEvents>, | ||||
|     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<Command> { | ||||
|     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<usize> { | ||||
|     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) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										102
									
								
								src/udp_server.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/udp_server.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<AppEvents>, | ||||
|     buf: [u8; BUF_SIZE], | ||||
| } | ||||
| 
 | ||||
| impl<'t> UdpServer<'t> { | ||||
|     pub fn new( | ||||
|         bind: String, | ||||
|         stop_rx: Receiver<()>, | ||||
|         command_executor: CommandExecutor<'t>, | ||||
|         app_events: EventLoopProxy<AppEvents>, | ||||
|     ) -> 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<Command> { | ||||
|         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<usize> { | ||||
|         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"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter