first working version
This commit is contained in:
		
						commit
						db8370e642
					
				
					 8 changed files with 240 additions and 0 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| /target | ||||
| .idea | ||||
							
								
								
									
										7
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| # This file is automatically @generated by Cargo. | ||||
| # It is not intended for manual editing. | ||||
| version = 3 | ||||
| 
 | ||||
| [[package]] | ||||
| name = "servicepoint2" | ||||
| version = "0.1.0" | ||||
							
								
								
									
										6
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| [package] | ||||
| name = "servicepoint2" | ||||
| version = "0.1.0" | ||||
| edition = "2021" | ||||
| 
 | ||||
| [dependencies] | ||||
							
								
								
									
										44
									
								
								src/bit_vec.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/bit_vec.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | |||
| use crate::Packet; | ||||
| 
 | ||||
| /// A vector of bits
 | ||||
| #[derive(Debug)] | ||||
| pub struct BitVec { | ||||
|     data: Vec<u8>, | ||||
| } | ||||
| 
 | ||||
| impl From<BitVec> for Packet { | ||||
|     fn from(value: BitVec) -> Self { | ||||
|         value.data | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl BitVec { | ||||
|     pub fn new(size: usize) -> BitVec { | ||||
|         assert_eq!(size % 8, 0); | ||||
|         Self { data: vec!(0; size / 8) } | ||||
|     } | ||||
| 
 | ||||
|     pub fn set(&mut self, index: usize, value: bool) -> bool { | ||||
|         let byte_index = index / 8; | ||||
|         let bit_in_byte_index = 7 - index % 8; | ||||
|         let bit_mask = 1 << bit_in_byte_index; | ||||
| 
 | ||||
|         let byte = self.data[byte_index]; | ||||
|         let old_value = byte & bit_mask != 0; | ||||
| 
 | ||||
|         self.data[byte_index] = if value { | ||||
|             byte | bit_mask | ||||
|         } else { | ||||
|             byte ^ bit_mask | ||||
|         }; | ||||
| 
 | ||||
|         return old_value; | ||||
|     } | ||||
| 
 | ||||
|     pub fn get(&self, index: usize) -> bool { | ||||
|         let byte_index = index / 8; | ||||
|         let bit_in_byte_index = 7 - index % 8; | ||||
|         let bit_mask = 1 << bit_in_byte_index; | ||||
|         return self.data[byte_index] & bit_mask != 0; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										22
									
								
								src/connection.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/connection.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| use std::net::{ToSocketAddrs, UdpSocket}; | ||||
| use crate::{Command, Packet}; | ||||
| 
 | ||||
| pub struct Connection { | ||||
|     socket: UdpSocket, | ||||
| } | ||||
| 
 | ||||
| impl Connection { | ||||
|     /// Open a new UDP socket and create a display instance
 | ||||
|     pub fn open(addr: impl ToSocketAddrs) -> std::io::Result<Self> { | ||||
|         let socket = UdpSocket::bind("0.0.0.0:0")?; | ||||
|         socket.connect(addr)?; | ||||
|         Ok(Self { socket }) | ||||
|     } | ||||
| 
 | ||||
|     /// Send a command to the display
 | ||||
|     pub fn send(&self, command: Command) -> std::io::Result<()> { | ||||
|         let packet: Packet = command.into(); | ||||
|         self.socket.send(&packet)?; | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										98
									
								
								src/lib.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/lib.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,98 @@ | |||
| mod connection; | ||||
| mod pixel_grid; | ||||
| mod bit_vec; | ||||
| 
 | ||||
| pub use crate::connection::*; | ||||
| pub use crate::pixel_grid::*; | ||||
| pub use crate::bit_vec::*; | ||||
| 
 | ||||
| pub const TILE_SIZE: u16 = 8; | ||||
| pub const TILE_WIDTH: u16 = 56; | ||||
| pub const TILE_HEIGHT: u16 = 20; | ||||
| pub const PIXEL_WIDTH: u16 = TILE_WIDTH * TILE_SIZE; | ||||
| pub const PIXEL_HEIGHT: u16 = TILE_HEIGHT * TILE_SIZE; | ||||
| 
 | ||||
| pub const PIXEL_COUNT: usize = PIXEL_WIDTH as usize * PIXEL_HEIGHT as usize; | ||||
| 
 | ||||
| /// A window
 | ||||
| #[derive(Debug)] | ||||
| pub struct Window(pub Origin, pub Size); | ||||
| 
 | ||||
| /// An origin marks the top left position of the
 | ||||
| /// data sent to the display.
 | ||||
| /// A window
 | ||||
| #[derive(Debug)] | ||||
| pub struct Origin(pub u16, pub u16); | ||||
| 
 | ||||
| /// Size defines the width and height of a window
 | ||||
| /// A window
 | ||||
| #[derive(Debug)] | ||||
| pub struct Size(pub u16, pub u16); | ||||
| 
 | ||||
| type Offset = u16; | ||||
| 
 | ||||
| type Brightness = u8; | ||||
| 
 | ||||
| type Packet = Vec<u8>; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum Command { | ||||
|     Clear, | ||||
|     HardReset, | ||||
|     FadeOut, | ||||
|     CharBrightness(Window, Vec<Brightness>), | ||||
|     Brightness(Brightness), | ||||
|     BitmapLinear(Offset, BitVec), | ||||
|     BitmapLinearAnd(Offset, BitVec), | ||||
|     BitmapLinearOr(Offset, BitVec), | ||||
|     BitmapLinearXor(Offset, BitVec), | ||||
|     Cp437Data(Window, Vec<u8>), | ||||
|     BitmapLinearWin(Window, PixelGrid), | ||||
| } | ||||
| 
 | ||||
| fn offset_and_payload(command: u16, offset: Offset, payload: Vec<u8>) -> Packet { | ||||
|     let mut packet = vec!(0u8; 10 + payload.len()); | ||||
| 
 | ||||
|     packet[0..=1].copy_from_slice(&u16::to_be_bytes(command)); | ||||
|     packet[2..=3].copy_from_slice(&u16::to_be_bytes(offset)); | ||||
|     packet[4..=5].copy_from_slice(&u16::to_be_bytes(payload.len() as u16)); | ||||
|     packet[6..=7].copy_from_slice(&[0x00, 0x00]); // subcommand 0 => no compression
 | ||||
|     packet[8..=9].copy_from_slice(&[0x00, 0x00]); // reserved
 | ||||
| 
 | ||||
|     packet[10..].copy_from_slice(&*payload); | ||||
| 
 | ||||
|     packet | ||||
| } | ||||
| 
 | ||||
| fn window_and_payload(command: u16, window: Window, payload: Vec<u8>) -> Packet { | ||||
|     let Window(Origin(x, y), Size(w, h)) = window; | ||||
| 
 | ||||
|     let mut packet = vec!(0u8; 10 + payload.len()); | ||||
|     packet[0..=1].copy_from_slice(&u16::to_be_bytes(command)); | ||||
|     packet[2..=3].copy_from_slice(&u16::to_be_bytes(x)); | ||||
|     packet[4..=5].copy_from_slice(&u16::to_be_bytes(y)); | ||||
|     packet[6..=7].copy_from_slice(&u16::to_be_bytes(w)); | ||||
|     packet[8..=9].copy_from_slice(&u16::to_be_bytes(h)); | ||||
| 
 | ||||
|     packet[10..].copy_from_slice(&*payload); | ||||
| 
 | ||||
|     packet | ||||
| } | ||||
| 
 | ||||
| impl From<Command> for Packet { | ||||
|     fn from(value: Command) -> Self { | ||||
|         match value { | ||||
|             Command::Clear => vec!(0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), | ||||
|             Command::CharBrightness(window, payload) => window_and_payload(0x0005, window, payload), | ||||
|             Command::Brightness(brightness) => vec!(0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, brightness), | ||||
|             Command::HardReset => vec!(0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), | ||||
|             Command::FadeOut => vec!(0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), | ||||
|             Command::BitmapLinear(offset, payload) => offset_and_payload(0x0012, offset, payload.into()), | ||||
|             Command::BitmapLinearWin(window, payload) => window_and_payload(0x0013, window, payload.into()), | ||||
|             Command::BitmapLinearAnd(offset, payload) => offset_and_payload(0x0014, offset, payload.into()), | ||||
|             Command::BitmapLinearOr(offset, payload) => offset_and_payload(0x0015, offset, payload.into()), | ||||
|             Command::BitmapLinearXor(offset, payload) => offset_and_payload(0x0016, offset, payload.into()), | ||||
|             Command::Cp437Data(window, payload) => window_and_payload(0x0003, window, payload), | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								src/main.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/main.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | |||
| use std::thread; | ||||
| use std::time::Duration; | ||||
| use servicepoint2::{Command, Connection, Origin, PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid, Size, TILE_WIDTH, Window}; | ||||
| 
 | ||||
| fn main() { | ||||
|     // 172.23.42.29
 | ||||
|     let connection = Connection::open("localhost:2342").unwrap(); | ||||
|     connection.send(Command::Clear).unwrap(); | ||||
| 
 | ||||
|     connection.send(Command::Cp437Data(Window(Origin(0, 0), Size(3, 2)), Vec::from("abcdef"))).unwrap(); | ||||
| 
 | ||||
|     loop { | ||||
|         for x_offset in 0..usize::MAX { | ||||
|             let mut pixels = PixelGrid::new(PIXEL_WIDTH as usize, PIXEL_HEIGHT as usize); | ||||
|             for y in 0..PIXEL_HEIGHT as usize { | ||||
|                 for x_add in 0..=y%8 { | ||||
|                     pixels.set((y + x_offset +x_add) % PIXEL_WIDTH as usize, y, true); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             let window = Window(Origin(0, 0), Size(TILE_WIDTH, PIXEL_HEIGHT)); | ||||
|             connection.send(Command::BitmapLinearWin(window, pixels)).unwrap(); | ||||
| 
 | ||||
|             thread::sleep(Duration::from_millis(100)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										34
									
								
								src/pixel_grid.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/pixel_grid.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | |||
| use crate::{BitVec, Packet}; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct PixelGrid { | ||||
|     pub width: usize, | ||||
|     pub height: usize, | ||||
|     bit_vec: BitVec, | ||||
| } | ||||
| 
 | ||||
| impl PixelGrid { | ||||
|     pub fn new(width: usize, height: usize) -> PixelGrid { | ||||
|         assert_eq!(width % 8, 0); | ||||
|         assert_eq!(height % 8, 0); | ||||
|         Self { | ||||
|             width, | ||||
|             height, | ||||
|             bit_vec: BitVec::new(width * height), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn set(&mut self, x: usize, y: usize, value: bool) -> bool { | ||||
|         self.bit_vec.set(x + y * self.width, value) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get(&self, x: usize, y: usize) -> bool { | ||||
|         self.bit_vec.get(x + y * self.width) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<PixelGrid> for Packet { | ||||
|     fn from(value: PixelGrid) -> Self { | ||||
|         value.bit_vec.into() | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter