Compare commits
	
		
			4 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7d159087b3 | |||
| e409071cc1 | |||
| a006bd87a6 | |||
| baf149ef8a | 
| @ -35,3 +35,7 @@ Until = 48 | ||||
| [[transformers]] | ||||
| [transformers.intensity] | ||||
| 
 | ||||
| 
 | ||||
| [[transformers]] | ||||
| [transformers.angle_correction] | ||||
| coef = 2000.0 | ||||
|  | ||||
| @ -1,151 +0,0 @@ | ||||
| extern crate ether_dream; | ||||
| 
 | ||||
| use ether_dream::dac; | ||||
| 
 | ||||
| fn main() { | ||||
| 	println!("Listening for an Ether Dream DAC..."); | ||||
| 
 | ||||
| 	let (dac_broadcast, source_addr) = ether_dream::recv_dac_broadcasts() | ||||
| 		.expect("failed to bind to UDP socket") | ||||
| 		.filter_map(Result::ok) | ||||
| 		.next() | ||||
| 		.unwrap(); | ||||
| 	let mac_address = dac::MacAddress(dac_broadcast.mac_address); | ||||
| 
 | ||||
| 	println!( | ||||
| 		"Discovered DAC \"{}\" at \"{}\"! Connecting...", | ||||
| 		mac_address, source_addr | ||||
| 	); | ||||
| 
 | ||||
| 	// Establish the TCP connection.
 | ||||
| 	let mut stream = dac::stream::connect(&dac_broadcast, source_addr.ip().clone()).unwrap(); | ||||
| 
 | ||||
| 	// If we want to create an animation (in our case a moving sine wave) we need a frame rate.
 | ||||
| 	let frames_per_second = 60.0; | ||||
| 	// Lets use the DAC at an eighth the maximum scan rate.
 | ||||
| 	let points_per_second = stream.dac().max_point_rate / 32; | ||||
| 	// Determine the number of points per frame given our target frame and point rates.
 | ||||
| 	let points_per_frame = (points_per_second as f32 / frames_per_second) as u16; | ||||
| 
 | ||||
| 	println!( | ||||
| 		"Preparing for playback:\n\tframe_hz: {}\n\tpoint_hz: {}\n\tpoints_per_frame: {}\n", | ||||
| 		frames_per_second, points_per_second, points_per_frame | ||||
| 	); | ||||
| 
 | ||||
| 	// Prepare the DAC's playback engine and await the repsonse.
 | ||||
| 	stream | ||||
| 		.queue_commands() | ||||
| 		.prepare_stream() | ||||
| 		.submit() | ||||
| 		.err() | ||||
| 		.map(|err| { | ||||
| 			eprintln!( | ||||
| 				"err occurred when submitting PREPARE_STREAM \ | ||||
|                       command and listening for response: {}",
 | ||||
| 				err | ||||
| 			); | ||||
| 		}); | ||||
| 
 | ||||
| 	println!("Beginning playback!"); | ||||
| 
 | ||||
| 	// The sine wave used to generate points.
 | ||||
| 	let mut sine_wave = SineWave { | ||||
| 		point: 0, | ||||
| 		points_per_frame, | ||||
| 		frames_per_second, | ||||
| 	}; | ||||
| 
 | ||||
| 	// Queue the initial frame and tell the DAC to begin producing output.
 | ||||
| 	let n_points = points_to_generate(stream.dac()); | ||||
| 	stream | ||||
| 		.queue_commands() | ||||
| 		.data(sine_wave.by_ref().take(n_points)) | ||||
| 		.begin(0, points_per_second) | ||||
| 		.submit() | ||||
| 		.err() | ||||
| 		.map(|err| { | ||||
| 			eprintln!( | ||||
| 				"err occurred when submitting initial DATA and BEGIN \ | ||||
|                       commands and listening for response: {}",
 | ||||
| 				err | ||||
| 			); | ||||
| 		}); | ||||
| 	eprintln!("Stream dac{:?}", stream.dac()); | ||||
| 
 | ||||
| 	// Loop and continue to send points forever.
 | ||||
| 	loop { | ||||
| 		// Determine how many points the DAC can currently receive.
 | ||||
| 		let n_points = points_to_generate(stream.dac()); | ||||
| 		if let Err(err) = stream | ||||
| 			.queue_commands() | ||||
| 			.data(sine_wave.by_ref().take(n_points)) | ||||
| 			.submit() | ||||
| 		{ | ||||
| 			eprintln!( | ||||
| 				"err occurred when submitting DATA command and listening \ | ||||
|                       for response: {}",
 | ||||
| 				err | ||||
| 			); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Tell the DAC to stop producing output and return to idle. Wait for the response.
 | ||||
| 	//
 | ||||
| 	// Note that the DAC is commanded to stop on `Drop` if this is not called and any errors
 | ||||
| 	// produced are ignored.
 | ||||
| 	stream | ||||
| 		.queue_commands() | ||||
| 		.stop() | ||||
| 		.submit() | ||||
| 		.expect("err occurred when submitting STOP command and listening for response"); | ||||
| } | ||||
| 
 | ||||
| // Determine the number of points needed to fill the DAC.
 | ||||
| fn points_to_generate(dac: ðer_dream::dac::Dac) -> usize { | ||||
| 	dac.buffer_capacity as usize - 1 - dac.status.buffer_fullness as usize | ||||
| } | ||||
| 
 | ||||
| // An iterator that endlessly generates a sine wave of DAC points.
 | ||||
| //
 | ||||
| // The sine wave oscillates at a rate of once per second.
 | ||||
| struct SineWave { | ||||
| 	point: u32, | ||||
| 	points_per_frame: u16, | ||||
| 	frames_per_second: f32, | ||||
| } | ||||
| 
 | ||||
| impl Iterator for SineWave { | ||||
| 	type Item = ether_dream::protocol::DacPoint; | ||||
| 	fn next(&mut self) -> Option<Self::Item> { | ||||
| 		let coloured_points_per_frame = self.points_per_frame - 1; | ||||
| 		let i = (self.point % self.points_per_frame as u32) as u16; | ||||
| 		let hz = 1.0; | ||||
| 		let fract = i as f32 / coloured_points_per_frame as f32; | ||||
| 		let phase = (self.point as f32 / coloured_points_per_frame as f32) / self.frames_per_second; | ||||
| 		let amp = (hz * (fract + phase) * 2.0 * std::f32::consts::PI).sin(); | ||||
| 		let (r, g, b) = match i { | ||||
| 			i if i == coloured_points_per_frame || i < 13 => (0, 0, 0), | ||||
| 			_ => (std::u16::MAX, std::u16::MAX, std::u16::MAX), | ||||
| 		}; | ||||
| 		let x_min = std::i16::MIN; | ||||
| 		let x_max = std::i8::MAX as i16; | ||||
| 		let x = (x_min as f32 + fract * (x_max as f32 - x_min as f32)) as i16; | ||||
| 		let y = (amp * x_max as f32) as i16; | ||||
| 		let control = 0; | ||||
| 		let (u1, u2) = (0, 0); | ||||
| 		let p = ether_dream::protocol::DacPoint { | ||||
| 			control, | ||||
| 			x, | ||||
| 			y, | ||||
| 			i, | ||||
| 			r, | ||||
| 			g, | ||||
| 			b, | ||||
| 			u1, | ||||
| 			u2, | ||||
| 		}; | ||||
| 		self.point += 1; | ||||
| 		Some(p) | ||||
| 	} | ||||
| } | ||||
| @ -1,34 +1,26 @@ | ||||
| use std::io::_print; | ||||
| /**
 | ||||
| 
 | ||||
| # Populate Redis Example | ||||
| 
 | ||||
| **This script simulates the redis content provided by the LJ Python / web tool** | ||||
| 
 | ||||
| $ cargo run --example populate_redis | ||||
|  **/ | ||||
| 
 | ||||
| ///
 | ||||
| /// $ cargo run --example populate_redis
 | ||||
| ///
 | ||||
| use redis::{ | ||||
| 	//RedisResult,
 | ||||
| 	Client, | ||||
| 	Commands, | ||||
| 	Connection, | ||||
|     //RedisResult,
 | ||||
|     Client, | ||||
|     Commands, | ||||
|     Connection, | ||||
| }; | ||||
| 
 | ||||
| fn do_something() -> redis::RedisResult<()> { | ||||
| 	let client = Client::open("redis://127.0.0.1/")?; | ||||
| 	let mut con: Connection = client.get_connection()?; | ||||
| 	let _ = con.set("/clientkey", "/pl/0/")?; | ||||
| 	let _ = con.set("/EDH/0", "[[1.0, 0.0, 0.0],\n [ 0.0, 1.0, 0.0],\n [ 0.0, 0.0, 1.0]]")?; | ||||
| 	let _ = con.set("/kpps/0", "5000")?; | ||||
| 	let _ = con.set("/intensity/0", "255")?; | ||||
| 	let _ = con.set("/pl/0/0", "[(1000, 2000, 0), (1000, 1000, 65535), (2000, 1000, 65535), (2000, 2000, 65535), (1000, 2000, 65535)]")?; | ||||
| 	Ok(()) | ||||
| } | ||||
|     let client = Client::open("redis://127.0.0.1/")?; | ||||
|     let mut con: Connection = client.get_connection()?; | ||||
| 
 | ||||
| fn main() { | ||||
| 	match do_something() { | ||||
| 		Err(err)    => println!("Something wrong occured: {:?}", err), | ||||
| 		Ok(..)                  => println!("Successfully inserted content in Redis") | ||||
| 	} | ||||
|     let _ = con.set("/clientkey", "/pl/0/")?; | ||||
|     let _ = con.set( | ||||
|         "/EDH/0", | ||||
|         "[[1.0, 0.0, 0.0],\n [ 0.0, 1.0, 0.0],\n [ 0.0, 0.0, 1.0]]", | ||||
|     )?; | ||||
|     let _ = con.set("/kpps/0", "5000")?; | ||||
|     let _ = con.set("/intensity/0", "255")?; | ||||
|     Ok(()) | ||||
| } | ||||
| fn main() { | ||||
|     _ = do_something(); | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| ///
 | ||||
| /// $ cargo run --example simple_client
 | ||||
| ///
 | ||||
| 
 | ||||
| use redis::{ | ||||
|     //RedisResult,
 | ||||
|     Client, | ||||
| @ -17,26 +16,22 @@ fn do_something() -> redis::RedisResult<()> { | ||||
|     let start = Instant::now(); | ||||
| 
 | ||||
|     loop { | ||||
| 	let elapsed = start.elapsed(); | ||||
| 	let time = 60.0 * elapsed.as_millis() as f32 / 1000.0; | ||||
|         let elapsed = start.elapsed(); | ||||
|         let time = 60.0 * elapsed.as_millis() as f32 / 1000.0; | ||||
| 
 | ||||
| 	let mut v : Vec<(f32,f32,u32)> = vec![]; | ||||
|         let mut v: Vec<(f32, f32, u32)> = vec![]; | ||||
| 
 | ||||
| 	for i in 0..128 { | ||||
| 	    let a = (time + i as f32) / 128.0 * std::f32::consts::PI * 2.0; | ||||
| 	    let r = 1200.0 + (a*5.0).cos() * (500.0 * (time/5.0).cos()); | ||||
|         for i in 0..128 { | ||||
|             let a = (time + i as f32) / 128.0 * std::f32::consts::PI * 2.0; | ||||
|             let r = 1200.0 + (a * 5.0).cos() * (500.0 * (time / 5.0).cos()); | ||||
| 
 | ||||
| 	    let x = a.cos() * r; | ||||
| 	    let y = a.sin() * r; | ||||
| 	    let col = if i % 8 < 4 { | ||||
| 		0x000000ff | ||||
| 	    } else { | ||||
| 		0x00ff0000 | ||||
| 	    }; | ||||
| 	    v.push((x,y,col)); | ||||
| 	} | ||||
| 	// println!("{:?}", v);
 | ||||
| 	let _ = con.set("/pl/0/0", format!("{:?}", v))?; | ||||
|             let x = a.cos() * r; | ||||
|             let y = a.sin() * r; | ||||
|             let col = if i % 8 < 4 { 0x000000ff } else { 0x00ff0000 }; | ||||
|             v.push((x, y, col)); | ||||
|         } | ||||
|         // println!("{:?}", v);
 | ||||
|         let _ = con.set("/pl/0/0", format!("{:?}", v))?; | ||||
|     } | ||||
|     // Ok(())
 | ||||
| } | ||||
|  | ||||
| @ -1,46 +0,0 @@ | ||||
| ///
 | ||||
| /// $ cargo run --example simple_client
 | ||||
| ///
 | ||||
| 
 | ||||
| use redis::{ | ||||
| 	//RedisResult,
 | ||||
| 	Client, | ||||
| 	Commands, | ||||
| 	Connection, | ||||
| }; | ||||
| 
 | ||||
| use std::time::Instant; | ||||
| 
 | ||||
| fn do_something() -> redis::RedisResult<()> { | ||||
| 	let client = Client::open("redis://127.0.0.1/")?; | ||||
| 	let mut con: Connection = client.get_connection()?; | ||||
| 	let start = Instant::now(); | ||||
| 
 | ||||
| 	loop { | ||||
| 		let elapsed = start.elapsed(); | ||||
| 		let time = 60.0 * elapsed.as_millis() as f32 / 1000.0; | ||||
| 
 | ||||
| 		let mut v: Vec<(f32, f32, u32)> = vec![]; | ||||
| 
 | ||||
| 		for i in 0..128 { | ||||
| 			let a = (time + i as f32) / 128.0 * std::f32::consts::PI * 2.0; | ||||
| 			let r = 1200.0 + (a * 5.0).cos() * (500.0 * (time / 5.0).cos()); | ||||
| 
 | ||||
| 			let x = a.cos() * r; | ||||
| 			let y = a.sin() * r; | ||||
| 			let col = if i % 8 < 4 { | ||||
| 				0x000000ff | ||||
| 			} else { | ||||
| 				0x00ff0000 | ||||
| 			}; | ||||
| 			v.push((x, y, col)); | ||||
| 		} | ||||
| 		// println!("{:?}", v);
 | ||||
| 		let _ = con.set("/pl/0/0", format!("{:?}", v))?; | ||||
| 	} | ||||
| 	// Ok(())
 | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	_ = do_something(); | ||||
| } | ||||
							
								
								
									
										159
									
								
								src/conf.rs
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								src/conf.rs
									
									
									
									
									
								
							| @ -1,106 +1,109 @@ | ||||
| use config::Config; | ||||
| use serde::{Serialize, Deserialize}; | ||||
| use crate::errors::{LJError, LJResult}; | ||||
| use crate::transformer; | ||||
| use config::Config; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize, Debug, Clone)] | ||||
| pub struct Conf { | ||||
| 	pub laser_id: u8, | ||||
| 	pub debug: bool, | ||||
| 	pub redis_url: String, | ||||
| 	pub dac: DacFamily, | ||||
| 	#[serde(default)] | ||||
| 	pub transformers: Vec<TransformConf>, | ||||
|     pub laser_id: u8, | ||||
|     pub debug: bool, | ||||
|     pub redis_url: String, | ||||
|     pub dac: DacFamily, | ||||
|     #[serde(default)] | ||||
|     pub transformers: Vec<TransformConf>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize, Debug, Clone)] | ||||
| pub enum DacFamily { | ||||
| 	#[serde(rename = "helios")] | ||||
| 	Helios(HeliosConf), | ||||
| 	#[serde(rename = "etherdream")] | ||||
| 	Etherdream(EtherDreamConf), | ||||
| 	#[serde(rename = "dummy")] | ||||
| 	Dummy, | ||||
|     #[serde(rename = "helios")] | ||||
|     Helios(HeliosConf), | ||||
|     #[serde(rename = "etherdream")] | ||||
|     Etherdream(EtherDreamConf), | ||||
|     #[serde(rename = "dummy")] | ||||
|     Dummy, | ||||
| } | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize, Debug, Clone)] | ||||
| pub struct HeliosConf { | ||||
| 	pub id: u8, | ||||
|     pub id: u8, | ||||
| } | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize, Debug, Clone)] | ||||
| pub struct EtherDreamConf { | ||||
| 	pub ip: String, | ||||
|     pub ip: String, | ||||
| } | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize, Debug, Clone)] | ||||
| pub enum TransformConf { | ||||
| 	#[serde(rename = "translate")] | ||||
| 	Translate(transformer::Translate), | ||||
| 	#[serde(rename = "replicate")] | ||||
| 	Replicate(transformer::Replicate), | ||||
| 	#[serde(rename = "rotate")] | ||||
| 	Rotate(transformer::Rotate), | ||||
| 	#[serde(rename = "flip_horizontal")] | ||||
| 	FlipH(transformer::FlipHorizontal), | ||||
| 	#[serde(rename = "flip_vertical")] | ||||
| 	FlipV(transformer::FlipVertical), | ||||
| 	#[serde(rename = "grid")] | ||||
| 	Grid(transformer::Grid), | ||||
| 	#[serde(rename = "homography")] | ||||
| 	Homography(transformer::Homography), | ||||
| 	#[serde(rename = "helios_to_etherdream")] | ||||
| 	HeliosToEtherdream(transformer::HeliosToEtherdream), | ||||
| 	#[serde(rename = "intensity")] | ||||
| 	Intensity(transformer::Intensity), | ||||
|     #[serde(rename = "translate")] | ||||
|     Translate(transformer::Translate), | ||||
|     #[serde(rename = "replicate")] | ||||
|     Replicate(transformer::Replicate), | ||||
|     #[serde(rename = "rotate")] | ||||
|     Rotate(transformer::Rotate), | ||||
|     #[serde(rename = "flip_horizontal")] | ||||
|     FlipH(transformer::FlipHorizontal), | ||||
|     #[serde(rename = "flip_vertical")] | ||||
|     FlipV(transformer::FlipVertical), | ||||
|     #[serde(rename = "grid")] | ||||
|     Grid(transformer::Grid), | ||||
|     #[serde(rename = "homography")] | ||||
|     Homography(transformer::Homography), | ||||
|     #[serde(rename = "angle_correction")] | ||||
|     AngleOptimisation(transformer::AngleOptimisation), | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| impl Conf { | ||||
| 	pub fn new(path: &str) -> LJResult<Conf> { | ||||
| 		let settings = Config::builder() | ||||
| 			.add_source(config::File::with_name(path)) | ||||
| 			.build()?; | ||||
|     pub fn new(path: &str) -> LJResult<Conf> { | ||||
|         let settings = Config::builder() | ||||
|             .add_source(config::File::with_name(path)) | ||||
|             .build()?; | ||||
| 
 | ||||
| 		let conf: Conf = settings.try_deserialize().map_err(LJError::Config)?; | ||||
| 		Ok(conf) | ||||
| 	} | ||||
|         dbg!("path:", path); | ||||
|         //println!("plop");
 | ||||
|         dbg!(settings.clone()); | ||||
|         let conf: Conf = settings.try_deserialize().map_err(LJError::Config)?; | ||||
| 
 | ||||
| 	pub fn get_transformers(&self) -> Vec<Box<dyn transformer::Transformers>> { | ||||
| 		let mut v = vec![]; | ||||
| 		for t in &self.transformers { | ||||
| 			let t: Box<dyn transformer::Transformers> = match t { | ||||
| 				TransformConf::FlipH(r) => Box::new(*r), | ||||
| 				TransformConf::FlipV(r) => Box::new(*r), | ||||
| 				TransformConf::Grid(r) => Box::new(*r), | ||||
| 				TransformConf::HeliosToEtherdream(r) => Box::new(*r), | ||||
| 				TransformConf::Homography(r) => Box::new(*r), | ||||
| 				TransformConf::Intensity(r) => Box::new(*r), | ||||
| 				TransformConf::Replicate(r) => Box::new(*r), | ||||
| 				TransformConf::Rotate(r) => Box::new(*r), | ||||
| 				TransformConf::Translate(t) => Box::new(*t), | ||||
| 			}; | ||||
| 			v.push(t); | ||||
| 		} | ||||
| 		v | ||||
| 	} | ||||
|         //println!("plum");
 | ||||
|         Ok(conf) | ||||
|     } | ||||
| 
 | ||||
| 	#[allow(dead_code)] | ||||
| 	pub fn dump() { | ||||
| 		let conf = Conf { | ||||
| 			laser_id: 0, | ||||
| 			debug: true, | ||||
| 			redis_url: "redis://127.0.0.1:6379/".to_string(), | ||||
| 			dac: DacFamily::Helios(HeliosConf { id: 0 }), | ||||
| 			transformers: vec![ | ||||
| 				TransformConf::Translate(transformer::Translate { | ||||
| 					x: 2000.0, | ||||
| 					y: 2000.0, | ||||
| 				}), | ||||
| 				TransformConf::Replicate(transformer::Replicate::Until(48)), | ||||
| 			], | ||||
| 		}; | ||||
| 		let s = toml::to_string(&conf).unwrap(); | ||||
| 		println!("{}", s); | ||||
| 	} | ||||
|     //println!("plop");
 | ||||
| 
 | ||||
|     pub fn get_transformers(&self) -> Vec<Box<dyn transformer::Transformers>> { | ||||
|         let mut v = vec![]; | ||||
|         for t in &self.transformers { | ||||
|             let t: Box<dyn transformer::Transformers> = match t { | ||||
|                 TransformConf::Translate(t) => Box::new(*t), | ||||
|                 TransformConf::Replicate(r) => Box::new(*r), | ||||
|                 TransformConf::Rotate(r) => Box::new(*r), | ||||
|                 TransformConf::FlipH(r) => Box::new(*r), | ||||
|                 TransformConf::FlipV(r) => Box::new(*r), | ||||
|                 TransformConf::Grid(r) => Box::new(*r), | ||||
|                 TransformConf::Homography(r) => Box::new(*r), | ||||
|                 TransformConf::AngleOptimisation(r) => Box::new(*r), | ||||
|             }; | ||||
|             v.push(t); | ||||
|         } | ||||
|         v | ||||
|     } | ||||
| 
 | ||||
|     #[allow(dead_code)] | ||||
|     pub fn dump() { | ||||
|         let conf = Conf { | ||||
|             laser_id: 0, | ||||
|             debug: true, | ||||
|             redis_url: "redis://127.0.0.1:6379/".to_string(), | ||||
|             dac: DacFamily::Helios(HeliosConf { id: 0 }), | ||||
|             transformers: vec![ | ||||
|                 TransformConf::Translate(transformer::Translate { | ||||
|                     x: 2000.0, | ||||
|                     y: 2000.0, | ||||
|                 }), | ||||
|                 TransformConf::Replicate(transformer::Replicate::Until(48)), | ||||
|             ], | ||||
|         }; | ||||
|         let s = toml::to_string(&conf).unwrap(); | ||||
|         println!("{}", s); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,15 +1,15 @@ | ||||
| mod helios; | ||||
| mod dummy; | ||||
| mod etherdream; | ||||
| mod helios; | ||||
| 
 | ||||
| use std::fmt; | ||||
| use crate::conf::{Conf, DacFamily /*EtherDreamConf, HeliosConf*/}; | ||||
| use crate::device::helios::HeliosDevice; | ||||
| use crate::device::dummy::DummyDevice; | ||||
| use crate::device::etherdream::EtherdreamDevice; | ||||
| use crate::device::helios::HeliosDevice; | ||||
| use crate::errors::LJResult; | ||||
| use crate::point::Point; | ||||
| use serde::Serialize; | ||||
| use crate::device::etherdream::EtherdreamDevice; | ||||
| use std::fmt; | ||||
| 
 | ||||
| /* | ||||
| self.protocol_version, | ||||
| @ -26,24 +26,25 @@ self.point_count | ||||
| #[repr(u8)] | ||||
| #[derive(Debug, PartialEq, Serialize, Copy, Clone)] | ||||
| pub enum PlaybackState { | ||||
| 	IDLE = 0, | ||||
| 	PREPARE = 1, | ||||
| 	PLAYING = 2 | ||||
|     IDLE = 0, | ||||
|     PREPARE = 1, | ||||
|     PLAYING = 2, | ||||
|     UNKNOWN = 99, | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for PlaybackState { | ||||
| 	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 		write!(f, "{:?}", self) | ||||
| 	} | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         write!(f, "{:?}", self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct Status { | ||||
| 	pub last_traced_at: String, | ||||
| 	pub properties: Vec<String>, | ||||
| 	pub playback_state: PlaybackState, | ||||
| 	pub capacity: usize, | ||||
| 	pub lack: String, | ||||
|     pub last_traced_at: String, | ||||
|     pub properties: Vec<String>, | ||||
|     pub playback_state: PlaybackState, | ||||
|     pub capacity: u16, | ||||
|     pub lack: String, | ||||
| } | ||||
| 
 | ||||
| // /lstt/lasernumber       etherdream last_status.playback_state  (0: idle   1: prepare   2: playing)
 | ||||
| @ -51,21 +52,17 @@ pub struct Status { | ||||
| // /lack/lasernumber       "a": ACK   "F": Full  "I": invalid. 64 or 35 for no connection.
 | ||||
| 
 | ||||
| pub trait Device { | ||||
| 	fn status(&mut self) -> Status; | ||||
| 	fn draw( | ||||
| 		&mut self, | ||||
| 		frame: Vec<Point>, | ||||
| 		speed: u32, | ||||
| 	) -> LJResult<()>; | ||||
| 	fn stop(&mut self) -> LJResult<()>; | ||||
| 	fn grid(&mut self) -> Vec<Point>; | ||||
|     fn status(&mut self) -> Status; | ||||
|     fn draw(&mut self, frame: Vec<Point>, speed: u32) -> LJResult<()>; | ||||
|     fn stop(&mut self) -> LJResult<()>; | ||||
|     fn grid(&mut self) -> Vec<Point>; | ||||
| } | ||||
| 
 | ||||
| pub fn device_factory(config: &Conf) -> LJResult<Box<dyn Device>> { | ||||
| 	let device: Box<dyn Device> = match &config.dac { | ||||
| 		DacFamily::Helios(conf) => Box::new(HeliosDevice::new(conf)?), | ||||
| 		DacFamily::Etherdream( conf) => Box::new( EtherdreamDevice::new(conf)?), | ||||
| 		DacFamily::Dummy => Box::new(DummyDevice::new()?) | ||||
| 	}; | ||||
| 	Ok(device) | ||||
|     let device: Box<dyn Device> = match &config.dac { | ||||
|         DacFamily::Helios(conf) => Box::new(HeliosDevice::new(conf)?), | ||||
|         DacFamily::Etherdream(conf) => Box::new(EtherdreamDevice::new(conf)?), | ||||
|         DacFamily::Dummy => Box::new(DummyDevice::new()?), | ||||
|     }; | ||||
|     Ok(device) | ||||
| } | ||||
|  | ||||
| @ -1,43 +1,44 @@ | ||||
| use crate::device::{Device, Status, PlaybackState}; | ||||
| use crate::device::{Device, PlaybackState, Status}; | ||||
| use crate::errors::LJResult; | ||||
| use crate::point::{Color, Point}; | ||||
| use log::debug; | ||||
| 
 | ||||
| pub struct DummyDevice { | ||||
|     state: PlaybackState | ||||
|     state: PlaybackState, | ||||
| } | ||||
| 
 | ||||
| impl DummyDevice { | ||||
| 	pub fn new() -> LJResult<Self> { | ||||
| 	    Ok(Self { state: PlaybackState::IDLE }) | ||||
| 	} | ||||
|     pub fn new() -> LJResult<Self> { | ||||
|         Ok(Self { | ||||
|             state: PlaybackState::IDLE, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Device for DummyDevice { | ||||
| 	fn status(&mut self) -> Status { | ||||
| 		Status { | ||||
| 			last_traced_at: "never".to_string(), | ||||
| 			properties: vec!["foo".to_string()], | ||||
| 			playback_state: self.state, | ||||
| 			capacity: 0, | ||||
| 			lack: "lack".to_string() | ||||
| 		} | ||||
| 	} | ||||
|     fn status(&mut self) -> Status { | ||||
|         Status { | ||||
|             last_traced_at: "never".to_string(), | ||||
|             properties: vec!["foo".to_string()], | ||||
|             playback_state: self.state, | ||||
|             capacity: 0, | ||||
|             lack: "lack".to_string(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 	fn draw(&mut self, | ||||
| 	        line: Vec<Point>, | ||||
| 	        speed: u32, | ||||
| 	) -> LJResult<()> { | ||||
| 	    debug!("Draw Line at speed {speed} : {:?}", line); | ||||
| 	    Ok(()) | ||||
| 	} | ||||
|     fn draw(&mut self, line: Vec<Point>, speed: u32) -> LJResult<()> { | ||||
|         debug!("Draw Line at speed {speed} : {:?}", line); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
| 	fn stop(&mut self) -> LJResult<()> { | ||||
| 		Ok(()) | ||||
| 	} | ||||
| 	fn grid(&mut self) -> Vec<Point> { | ||||
| 		vec!( | ||||
| 			Point{ x: 0 as f32, y: 0 as f32, color:Color{ r: 0, g: 0, b: 0 }} | ||||
| 		) | ||||
| 	} | ||||
|     fn stop(&mut self) -> LJResult<()> { | ||||
|         Ok(()) | ||||
|     } | ||||
|     fn grid(&mut self) -> Vec<Point> { | ||||
|         vec![Point { | ||||
|             x: 0 as f32, | ||||
|             y: 0 as f32, | ||||
|             color: Color { r: 0, g: 0, b: 0 }, | ||||
|         }] | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,308 +1,155 @@ | ||||
| #[warn(unused_imports)] | ||||
| use log::{ debug, info, warn}; | ||||
| 
 | ||||
| use ether_dream::dac::stream::connect; | ||||
| use ether_dream::dac::Stream; | ||||
| use std::net::SocketAddr; | ||||
| use std::thread::sleep; | ||||
| use ether_dream::dac::stream::{CommunicationError, connect}; | ||||
| use ether_dream::dac::{Playback, Stream}; | ||||
| use chrono::{DateTime, Utc}; | ||||
| use std::time; | ||||
| use std::time::{Duration, SystemTime}; | ||||
| 
 | ||||
| use crate::conf::EtherDreamConf; | ||||
| use crate::device::{Device, Status, PlaybackState}; | ||||
| use crate::device::{Device, PlaybackState, Status}; | ||||
| use crate::errors::{LJError, LJResult}; | ||||
| use crate::point::{Color, Point}; | ||||
| use ether_dream::protocol::{DacBroadcast, DacResponse}; | ||||
| 
 | ||||
| use ether_dream::protocol::{DacBroadcast, DacStatus}; | ||||
| use log::{info, warn}; | ||||
| 
 | ||||
| #[warn(dead_code)] | ||||
| pub struct EtherdreamDevice { | ||||
| 	pub conf: EtherDreamConf, | ||||
| 	dac: DacBroadcast, | ||||
| 	stream: Stream, | ||||
| 
 | ||||
| 	// "a": ACK   "F": Full  "I": invalid. 64 or 35 for no connection.
 | ||||
| 	//     /// The previous command was accepted.
 | ||||
| 	//     pub const ACK: u8 = 0x61;
 | ||||
| 	//     /// The write command could not be performed because there was not enough buffer space when it
 | ||||
| 	//     /// was received.
 | ||||
| 	//     pub const NAK_FULL: u8 = 0x46;
 | ||||
| 	//     /// The command contained an invalid `command` byte or parameters.
 | ||||
| 	//     pub const NAK_INVALID: u8 = 0x49;
 | ||||
| 	//     /// An emergency-stop condition still exists.
 | ||||
| 	//     pub const NAK_STOP_CONDITION: u8 = 0x21;
 | ||||
| 	// }
 | ||||
| 	dac_response: u8, | ||||
|     pub conf: EtherDreamConf, | ||||
|     dac: DacBroadcast, | ||||
|     // source_address: SocketAddr,
 | ||||
|     stream: Stream, | ||||
|     // sent_points: u16,
 | ||||
|     lack: String, | ||||
|     last_traced_at: String, | ||||
| } | ||||
| 
 | ||||
| impl EtherdreamDevice { | ||||
| 	pub fn new(conf: &EtherDreamConf) -> LJResult<Self> { | ||||
| 		let (dac, _source_address, stream) = EtherdreamDevice::connect(conf)?; | ||||
| 		Ok(Self { | ||||
| 			conf: (*conf).clone(), | ||||
| 			dac, | ||||
| 			stream, | ||||
| 			dac_response: DacResponse::ACK, | ||||
| 		}) | ||||
| 	} | ||||
| 	fn connect(conf: &EtherDreamConf) -> LJResult<(DacBroadcast, SocketAddr, Stream)> { | ||||
| 		let ip = &conf.ip; | ||||
| 		let dac_broadcast = ether_dream::recv_dac_broadcasts()?; | ||||
| 		dac_broadcast.set_timeout(Some(time::Duration::new(10, 0)))?; | ||||
| 		info!("Attempting to get DAC broadcast..."); | ||||
| 		let broadcast = dac_broadcast | ||||
| 			.take(3) | ||||
| 			.filter_map(|result| { | ||||
| 				match result { | ||||
| 					Err(err) => { | ||||
| 						warn!( "Failed to find a valid DAC via broadcast. Error: {:?}", err); | ||||
| 						info!( "Retrying..."); | ||||
| 						None | ||||
| 					} | ||||
| 					Ok((dac, source_addr)) => { | ||||
| 						info!("Valid broadcast, source_addr: {}", source_addr); | ||||
| 						if source_addr.is_ipv6() { | ||||
| 							warn!("Broadcast ignored: ipv6 address."); | ||||
| 							return None; | ||||
| 					        } | ||||
| 						let src_ip = source_addr.ip().to_string(); | ||||
| 						if &src_ip != ip { | ||||
| 							warn!("Broadcast ignored: expected {ip}, got: {src_ip} "); | ||||
| 							return None; | ||||
| 						} | ||||
| 						Some(Ok((dac, source_addr))) | ||||
| 					} | ||||
| 				} | ||||
| 			}) | ||||
| 			.next() | ||||
| 			.expect("Failed to receive broadcast."); | ||||
| 		match broadcast { | ||||
| 			Err(err) => { | ||||
| 				Err(Box::new(LJError::EtherdreamConnectError(err))) | ||||
| 			} | ||||
| 			Ok((dac, source_addr)) => { | ||||
| 				info!("Trying to open TCP stream..."); | ||||
| 				let stream = EtherdreamDevice::get_tcp_stream(&dac, &source_addr)?; | ||||
| 				info!("Finished configuring DAC and TCP stream."); | ||||
| 				Ok((dac, source_addr, stream)) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|     pub fn new(conf: &EtherDreamConf) -> LJResult<Self> { | ||||
|         let (dac, _source_address, stream) = EtherdreamDevice::get_dac(conf)?; | ||||
|         // let (dac, source_address) = EtherdreamDevice::get_dac(conf)?;
 | ||||
|         Ok(Self { | ||||
|             conf: (*conf).clone(), | ||||
|             dac, | ||||
|             // source_address,
 | ||||
|             stream, | ||||
|             // sent_points: 0,
 | ||||
|             lack: "".to_string(), | ||||
|             last_traced_at: "1985-04-12T23:20:50.52Z".to_string(), | ||||
|         }) | ||||
|     } | ||||
|     pub fn get_dac(conf: &EtherDreamConf) -> LJResult<(DacBroadcast, SocketAddr, Stream)> { | ||||
|         let ip = &conf.ip; | ||||
|         let dac_broadcast = ether_dream::recv_dac_broadcasts()?; | ||||
|         dac_broadcast.set_timeout(Some(time::Duration::new(10, 0)))?; | ||||
|         info!("Attempting to get DAC broadcast..."); | ||||
|         let broadcast = dac_broadcast | ||||
|             .filter_map(|result| match result { | ||||
|                 Err(err) => { | ||||
|                     warn!("Failed to find a valid DAC via broadcast. Error: {:?}", err); | ||||
|                     info!("Retrying..."); | ||||
|                     None | ||||
|                 } | ||||
|                 Ok((dac, source_addr)) => { | ||||
|                     if source_addr.is_ipv6() { | ||||
|                         return None; | ||||
|                     } | ||||
|                     if &source_addr.ip().to_string() != ip { | ||||
|                         return None; | ||||
|                     } | ||||
|                     info!("Valid broadcast"); | ||||
|                     Some(Ok((dac, source_addr))) | ||||
|                 } | ||||
|             }) | ||||
|             .next() | ||||
|             .expect("Failed to receive broadcast."); | ||||
|         match broadcast { | ||||
|             Err(err) => Err(Box::new(LJError::EtherdreamConnectError(err))), | ||||
|             Ok((dac, source_addr)) => { | ||||
|                 let stream = EtherdreamDevice::get_tcp_stream(&dac, &source_addr)?; | ||||
|                 Ok((dac, source_addr, stream)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 	fn get_tcp_stream(dac: &DacBroadcast, source_address: &SocketAddr) -> LJResult<Stream> { | ||||
| 		let mut stream = connect(dac, source_address.ip())?; | ||||
| 		match stream | ||||
| 			.queue_commands() | ||||
| 			.prepare_stream() | ||||
| 			.submit() { | ||||
| 			Err(err) => warn!("err occurred when submitting PREPARE_STREAM command and listening for response: {}",err), | ||||
| 			Ok(_) => info!("Prepared Stream.") | ||||
| 		} | ||||
| 		// If we want to create an animation (in our case a moving sine wave) we need a frame rate.
 | ||||
| 		let frames_per_second = 60.0; | ||||
| 		// Lets use the DAC at an eighth the maximum scan rate.
 | ||||
| 		let points_per_second = stream.dac().max_point_rate / 32; | ||||
| 		// Determine the number of points per frame given our target frame and point rates.
 | ||||
| 		let points_per_frame = (points_per_second as f32 / frames_per_second) as u16; | ||||
|     pub fn get_tcp_stream(dac: &DacBroadcast, source_address: &SocketAddr) -> LJResult<Stream> { | ||||
|         // Establish the TCP connection.
 | ||||
|         let mut stream = connect(dac, source_address.ip())?; | ||||
| 
 | ||||
| 		let mut sine_wave = SineWave { | ||||
| 			point: 0, | ||||
| 			points_per_frame, | ||||
| 			frames_per_second, | ||||
| 		}; | ||||
|         // Prepare  stream
 | ||||
|         stream | ||||
|             .queue_commands() | ||||
|             .prepare_stream() | ||||
|             .submit() | ||||
|             .err() | ||||
|             .map(|err| { | ||||
|                 eprintln!( | ||||
|                     "err occurred when submitting PREPARE_STREAM \ | ||||
| 		                  command and listening for response: {}",
 | ||||
|                     err | ||||
|                 ); | ||||
|             }); | ||||
| 
 | ||||
| 		match stream | ||||
| 			.queue_commands() | ||||
| 			.data(sine_wave.by_ref().take(400)) | ||||
| 			// .data(begin_list.into_iter().take(400 as usize))
 | ||||
| 			.begin(0, points_per_second) | ||||
| 			.submit() { | ||||
| 			Err(err) => warn!("err occurred when submitting first data: {}",err), | ||||
| 			Ok(_) => info!("Sent first data to Etherdream.") | ||||
| 		} | ||||
|         Ok(stream) | ||||
|     } | ||||
| 
 | ||||
| 		Ok(stream) | ||||
| 	} | ||||
|     pub fn check_tcp_stream(&mut self) -> LJResult<()> { | ||||
|         // todo Reinit stream if needed
 | ||||
|         // self.stream = EtherdreamDevice::get_tcp_stream(&self.dac, &self.source_address)?
 | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
| 	fn points_capacity(&self) -> usize { | ||||
| 		/*** | ||||
| 		Determine the number of points needed to fill the DAC. | ||||
| 		 ***/ | ||||
| 		// Fixme thread 'main' panicked at 'attempt to subtract with overflow', src/device/etherdream.rs:144:24
 | ||||
| 		let n_points = self.dac.buffer_capacity as usize - self.stream.dac().dac.status.buffer_fullness as usize - 1; | ||||
| 		n_points | ||||
| 	} | ||||
| 
 | ||||
| 	fn ping(&mut self) -> LJResult<()> { | ||||
| 
 | ||||
| 		Ok(self.stream.queue_commands().ping().submit()?) | ||||
| 
 | ||||
| 	} | ||||
|     // Determine the number of points needed to fill the DAC.
 | ||||
|     fn points_to_generate(&self) -> usize { | ||||
|         self.dac.buffer_capacity as usize - 1 - self.dac.dac_status.buffer_fullness as usize | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Device for EtherdreamDevice { | ||||
| 	fn status(&mut self) -> Status { | ||||
| 		let playback_state = match self.stream.dac().dac.status.playback { | ||||
| 			Playback::Idle => PlaybackState::IDLE, | ||||
| 			Playback::Prepared => PlaybackState::PREPARE, | ||||
| 			Playback::Playing => PlaybackState::PLAYING, | ||||
| 		}; | ||||
| 		let now = SystemTime::now(); | ||||
| 		let now: DateTime<Utc> = now.into(); | ||||
| 		let now = now.to_rfc3339(); | ||||
|     fn status(&mut self) -> Status { | ||||
|         let _ = self.check_tcp_stream(); | ||||
| 
 | ||||
| 		Status { | ||||
| 			last_traced_at: now, | ||||
| 			properties: vec!["foo".to_string()], | ||||
| 			playback_state, | ||||
| 			capacity: self.points_capacity(), | ||||
| 			lack: self.dac_response.to_string(), | ||||
| 		} | ||||
| 		// debug!("Dac Status: {:?} ", status );
 | ||||
| 		// debug!("Etherdream Dac {:?} ", self.dac );
 | ||||
| 		// debug!("Stream dac{:?}", self.stream.dac());
 | ||||
| 		// status
 | ||||
| 	} | ||||
|         // "a": ACK   "F": Full  "I": invalid. 64 or 35 for no connection.
 | ||||
|         let playback_state = match self.dac.dac_status.playback_state { | ||||
|             DacStatus::PLAYBACK_IDLE => PlaybackState::IDLE, | ||||
|             DacStatus::PLAYBACK_PREPARED => PlaybackState::PREPARE, | ||||
|             DacStatus::PLAYBACK_PLAYING => PlaybackState::PLAYING, | ||||
|             _ => PlaybackState::UNKNOWN, | ||||
|         }; | ||||
| 
 | ||||
| 	fn draw(&mut self, | ||||
| 	        line: Vec<Point>, | ||||
| 	        _speed: u32, | ||||
| 	) -> LJResult<()> { | ||||
| 		let chunk_size = 64; | ||||
| 		let points_iter = line.into_iter(); | ||||
| 		for chunk in  points_iter.as_slice().chunks(chunk_size){ | ||||
| 			debug!("New chunk length: {:?}", chunk.len()); | ||||
| 			loop { | ||||
| 				let capacity = self.points_capacity(); | ||||
| 				if chunk.len() > capacity { | ||||
| 					debug!("Sleep, capacity : {:?}", capacity); | ||||
| 					// Sleep for 1/100th of a sec
 | ||||
| 					sleep(Duration::new( 0,     10000000)); | ||||
| 					self.ping()?; | ||||
| 				} else { | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 			debug!("drawing"); | ||||
| 			match self.stream | ||||
| 				.queue_commands() | ||||
| 				.data( | ||||
| 					chunk.iter() | ||||
| 						.map(|point| (*point).into()) | ||||
| 						.take(chunk_size) | ||||
| 				) | ||||
| 				.submit() { | ||||
| 				Err(err) => { | ||||
| 					// We should account for
 | ||||
| 					// 'Broken pipe (os error 32)'
 | ||||
| 					// Connection reset by peer (os error 104)
 | ||||
| 					self.dac_response = match err { | ||||
| 						CommunicationError::Io(err) => { | ||||
| 							warn!("IO ERROR while drawing: '{}'",err); | ||||
| 							DacResponse::ACK | ||||
| 						} | ||||
| 						CommunicationError::Protocol(err) => { | ||||
| 							warn!("Protocol ERROR while drawing: '{}'",err); | ||||
| 							DacResponse::ACK | ||||
| 						} | ||||
| 						CommunicationError::Response(err) => { | ||||
| 							warn!("Response ERROR while drawing: '{}'",err); | ||||
| 							err.response.response | ||||
| 						} | ||||
| 					}; | ||||
| 				} | ||||
| 				Ok(_) => { | ||||
| 					self.dac_response = DacResponse::ACK; | ||||
| 					// debug!("Draw is ok");
 | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		Ok(()) | ||||
| 	} | ||||
|         Status { | ||||
|             last_traced_at: self.last_traced_at.clone(), | ||||
|             properties: vec!["foo".to_string()], | ||||
|             playback_state, | ||||
|             capacity: self.dac.dac_status.buffer_fullness, | ||||
|             lack: String::from(&self.lack), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 	fn stop(&mut self) -> LJResult<()> { | ||||
| 		info!("Stopping Etherdream device..."); | ||||
| 		match self.stream | ||||
| 			.queue_commands() | ||||
| 			.stop() | ||||
| 			.submit() | ||||
| 		{ | ||||
| 			Err(err) => { | ||||
| 				warn!("Failed to stop EtherDream device with error {:?}", err); | ||||
| 				Err(Box::new(err)) | ||||
| 			} | ||||
| 			Ok(_) => { | ||||
| 				info!("Sucessfully closed EtherDream device."); | ||||
| 				Ok(()) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|     fn draw(&mut self, line: Vec<Point>, _speed: u32) -> LJResult<()> { | ||||
|         let n_points = self.points_to_generate(); | ||||
|         self.stream | ||||
|             .queue_commands() | ||||
|             .data(line.into_iter().map(|point| point.into()).take(n_points)) | ||||
|             .submit()?; | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
| 	fn grid(&mut self) -> Vec<Point> { | ||||
| 		let dim_mid = 16000.0; | ||||
| 		let dim_max = 32000.0; | ||||
| 		let col_min = Color { r: 0, g: 0, b: 0 }; | ||||
| 		let col_max = Color { r: 255, g: 255, b: 255 }; | ||||
|     fn stop(&mut self) -> LJResult<()> { | ||||
|         self.stream | ||||
|             .queue_commands() | ||||
|             .stop() | ||||
|             .submit() | ||||
|             .expect("err occurred when submitting STOP command and listening for response"); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
| 		vec![ | ||||
| 			Point { x: -dim_max, y: dim_max, color: col_min }, | ||||
| 			Point { x: -dim_max, y: dim_max, color: col_max }, | ||||
| 			Point { x: dim_max, y: dim_max, color: col_max }, | ||||
| 			Point { x: dim_max, y: -dim_max, color: col_max }, | ||||
| 			Point { x: -dim_max, y: -dim_max, color: col_max }, | ||||
| 			Point { x: -dim_max, y: -dim_mid, color: col_min }, | ||||
| 			Point { x: -dim_mid, y: dim_mid, color: col_min }, | ||||
| 			Point { x: -dim_mid, y: dim_mid, color: col_max }, | ||||
| 			Point { x: dim_mid, y: dim_mid, color: col_max }, | ||||
| 			Point { x: dim_mid, y: -dim_mid, color: col_max }, | ||||
| 			Point { x: -dim_mid, y: -dim_mid, color: col_max }, | ||||
| 			Point { x: -dim_mid, y: -dim_mid, color: col_min }, | ||||
| 		] | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // An iterator that endlessly generates a sine wave of DAC points.
 | ||||
| //
 | ||||
| // The sine wave oscillates at a rate of once per second.
 | ||||
| struct SineWave { | ||||
| 	point: u32, | ||||
| 	points_per_frame: u16, | ||||
| 	frames_per_second: f32, | ||||
| } | ||||
| 
 | ||||
| impl Iterator for SineWave { | ||||
| 	type Item = ether_dream::protocol::DacPoint; | ||||
| 	fn next(&mut self) -> Option<Self::Item> { | ||||
| 		let coloured_points_per_frame = self.points_per_frame - 1; | ||||
| 		let i = (self.point % self.points_per_frame as u32) as u16; | ||||
| 		let hz = 1.0; | ||||
| 		let fract = i as f32 / coloured_points_per_frame as f32; | ||||
| 		let phase = (self.point as f32 / coloured_points_per_frame as f32) / self.frames_per_second; | ||||
| 		let amp = (hz * (fract + phase) * 2.0 * std::f32::consts::PI).sin(); | ||||
| 		let (r, g, b) = match i { | ||||
| 			i if i == coloured_points_per_frame || i < 13 => (0, 0, 0), | ||||
| 			_ => (u16::MAX, u16::MAX, u16::MAX), | ||||
| 		}; | ||||
| 		let x_min = i16::MIN; | ||||
| 		let x_max = i16::MAX; | ||||
| 		let x = (x_min as f32 + fract * (x_max as f32 - x_min as f32)) as i16; | ||||
| 		let y = (amp * x_max as f32) as i16; | ||||
| 		let control = 0; | ||||
| 		let (u1, u2) = (0, 0); | ||||
| 		let p = ether_dream::protocol::DacPoint { | ||||
| 			control, | ||||
| 			x, | ||||
| 			y, | ||||
| 			i, | ||||
| 			r, | ||||
| 			g, | ||||
| 			b, | ||||
| 			u1, | ||||
| 			u2, | ||||
| 		}; | ||||
| 		// debug!("{:?}",p);
 | ||||
| 		self.point += 1; | ||||
| 		Some(p) | ||||
| 	} | ||||
|     fn grid(&mut self) -> Vec<Point> { | ||||
|         vec![Point { | ||||
|             x: 0.0, | ||||
|             y: 0.0, | ||||
|             color: Color { | ||||
|                 r: 255, | ||||
|                 g: 255, | ||||
|                 b: 255, | ||||
|             }, | ||||
|         }] | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,108 +1,152 @@ | ||||
| use std::time::SystemTime; | ||||
| use crate::conf::HeliosConf; | ||||
| use crate::device::{Device, PlaybackState, Status}; | ||||
| use crate::errors::{LJError, LJResult}; | ||||
| use crate::point::{Color, Point}; | ||||
| use chrono::Utc; | ||||
| use helios_dac::{ | ||||
|     // Coordinate,
 | ||||
|     // Color,
 | ||||
|     DeviceStatus, | ||||
|     Frame, | ||||
|     // Point as HeliosPoint,
 | ||||
| }; | ||||
| ///
 | ||||
| /// Configure udev:
 | ||||
| /// https://github.com/Grix/helios_dac/blob/master/docs/udev_rules_for_linux.md
 | ||||
| ///
 | ||||
| use helios_dac::{NativeHeliosDac, NativeHeliosDacController}; | ||||
| use helios_dac::{ | ||||
| 	// Coordinate,
 | ||||
| 	// Color,
 | ||||
| 	DeviceStatus, | ||||
| 	Frame, | ||||
| 	// Point as HeliosPoint,
 | ||||
| }; | ||||
| use crate::conf::HeliosConf; | ||||
| use crate::device::{Device, Status, PlaybackState}; | ||||
| use crate::errors::{LJError, LJResult}; | ||||
| use crate::point::{Color, Point}; | ||||
| use chrono::{DateTime, Utc}; | ||||
| 
 | ||||
| pub struct HeliosDevice { | ||||
| 	pub conf: HeliosConf, | ||||
| 	dac: NativeHeliosDac, | ||||
| 	sent_points: u16, | ||||
| 	state: PlaybackState, | ||||
| 	lack: String, | ||||
| 	last_traced_at: String, | ||||
|     pub conf: HeliosConf, | ||||
|     dac: NativeHeliosDac, | ||||
|     sent_points: u16, | ||||
|     state: PlaybackState, | ||||
|     lack: String, | ||||
|     last_traced_at: String, | ||||
| } | ||||
| 
 | ||||
| impl HeliosDevice { | ||||
| 	pub fn new(conf: &HeliosConf) -> LJResult<Self> { | ||||
| 		let id = conf.id; | ||||
| 		let controller = NativeHeliosDacController::new()?; | ||||
| 		let devices = controller.list_devices()?; | ||||
| 		let Some(device) = devices.into_iter().nth(id as usize) else { | ||||
|     pub fn new(conf: &HeliosConf) -> LJResult<Self> { | ||||
|         let id = conf.id; | ||||
|         let controller = NativeHeliosDacController::new()?; | ||||
|         let devices = controller.list_devices()?; | ||||
|         let Some(device) = devices.into_iter().nth(id as usize) else { | ||||
| 			return Err(Box::new(LJError::HeliosDeviceMissing)); | ||||
| 		}; | ||||
| 		let dac = device.open()?; | ||||
| 		let now = SystemTime::now(); | ||||
| 		let now: DateTime<Utc> = now.into(); | ||||
| 		let last_traced_at = now.to_rfc3339(); | ||||
| 
 | ||||
| 		Ok(Self { | ||||
| 			conf: (*conf).clone(), | ||||
| 			dac, | ||||
| 			sent_points: 0, | ||||
| 			state: PlaybackState::PREPARE, | ||||
| 			lack: "".to_string(), | ||||
| 			last_traced_at, | ||||
| 		}) | ||||
| 	} | ||||
|         let dac = device.open()?; | ||||
|         Ok(Self { | ||||
|             conf: (*conf).clone(), | ||||
|             dac, | ||||
|             sent_points: 0, | ||||
|             state: PlaybackState::PREPARE, | ||||
|             lack: "".to_string(), | ||||
|             last_traced_at: "1985-04-12T23:20:50.52Z".to_string(), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Device for HeliosDevice { | ||||
| 	fn status(&mut self) -> Status { | ||||
| 		let lack = self.lack.clone(); | ||||
| 		Status { | ||||
| 			last_traced_at: self.last_traced_at.clone(), | ||||
| 			properties: vec!["foo".to_string()], | ||||
| 			playback_state: self.state, | ||||
| 			capacity: self.sent_points as usize, | ||||
| 			lack, | ||||
| 		} | ||||
| 	} | ||||
|     fn status(&mut self) -> Status { | ||||
|         let lack = self.lack.clone(); | ||||
|         Status { | ||||
|             last_traced_at: self.last_traced_at.clone(), | ||||
|             properties: vec!["foo".to_string()], | ||||
|             playback_state: self.state, | ||||
|             capacity: self.sent_points, | ||||
|             lack, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 	fn draw(&mut self, | ||||
| 	        line: Vec<Point>, | ||||
| 	        speed: u32, | ||||
| 	) -> LJResult<()> { | ||||
| 		self.state = PlaybackState::IDLE; | ||||
| 		while let Ok(DeviceStatus::NotReady) = self.dac.status() {} | ||||
| 		self.state = PlaybackState::PLAYING; | ||||
|     fn draw(&mut self, line: Vec<Point>, speed: u32) -> LJResult<()> { | ||||
|         self.state = PlaybackState::IDLE; | ||||
|         while let Ok(DeviceStatus::NotReady) = self.dac.status() {} | ||||
|         self.state = PlaybackState::PLAYING; | ||||
| 
 | ||||
| 		let points: Vec<helios_dac::Point> = line.into_iter().map(|p| p.into()).collect(); | ||||
| 		let frame = Frame::new(speed, points.clone()); | ||||
| 		self.dac.write_frame(frame.clone())?; | ||||
| 		self.sent_points = points.len() as u16; | ||||
| 		self.last_traced_at = Utc::now().to_rfc3339(); | ||||
| 		Ok(()) | ||||
| 	} | ||||
|         let points: Vec<helios_dac::Point> = line.into_iter().map(|p| p.into()).collect(); | ||||
|         let frame = Frame::new(speed, points.clone()); | ||||
|         self.dac.write_frame(frame.clone())?; | ||||
|         self.sent_points = points.len() as u16; | ||||
|         self.last_traced_at = Utc::now().to_rfc3339(); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
| 	fn stop(&mut self) -> LJResult<()> { | ||||
| 		self.dac.stop()?; | ||||
| 		Ok(()) | ||||
| 	} | ||||
|     fn stop(&mut self) -> LJResult<()> { | ||||
|         self.dac.stop()?; | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
| 	fn grid(&mut self) -> Vec<Point> { | ||||
| 		let dim_min = 0 as f32; | ||||
| 		let dim_mid = 2047.0; | ||||
| 		let dim_max = 4095.0; | ||||
| 		let col_min = Color { r: 0, g: 0, b: 0 }; | ||||
| 		let col_max = Color { r: 255, g: 255, b: 255 }; | ||||
|     fn grid(&mut self) -> Vec<Point> { | ||||
|         let dim_min = 0 as f32; | ||||
|         let dim_mid = 2047 as f32; | ||||
|         let dim_max = 4095 as f32; | ||||
|         let col_min = Color { r: 0, g: 0, b: 0 }; | ||||
|         let col_max = Color { | ||||
|             r: 255, | ||||
|             g: 255, | ||||
|             b: 255, | ||||
|         }; | ||||
| 
 | ||||
| 		vec![ | ||||
| 			Point { x: dim_min, y: dim_max, color: col_min }, | ||||
| 			Point { x: dim_min, y: dim_max, color: col_max }, | ||||
| 			Point { x: dim_max, y: dim_max, color: col_max }, | ||||
| 			Point { x: dim_max, y: dim_min, color: col_max }, | ||||
| 			Point { x: dim_min, y: dim_min, color: col_max }, | ||||
| 			Point { x: dim_min, y: dim_min, color: col_min }, | ||||
| 			Point { x: dim_min, y: dim_mid, color: col_min }, | ||||
| 			Point { x: dim_min, y: dim_mid, color: col_max }, | ||||
| 			Point { x: dim_mid, y: dim_mid, color: col_max }, | ||||
| 			Point { x: dim_mid, y: dim_min, color: col_max }, | ||||
| 			Point { x: dim_min, y: dim_min, color: col_max }, | ||||
| 			Point { x: dim_min, y: dim_min, color: col_min }, | ||||
| 		] | ||||
| 	} | ||||
|         vec![ | ||||
|             Point { | ||||
|                 x: dim_min, | ||||
|                 y: dim_max, | ||||
|                 color: col_min, | ||||
|             }, | ||||
|             Point { | ||||
|                 x: dim_min, | ||||
|                 y: dim_max, | ||||
|                 color: col_max, | ||||
|             }, | ||||
|             Point { | ||||
|                 x: dim_max, | ||||
|                 y: dim_max, | ||||
|                 color: col_max, | ||||
|             }, | ||||
|             Point { | ||||
|                 x: dim_max, | ||||
|                 y: dim_min, | ||||
|                 color: col_max, | ||||
|             }, | ||||
|             Point { | ||||
|                 x: dim_min, | ||||
|                 y: dim_min, | ||||
|                 color: col_max, | ||||
|             }, | ||||
|             Point { | ||||
|                 x: dim_min, | ||||
|                 y: dim_min, | ||||
|                 color: col_min, | ||||
|             }, | ||||
|             Point { | ||||
|                 x: dim_min, | ||||
|                 y: dim_mid, | ||||
|                 color: col_min, | ||||
|             }, | ||||
|             Point { | ||||
|                 x: dim_min, | ||||
|                 y: dim_mid, | ||||
|                 color: col_max, | ||||
|             }, | ||||
|             Point { | ||||
|                 x: dim_mid, | ||||
|                 y: dim_mid, | ||||
|                 color: col_max, | ||||
|             }, | ||||
|             Point { | ||||
|                 x: dim_mid, | ||||
|                 y: dim_min, | ||||
|                 color: col_max, | ||||
|             }, | ||||
|             Point { | ||||
|                 x: dim_min, | ||||
|                 y: dim_min, | ||||
|                 color: col_max, | ||||
|             }, | ||||
|             Point { | ||||
|                 x: dim_min, | ||||
|                 y: dim_min, | ||||
|                 color: col_min, | ||||
|             }, | ||||
|         ] | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,50 +1,50 @@ | ||||
| use config::ConfigError; | ||||
| use redis::RedisError; | ||||
| use std::error::Error; | ||||
| use std::{fmt, io}; | ||||
| use redis::RedisError; | ||||
| use config::ConfigError; | ||||
| 
 | ||||
| pub type LJResult<T> = Result<T, Box<dyn std::error::Error>>; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum LJError { | ||||
| 	Config(ConfigError), | ||||
| 	RedisConnect(RedisError), | ||||
| 	HeliosDeviceMissing, | ||||
| 	BadEDH, | ||||
| 	EtherdreamConnectError(io::Error), | ||||
|     Config(ConfigError), | ||||
|     RedisConnect(RedisError), | ||||
|     HeliosDeviceMissing, | ||||
|     BadEDH, | ||||
|     EtherdreamConnectError(io::Error), | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for LJError { | ||||
| 	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
| 		use LJError::*; | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         use LJError::*; | ||||
| 
 | ||||
| 		match self { | ||||
| 			Config(err) => { | ||||
| 				write!(f, "unable to load config file: {err}") | ||||
| 			} | ||||
| 			RedisConnect(err) => { | ||||
| 				write!(f, "unable to connect to redis server: {err}") | ||||
| 			} | ||||
| 			HeliosDeviceMissing => { | ||||
| 				write!(f, "helios device not found") | ||||
| 			} | ||||
| 			BadEDH => { | ||||
| 				write!(f, "EDH matrix is not a 3x3 matrix") | ||||
| 			} | ||||
| 			EtherdreamConnectError(err) => { | ||||
| 				write!(f, "Failed to retrieve Etherdream device: {err}") | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|         match self { | ||||
|             Config(err) => { | ||||
|                 write!(f, "unable to load config file: {err}") | ||||
|             } | ||||
|             RedisConnect(err) => { | ||||
|                 write!(f, "unable to connect to redis server: {err}") | ||||
|             } | ||||
|             HeliosDeviceMissing => { | ||||
|                 write!(f, "helios device not found") | ||||
|             } | ||||
|             BadEDH => { | ||||
|                 write!(f, "EDH matrix is not a 3x3 matrix") | ||||
|             } | ||||
|             EtherdreamConnectError(err) => { | ||||
|                 write!(f, "Failed to retrieve Etherdream device: {err}") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Error for LJError { | ||||
| 	fn source(&self) -> Option<&(dyn Error + 'static)> { | ||||
| 		use LJError::*; | ||||
|     fn source(&self) -> Option<&(dyn Error + 'static)> { | ||||
|         use LJError::*; | ||||
| 
 | ||||
| 		match self { | ||||
| 			RedisConnect(err) => Some(err), | ||||
| 			_ => None | ||||
| 		} | ||||
| 	} | ||||
|         match self { | ||||
|             RedisConnect(err) => Some(err), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,39 +0,0 @@ | ||||
| use log::{debug, warn}; | ||||
| use std::time::{Duration, Instant}; | ||||
| use crate::errors::LJResult; | ||||
| use std::{thread}; | ||||
| 
 | ||||
| /// Converts helios Geometry to Helios
 | ||||
| #[derive(Debug, Clone, Copy)] | ||||
| pub struct Framerate { | ||||
| 	prev_trace_time: Instant, | ||||
| 	fps: u8, | ||||
| } | ||||
| 
 | ||||
| impl Framerate { | ||||
| 	pub fn new() -> LJResult<Self> { | ||||
| 		Ok(Framerate { | ||||
| 			prev_trace_time: Instant::now(), | ||||
| 			fps: 20, | ||||
| 		}) | ||||
| 	} | ||||
| 	pub fn handle_time(&mut self) -> LJResult<()> { | ||||
| 		let frame_time = 1000000000 / self.fps as u128; | ||||
| 		let now = Instant::now(); | ||||
| 		// How long since last loop ?
 | ||||
| 		let nanotime_spent = self.prev_trace_time.elapsed().as_nanos(); | ||||
| 		// Diw it go too fast? If so : sleep a bit
 | ||||
| 		if frame_time > nanotime_spent { | ||||
| 			let nanotime_towait = frame_time - nanotime_spent; | ||||
| 			let dur = Duration::new(0, (nanotime_towait as f32 * 0.9) as u32); | ||||
| 			// debug!("{:?} - {:?} : {:?}", nanotime_towait, self.prev_trace_time, now );
 | ||||
| 			thread::sleep(dur); | ||||
| 			debug!("Framerate OK"); | ||||
| 		} else { | ||||
| 			warn!("Frame longer than expected {:?} > {:?}", nanotime_spent, frame_time, ); | ||||
| 		} | ||||
| 
 | ||||
| 		self.prev_trace_time = now; | ||||
| 		Ok(()) | ||||
| 	} | ||||
| } | ||||
| @ -1,8 +1,7 @@ | ||||
| pub mod redis_ctrl; | ||||
| pub mod conf; | ||||
| pub mod errors; | ||||
| pub mod device; | ||||
| pub mod errors; | ||||
| pub mod point; | ||||
| pub mod redis_ctrl; | ||||
| pub mod transformer; | ||||
| pub mod worldstate; | ||||
| pub mod framerate; | ||||
|  | ||||
							
								
								
									
										289
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										289
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -1,200 +1,179 @@ | ||||
| mod conf; | ||||
| mod device; | ||||
| mod errors; | ||||
| mod point; | ||||
| ///
 | ||||
| /// Configure udev:
 | ||||
| /// https://github.com/Grix/helios_dac/blob/master/docs/udev_rules_for_linux.md
 | ||||
| ///
 | ||||
| mod redis_ctrl; | ||||
| mod conf; | ||||
| mod errors; | ||||
| mod point; | ||||
| mod transformer; | ||||
| mod device; | ||||
| mod worldstate; | ||||
| mod framerate; | ||||
| 
 | ||||
| use conf::Conf; | ||||
| use device::device_factory; | ||||
| use env_logger::Builder; | ||||
| use errors::LJResult; | ||||
| use log::{/* warn, */ error, info, LevelFilter}; | ||||
| use point::{Color, Point}; | ||||
| use redis_ctrl::{Order, RedisCtrl}; | ||||
| use std::sync::atomic::{AtomicBool, Ordering}; | ||||
| use std::sync::Arc; | ||||
| use redis_ctrl::{RedisCtrl, Order}; | ||||
| use conf::Conf; | ||||
| use errors::LJResult; | ||||
| use point::{Point, Color}; | ||||
| use transformer::Transformers; | ||||
| use log::{LevelFilter, info, /* warn, */ error}; | ||||
| use env_logger::Builder; | ||||
| use worldstate::WorldState; | ||||
| use framerate::Framerate; | ||||
| 
 | ||||
| const DEFAULT_CONF_FILE: &str = "settings.toml"; | ||||
| 
 | ||||
| pub fn main() { | ||||
| 	match run_all() { | ||||
| 		Ok(()) => {} | ||||
| 		Err(err) => { | ||||
| 			error!("Error: {}", err); | ||||
| 		} | ||||
| 	} | ||||
|     match run_all() { | ||||
|         Ok(()) => {} | ||||
|         Err(err) => { | ||||
|             error!("Error: {}", err); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| fn run_all() -> LJResult<()> { | ||||
| 	// Setup handler for interrupt Signals
 | ||||
| 	let running = Arc::new(AtomicBool::new(true)); | ||||
| 	let r = running.clone(); | ||||
| 	ctrlc::set_handler(move || { | ||||
| 		r.store(false, Ordering::SeqCst); | ||||
| 	})?; | ||||
|     // Setup configuration file and set up logs
 | ||||
|     let filename = std::env::args() | ||||
|         .nth(1) | ||||
|         .unwrap_or_else(|| DEFAULT_CONF_FILE.to_string()); | ||||
| 
 | ||||
| 	// Setup configuration file and set up logs
 | ||||
| 	let filename = std::env::args().nth(1).unwrap_or_else(|| { | ||||
| 		DEFAULT_CONF_FILE.to_string() | ||||
| 	}); | ||||
|     let config = Conf::new(&filename); | ||||
|     init_logging(&config); | ||||
|     let config = config?; | ||||
|     info!("*** Starting up ***"); | ||||
| 
 | ||||
| 	let config = Conf::new(&filename); | ||||
| 	init_logging(&config); | ||||
| 	let config = config?; | ||||
| 	info!("*** Starting up ***"); | ||||
|     info!("{:?}", config); | ||||
| 
 | ||||
| 	info!("{:?}", config); | ||||
|     // Setup Redis Service
 | ||||
|     let mut rs = RedisCtrl::new(&config.redis_url, &config.laser_id)?; | ||||
| 
 | ||||
| 	// Setup Redis Service
 | ||||
| 	let mut rs = RedisCtrl::new(&config.redis_url, &config.laser_id)?; | ||||
|     let mut world_state = rs.init_world_state().unwrap(); | ||||
|     info!("WorldState: {:?}", world_state); | ||||
|     dbg!("worldstate.kpps: {}", world_state.kpps); | ||||
| 
 | ||||
| 	let mut world_state = rs.init_world_state().unwrap(); | ||||
| 	info!("WorldState: {:?}", world_state); | ||||
|     // Setup handler for interrupt Signals
 | ||||
|     let running = Arc::new(AtomicBool::new(true)); | ||||
|     let r = running.clone(); | ||||
|     ctrlc::set_handler(move || { | ||||
|         r.store(false, Ordering::SeqCst); | ||||
|     })?; | ||||
| 
 | ||||
|     // Setup Laser Device based on conf
 | ||||
|     let mut tracer = device_factory(&config)?; | ||||
|     world_state.grid = tracer.grid(); | ||||
| 
 | ||||
| 	// Setup Laser Device based on conf
 | ||||
| 	let mut tracer = device_factory(&config)?; | ||||
| 	world_state.grid = tracer.grid(); | ||||
|     // can't work, but we can add + Debug to Device to make it work...
 | ||||
|     //dbg!(tracer);
 | ||||
| 
 | ||||
| 	// Setup geometry transformers on points lists
 | ||||
| 	let transformers = config.get_transformers(); | ||||
|     // Setup geometry transformers on points lists
 | ||||
|     let transformers = config.get_transformers(); | ||||
| 
 | ||||
| 	// Setup framerate limiter
 | ||||
| 	let mut framerate_handler = Framerate::new()?; | ||||
|     // Dispatch based on redis requests
 | ||||
|     while running.load(Ordering::SeqCst) { | ||||
|         rs.set_status(tracer.status())?; | ||||
| 
 | ||||
| 	// Dispatch based on redis requests
 | ||||
| 	while running.load(Ordering::SeqCst) { | ||||
| 		rs.set_status(tracer.status())?; | ||||
| 		framerate_handler.handle_time()?; | ||||
| 		let order = rs.get_order(config.laser_id)?; | ||||
| 		match order { | ||||
| 			Order::Draw | Order::Black | Order::Grid => { | ||||
| 				// 0 : Draw Normal point list
 | ||||
| 				// 2 : Draw BLACK point list
 | ||||
| 				// 3 : Draw GRID point list
 | ||||
| 				world_state.draw_black = order == Order::Black; | ||||
| 				world_state.draw_grid = order == Order::Grid; | ||||
| 				let frame = get_next_frame( | ||||
| 					&config, | ||||
| 					&transformers, | ||||
| 					&mut rs, | ||||
| 					&world_state, | ||||
| 				)?; | ||||
| 				// For now, draw all the time
 | ||||
| 				tracer.draw(frame, world_state.kpps)?; | ||||
| 			} | ||||
| 			Order::Intensity => { | ||||
| 				// 6 : Max Intensity Change     = reread redis key /intensity
 | ||||
| 				world_state.intensity = rs.get_int("intensity")? | ||||
| 					.try_into()?; | ||||
| 			} | ||||
| 			Order::Edh => { | ||||
| 				// 1 : Get the new EDH          = reread redis key /EDH/lasernumber
 | ||||
| 				world_state.edh = rs.get_edh()?; | ||||
| 			} | ||||
| 			Order::Kpps => { | ||||
| 				// 7 : kpps change              = reread redis key /kpps
 | ||||
| 				world_state.kpps = rs.get_int("kpps")?; | ||||
| 			} | ||||
| 			Order::ClientKey => { | ||||
| 				world_state.client_key = rs.get_client_key()?; | ||||
| 			} | ||||
| 			Order::ColorBalance => { | ||||
| 				let (r, g, b) = rs.get_color_balance()?; | ||||
| 				world_state.color_balance = Color { r, g, b }; | ||||
| 			} | ||||
| 			Order::Resampler => { | ||||
| 				world_state.resampler = rs.get_resampler()?; | ||||
| 			} | ||||
| 			_ => { | ||||
| 				// 9 : poweroff LJ
 | ||||
| 				info!("Order: {:?}", order); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|         let order = rs.get_order(config.laser_id)?; | ||||
| 
 | ||||
| 	info!("Exiting, stoping device."); | ||||
| 	tracer.stop()?; | ||||
| 	Ok(()) | ||||
|         match order { | ||||
|             Order::Draw | Order::Black | Order::Grid => { | ||||
|                 // 0 : Draw Normal point list
 | ||||
|                 // 2 : Draw BLACK point list
 | ||||
|                 // 3 : Draw GRID point list
 | ||||
|                 world_state.draw_black = order == Order::Black; | ||||
|                 world_state.draw_grid = order == Order::Grid; | ||||
|                 let frame = get_next_frame(&config, &transformers, &mut rs, &world_state)?; | ||||
|                 // For now, draw all the time
 | ||||
|                 tracer.draw(frame, world_state.kpps)?; | ||||
|             } | ||||
|             Order::Intensity => { | ||||
|                 // 6 : Max Intensity Change     = reread redis key /intensity
 | ||||
|                 world_state.intensity = rs.get_int("intensity")?.try_into()?; | ||||
|             } | ||||
|             Order::Edh => { | ||||
|                 // 1 : Get the new EDH          = reread redis key /EDH/lasernumber
 | ||||
|                 world_state.edh = rs.get_edh()?; | ||||
|             } | ||||
|             Order::Kpps => { | ||||
|                 // 7 : kpps change              = reread redis key /kpps
 | ||||
|                 world_state.kpps = rs.get_int("kpps")?; | ||||
|                 //dbg!(world_state.kpps);
 | ||||
|             } | ||||
|             Order::ClientKey => { | ||||
|                 world_state.client_key = rs.get_client_key()?; | ||||
|             } | ||||
|             // Order::ColorBalance => {},
 | ||||
|             _ => { | ||||
|                 // 4 : Resampler Change (longs and shorts lsteps)
 | ||||
|                 // 5 : Client Key Change        = reread redis key /clientkey
 | ||||
|                 // 8 : color balance change     = reread redis keys /red /green /blue
 | ||||
|                 // 9 : poweroff LJ
 | ||||
|                 info!("Order: {:?}", order); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     info!("Exiting, stoping device."); | ||||
|     tracer.stop()?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn init_logging(config: &LJResult<Conf>) { | ||||
| 	if let Ok(ref config) = config { | ||||
| 		let level = if config.debug { | ||||
| 			LevelFilter::Debug | ||||
| 		} else { | ||||
| 			LevelFilter::Info | ||||
| 		}; | ||||
| 		let mut builder = Builder::from_default_env(); | ||||
| 		builder | ||||
| 			.filter(None, level) | ||||
| 			.init(); | ||||
| 		info!("Debug mode enabled from configuration file"); | ||||
| 		return; | ||||
| 	} | ||||
| 	info!("Logging level inherited from env"); | ||||
| 	env_logger::init(); | ||||
|     if let Ok(ref config) = config { | ||||
|         if config.debug { | ||||
|             let mut builder = Builder::from_default_env(); | ||||
|             builder.filter(None, LevelFilter::Info).init(); | ||||
|             info!("Debug mode enabled from configuration file"); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     info!("Logging level inherited from env"); | ||||
|     env_logger::init(); | ||||
| } | ||||
| 
 | ||||
| fn get_next_frame( | ||||
| 	config: &Conf, | ||||
| 	transformers: &[Box<dyn Transformers>], | ||||
| 	rs: &mut RedisCtrl, | ||||
| 	world_state: &WorldState, | ||||
|     config: &Conf, | ||||
|     transformers: &[Box<dyn Transformers>], | ||||
|     rs: &mut RedisCtrl, | ||||
|     world_state: &WorldState, | ||||
| ) -> LJResult<Vec<Point>> { | ||||
| 	let format_key = format!("{}{}", | ||||
| 	                         world_state.client_key, | ||||
| 	                         config.laser_id); | ||||
|     let format_key = format!("{}{}", world_state.client_key, config.laser_id); | ||||
| 
 | ||||
| 	// Handle the grid case
 | ||||
|     // Handle the grid case
 | ||||
| 
 | ||||
| 	let mut line: Vec<Point> = if world_state.draw_grid { | ||||
| 		world_state.grid.clone() | ||||
| 	} else { | ||||
| 		let redis_line = rs.get_line(&format_key)?; | ||||
| 		redis_line.into_iter() | ||||
| 			.map(|tpl| tpl.into()) | ||||
| 			.collect() | ||||
| 	}; | ||||
|     let mut line: Vec<Point> = if world_state.draw_grid { | ||||
|         world_state.grid.clone() | ||||
|     } else { | ||||
|         let redis_line = rs.get_line(&format_key)?; | ||||
|         redis_line.into_iter().map(|tpl| tpl.into()).collect() | ||||
|     }; | ||||
| 
 | ||||
| 	for transformer in transformers { | ||||
| 		line = transformer.apply(&line, world_state); | ||||
| 	} | ||||
|     for transformer in transformers { | ||||
|         line = transformer.apply(&line, world_state); | ||||
|     } | ||||
| 
 | ||||
| 	// info!("Draw Black -> {}", world_state.draw_black);
 | ||||
| 	// info!("Draw Grid -> {}", world_state.draw_grid);
 | ||||
| 
 | ||||
| 	// LIMITER and BLACK
 | ||||
| 	line = line.into_iter() | ||||
| 		.map(|p| { | ||||
| 			let color = if world_state.draw_black { | ||||
| 				Color { r: 0, g: 0, b: 0 } | ||||
| 			} else { | ||||
| 				Color { | ||||
| 					r: p.color.r.min(world_state.intensity), | ||||
| 					g: p.color.g.min(world_state.intensity), | ||||
| 					b: p.color.b.min(world_state.intensity), | ||||
| 				} | ||||
| 			}; | ||||
| 			Point { | ||||
| 				color, | ||||
| 				..p | ||||
| 			} | ||||
| 		}) | ||||
| 		.collect(); | ||||
|     info!("Draw Black -> {}", world_state.draw_black); | ||||
|     info!("Draw Grid -> {}", world_state.draw_grid); | ||||
| 
 | ||||
| 	//info!("Line: {:?}", line);
 | ||||
| 	Ok(line) | ||||
|     // LIMITER and BLACK
 | ||||
|     line = line | ||||
|         .into_iter() | ||||
|         .map(|p| { | ||||
|             let color = if world_state.draw_black { | ||||
|                 Color { r: 0, g: 0, b: 0 } | ||||
|             } else { | ||||
|                 Color { | ||||
|                     r: p.color.r.min(world_state.intensity), | ||||
|                     g: p.color.g.min(world_state.intensity), | ||||
|                     b: p.color.b.min(world_state.intensity), | ||||
|                 } | ||||
|             }; | ||||
|             Point { color, ..p } | ||||
|         }) | ||||
|         .collect(); | ||||
| 
 | ||||
|     //info!("Line: {:?}", line);
 | ||||
|     Ok(line) | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										125
									
								
								src/point.rs
									
									
									
									
									
								
							
							
						
						
									
										125
									
								
								src/point.rs
									
									
									
									
									
								
							| @ -1,81 +1,92 @@ | ||||
| use ether_dream::protocol::DacPoint; | ||||
| use std::ops::Mul; | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug, Clone, Copy, Default, PartialEq)] | ||||
| pub struct Point { | ||||
| 	pub x: f32, | ||||
| 	pub y: f32, | ||||
| 	pub color: Color, | ||||
|     pub x: f32, | ||||
|     pub y: f32, | ||||
|     pub color: Color, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, Copy, Default, PartialEq)] | ||||
| pub struct Color { | ||||
| 	pub r: u8, | ||||
| 	pub g: u8, | ||||
| 	pub b: u8, | ||||
|     pub r: u8, | ||||
|     pub g: u8, | ||||
|     pub b: u8, | ||||
| } | ||||
| impl Mul<u8> for Color { | ||||
|     type Output = Self; | ||||
| 
 | ||||
|     fn mul(self, rhs: u8) -> Self { | ||||
| 	    Self{ | ||||
| 		    r: (255 * self.r as u16 / rhs as u16) as u8, | ||||
| 		    g: (255 * self.g as u16 / rhs as u16) as u8, | ||||
| 		    b: (255 * self.b as u16 / rhs as u16) as u8, | ||||
| 	    } | ||||
| impl Point { | ||||
|     pub fn diff(p1: &Self, p2: &Self) -> Self { | ||||
|         Point { | ||||
|             x: p1.x - p2.x, | ||||
|             y: p1.y - p2.y, | ||||
|             color: Color { r: 0, g: 0, b: 0 }, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn normalize(&mut self) -> Self { | ||||
|         let length = (self.x * self.x + self.y * self.y).sqrt(); | ||||
|         self.x = self.x / length; | ||||
|         self.y = self.y / length; | ||||
|         *self | ||||
|     } | ||||
| 
 | ||||
|     pub fn cross(&self, pt: &Self) -> f32 { | ||||
|         self.x * pt.x + self.y * pt.y | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // normalaize
 | ||||
| // diff
 | ||||
| 
 | ||||
| impl From<Color> for u32 { | ||||
| 	fn from(value: Color) -> Self { | ||||
| 		let r = value.r as u32; | ||||
| 		let g = value.g as u32; | ||||
| 		let b = (value.b) as u32; | ||||
| 		(r << 16) + (g << 8) + b | ||||
| 	} | ||||
|     fn from(value: Color) -> Self { | ||||
|         let r = value.r as u32; | ||||
|         let g = value.g as u32; | ||||
|         let b = (value.b) as u32; | ||||
|         (r << 16) + (g << 8) + b | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<(f32, f32, u32)> for Point { | ||||
| 	fn from((x, y, color): (f32, f32, u32)) -> Point { | ||||
| 		let r = (color >> 16) as u8; | ||||
| 		let g = ((color >> 8) & 255) as u8; | ||||
| 		let b = (color & 255) as u8; | ||||
| 		Point { x, y, color: Color { r, g, b } } | ||||
| 	} | ||||
|     fn from((x, y, color): (f32, f32, u32)) -> Point { | ||||
|         let r = (color >> 16) as u8; | ||||
|         let g = ((color >> 8) & 255) as u8; | ||||
|         let b = (color & 255) as u8; | ||||
|         Point { | ||||
|             x, | ||||
|             y, | ||||
|             color: Color { r, g, b }, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Point> for helios_dac::Point { | ||||
| 	fn from(pt: Point) -> helios_dac::Point { | ||||
| 		let x = pt.x.clamp(0.0, 4095.0) as u16; | ||||
| 		let y = pt.y.clamp(0.0, 4095.0) as u16; | ||||
| 		helios_dac::Point { | ||||
| 			coordinate: (x, y).into(), | ||||
| 			color: helios_dac::Color::new(pt.color.r, pt.color.g, pt.color.b), | ||||
| 			intensity: 0xFF, | ||||
| 		} | ||||
| 	} | ||||
|     fn from(pt: Point) -> helios_dac::Point { | ||||
|         let x = pt.x.clamp(0.0, 4095.0) as u16; | ||||
|         let y = pt.y.clamp(0.0, 4095.0) as u16; | ||||
|         helios_dac::Point { | ||||
|             coordinate: (x, y).into(), | ||||
|             color: helios_dac::Color::new(pt.color.r, pt.color.g, pt.color.b), | ||||
|             intensity: 0xFF, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Point> for DacPoint { | ||||
| 	fn from(pt: Point) -> DacPoint { | ||||
| 		let control = 0; | ||||
| 		let (u1, u2) = (0, 0); | ||||
| 		let i = 0; | ||||
| 		let x = pt.x.clamp(-32000.0, 32000.0); | ||||
| 		let y = pt.y.clamp(-32000.0, 32000.0); | ||||
| 		let pt = DacPoint { | ||||
| 			control, | ||||
| 			x: x as i16, | ||||
| 			y: y as i16, | ||||
| 			i, | ||||
| 			r: (pt.color.r as u16) * 255, | ||||
| 			g: (pt.color.g as u16) * 255, | ||||
| 			b: (pt.color.b as u16) * 255, | ||||
| 			u1, | ||||
| 			u2, | ||||
| 		}; | ||||
| 		// debug!("point {:?}", pt);
 | ||||
| 		pt | ||||
| 	} | ||||
|     fn from(pt: Point) -> DacPoint { | ||||
|         let control = 0; | ||||
|         let (u1, u2) = (0, 0); | ||||
|         let i = 255; | ||||
|         DacPoint { | ||||
|             control, | ||||
|             x: pt.x as i16, | ||||
|             y: pt.y as i16, | ||||
|             i, | ||||
|             r: pt.color.r.into(), | ||||
|             g: pt.color.g.into(), | ||||
|             b: pt.color.b.into(), | ||||
|             u1, | ||||
|             u2, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,143 +1,134 @@ | ||||
| use redis::{Client, Commands, Connection}; | ||||
| use ron::de::from_str; | ||||
| use crate::device::Status; | ||||
| use crate::errors::{LJError, LJResult}; | ||||
| use crate::worldstate::{WorldState, EDH}; | ||||
| use redis::{Client, Commands, Connection}; | ||||
| use ron::de::from_str; | ||||
| // use log::info;
 | ||||
| 
 | ||||
| #[repr(u8)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum Order { | ||||
| 	Draw = 0, | ||||
| 	Edh, | ||||
| 	Black, | ||||
| 	Grid, | ||||
| 	Resampler, | ||||
| 	ClientKey, | ||||
| 	Intensity, | ||||
| 	Kpps, | ||||
| 	ColorBalance, | ||||
| 	PowerOff | ||||
|     Draw = 0, | ||||
|     Edh, | ||||
|     //homography
 | ||||
|     Black, | ||||
|     Grid, | ||||
|     Resampler, | ||||
|     ClientKey, | ||||
|     Intensity, | ||||
|     Kpps, | ||||
|     ColorBalance, | ||||
| } | ||||
| 
 | ||||
| impl TryFrom<u8> for Order { | ||||
| 	type Error = String; | ||||
|     type Error = String; | ||||
| 
 | ||||
| 	fn try_from(value: u8) -> Result<Self, Self::Error> { | ||||
| 		use Order::*; | ||||
|     fn try_from(value: u8) -> Result<Self, Self::Error> { | ||||
|         use Order::*; | ||||
| 
 | ||||
| 		if value > 8 { | ||||
| 			return Err("order out of range".to_string()); | ||||
| 		} | ||||
|         if value > 8 { | ||||
|             return Err("order out of range".to_string()); | ||||
|         } | ||||
| 
 | ||||
| 		Ok(match value { | ||||
| 			0 => Draw, | ||||
| 			1 => Edh, | ||||
| 			2 => Black, | ||||
| 			3 => Grid, | ||||
| 			4 => Resampler, | ||||
| 			5 => ClientKey, | ||||
| 			6 => Intensity, | ||||
| 			7 => Kpps, | ||||
| 			8 => ColorBalance, | ||||
| 			9 => PowerOff, | ||||
| 			_ => unreachable!() | ||||
| 		}) | ||||
| 	} | ||||
|         Ok(match value { | ||||
|             0 => Draw, | ||||
|             1 => Edh, | ||||
|             2 => Black, | ||||
|             3 => Grid, | ||||
|             4 => Resampler, | ||||
|             5 => ClientKey, | ||||
|             6 => Intensity, | ||||
|             7 => Kpps, | ||||
|             8 => ColorBalance, | ||||
|             _ => unreachable!(), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub type Line = Vec<(f32, f32, u32)>; | ||||
| pub type Resampler = Vec<Vec<(f32,f32)>>; | ||||
| 
 | ||||
| pub struct RedisCtrl { | ||||
| 	pub client: Client, | ||||
| 	pub connection: Connection, | ||||
| 	laser_id: u8, | ||||
|     pub client: Client, | ||||
|     pub connection: Connection, | ||||
|     laser_id: u8, | ||||
| } | ||||
| 
 | ||||
| impl RedisCtrl { | ||||
| 	pub fn new(url: &str, laser_id: &u8) -> LJResult<Self> { | ||||
| 		let client = Client::open(url) | ||||
| 			.map_err(LJError::RedisConnect)?; | ||||
| 		let connection = client.get_connection() | ||||
| 			.map_err(LJError::RedisConnect)?; | ||||
| 		Ok(RedisCtrl { client, connection, laser_id: *laser_id }) | ||||
| 	} | ||||
|     pub fn new(url: &str, laser_id: &u8) -> LJResult<Self> { | ||||
|         let client = Client::open(url).map_err(LJError::RedisConnect)?; | ||||
|         let connection = client.get_connection().map_err(LJError::RedisConnect)?; | ||||
|         Ok(RedisCtrl { | ||||
|             client, | ||||
|             connection, | ||||
|             laser_id: *laser_id, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
| 	pub fn get_line(&mut self, key: &str) -> LJResult<Line> { | ||||
| 		let val: String = self.connection.get(key)?; | ||||
| 		let line: Line = from_str(&val)?; | ||||
| 		Ok(line) | ||||
| 	} | ||||
|     pub fn get_line(&mut self, key: &str) -> LJResult<Line> { | ||||
|         let val: String = self.connection.get(key)?; | ||||
|         let line: Line = from_str(&val)?; | ||||
|         Ok(line) | ||||
|     } | ||||
| 
 | ||||
| 	pub fn set(&mut self, key: String, value: String) -> LJResult<()> { | ||||
| 		self.connection.set(key, value)?; | ||||
| 		Ok(()) | ||||
| 	} | ||||
|     pub fn set(&mut self, key: String, value: String) -> LJResult<()> { | ||||
|         self.connection.set(key, value)?; | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
| 	pub fn get_order(&mut self, id: u8) -> LJResult<Order> { | ||||
| 		let path = format!("/order/{id}"); | ||||
| 		let val: u8 = self.connection.get(path.clone())?; | ||||
|     pub fn get_order(&mut self, id: u8) -> LJResult<Order> { | ||||
|         let path = format!("/order/{id}"); | ||||
|         let val: u8 = self.connection.get(path.clone())?; | ||||
| 
 | ||||
| 		if val == 1 || val >= 4 { | ||||
| 			self.connection.set(path, 0)?; | ||||
| 		} | ||||
|         if val == 1 || val >= 4 { | ||||
|             self.connection.set(path, 0)?; | ||||
|         } | ||||
| 
 | ||||
| 		Ok(val.try_into()?) | ||||
| 	} | ||||
|         Ok(val.try_into()?) | ||||
|     } | ||||
| 
 | ||||
| 	pub fn set_status(&mut self, status: Status) -> LJResult<()> { | ||||
| 		let lstt_key = format!("/lstt/{}", self.laser_id); | ||||
| 		let cap_key = format!("/cap/{}", self.laser_id); | ||||
| 		let lack_key = format!("/lack/{}", self.laser_id); | ||||
| 		self.set(lstt_key, status.playback_state.to_string())?; | ||||
| 		self.set(cap_key, status.capacity.to_string())?; | ||||
| 		self.set(lack_key, status.lack.to_string())?; | ||||
| 		Ok(()) | ||||
| 	} | ||||
|     /**
 | ||||
|     /lstt/lasernumber       etherdream last_status.playback_state  (0: idle   1: prepare   2: playing) | ||||
|     /cap/lasernumber        number of empty points sent to fill etherdream buffer (up to 1799) | ||||
|     /lack/lasernumber       "a": ACK   "F": Full  "I": invalid. 64 or 35 for no connection. | ||||
|      **/ | ||||
|     pub fn set_status(&mut self, status: Status) -> LJResult<()> { | ||||
|         let lstt_key = format!("/lstt/{}", self.laser_id); | ||||
|         let cap_key = format!("/cap/{}", self.laser_id); | ||||
|         let lack_key = format!("/lack/{}", self.laser_id); | ||||
|         self.set(lstt_key, status.playback_state.to_string())?; | ||||
|         self.set(cap_key, status.capacity.to_string())?; | ||||
|         self.set(lack_key, status.lack.to_string())?; | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
| 	pub fn init_world_state(&mut self) -> LJResult<WorldState> { | ||||
| 		Ok(WorldState { | ||||
| 			client_key: self.get_client_key()?, | ||||
| 			edh: self.get_edh()?, | ||||
| 			kpps: self.get_int("kpps")?.try_into()?, | ||||
| 			intensity: self.get_int("intensity")?.try_into()?, | ||||
| 			..WorldState::default() | ||||
| 		}) | ||||
| 	} | ||||
|     pub fn init_world_state(&mut self) -> LJResult<WorldState> { | ||||
|         Ok(WorldState { | ||||
|             client_key: self.get_client_key().unwrap(), | ||||
|             edh: self.get_edh().unwrap(), | ||||
|             kpps: self.get_int("kpps").unwrap().try_into().unwrap(), | ||||
|             intensity: self.get_int("intensity").unwrap().try_into().unwrap(), | ||||
|             ..WorldState::default() | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
| 	pub fn get_edh(&mut self) -> LJResult<EDH> { | ||||
| 		// Get new EDH
 | ||||
| 		let edh_key = format!("/EDH/{}", self.laser_id); | ||||
| 		let edh: String = self.connection.get(edh_key)?; | ||||
| 		let edh: Vec<Vec<f32>> = from_str(&edh)?; | ||||
| 		let edh = EDH::new(edh)?; | ||||
| 		Ok(edh) | ||||
| 	} | ||||
|     pub fn get_edh(&mut self) -> LJResult<EDH> { | ||||
|         // Get new EDH
 | ||||
|         let edh_key = format!("/EDH/{}", self.laser_id); | ||||
|         let edh: String = self.connection.get(edh_key)?; | ||||
|         let edh: Vec<Vec<f32>> = from_str(&edh)?; | ||||
|         let edh = EDH::new(edh)?; | ||||
|         Ok(edh) | ||||
|     } | ||||
| 
 | ||||
| 	pub fn get_client_key(&mut self) -> LJResult<String> { | ||||
| 		let key: String = self.connection.get("/clientkey")?; | ||||
| 		Ok(key) | ||||
| 	} | ||||
|     pub fn get_client_key(&mut self) -> LJResult<String> { | ||||
|         let key: String = self.connection.get("/clientkey")?; | ||||
|         Ok(key) | ||||
|     } | ||||
| 
 | ||||
| 	pub fn get_color_balance(&mut self) -> LJResult<(u8, u8, u8)> { | ||||
| 		Ok(( | ||||
| 			self.connection.get("/red")?, | ||||
| 			self.connection.get("/green")?, | ||||
| 			self.connection.get("/blue")?, | ||||
| 		)) | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn get_resampler(&mut self ) -> LJResult<Resampler> { | ||||
| 		let val: String = self.connection.get(format!("/resampler/{}", self.laser_id))?; | ||||
| 		let resampler : Resampler = from_str(&val)?; | ||||
| 		Ok(resampler) | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn get_int(&mut self, key: &str) -> LJResult<u32> { | ||||
| 		// Get new Int
 | ||||
| 		let fmt = format!("/{key}/{}", self.laser_id); | ||||
| 		let val: u32 = self.connection.get(fmt)?; | ||||
| 		Ok(val) | ||||
| 	} | ||||
|     pub fn get_int(&mut self, key: &str) -> LJResult<u32> { | ||||
|         // Get new Int
 | ||||
|         let fmt = format!("/{key}/{}", self.laser_id); | ||||
|         let val: u32 = self.connection.get(fmt)?; | ||||
|         Ok(val) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| //pub mod common;
 | ||||
| mod angle_optimisation; | ||||
| mod flip_horizontal; | ||||
| mod flip_vertical; | ||||
| mod grid; | ||||
| mod helios_to_etherdream; | ||||
| mod homography; | ||||
| mod intensity; | ||||
| mod replicate; | ||||
| mod rotate; | ||||
| mod translate; | ||||
| @ -12,20 +12,15 @@ use crate::point::Point; | ||||
| use crate::worldstate::WorldState; | ||||
| 
 | ||||
| // re-export transformers to be abe to use it directly from transformer::
 | ||||
| pub use translate::Translate; | ||||
| pub use replicate::Replicate; | ||||
| pub use rotate::Rotate; | ||||
| pub use self::homography::Homography; | ||||
| pub use angle_optimisation::AngleOptimisation; | ||||
| pub use flip_horizontal::FlipHorizontal; | ||||
| pub use flip_vertical::FlipVertical; | ||||
| pub use grid::Grid; | ||||
| pub use self::homography::Homography; | ||||
| pub use helios_to_etherdream::HeliosToEtherdream; | ||||
| pub use intensity::Intensity; | ||||
| pub use replicate::Replicate; | ||||
| pub use rotate::Rotate; | ||||
| pub use translate::Translate; | ||||
| 
 | ||||
| pub trait Transformers { | ||||
|     fn apply( | ||||
| 	&self, | ||||
| 	point_list: &[Point], | ||||
| 	world_state: &WorldState | ||||
|     ) -> Vec<Point>; | ||||
|     fn apply(&self, point_list: &[Point], world_state: &WorldState) -> Vec<Point>; | ||||
| } | ||||
|  | ||||
							
								
								
									
										129
									
								
								src/transformer/angle_optimisation.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								src/transformer/angle_optimisation.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,129 @@ | ||||
| use crate::point::{Color, Point}; | ||||
| use crate::transformer::Transformers; | ||||
| use crate::worldstate::WorldState; | ||||
| 
 | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use std::f64::consts::PI; | ||||
| //use std::cmp::min;
 | ||||
| 
 | ||||
| /// Angle Optimisation
 | ||||
| 
 | ||||
| #[derive(Serialize, Deserialize, Debug, Clone, Copy)] | ||||
| pub struct AngleOptimisation { | ||||
|     coef: f64, | ||||
|     //pps: u16,
 | ||||
| } | ||||
| 
 | ||||
| fn color_not_zero(p1: &Point, p2: &Point, p3: &Point) -> bool { | ||||
|     let c0 = Color { r: 0, g: 0, b: 0 }; | ||||
|     if p1.color == c0 || p2.color == c0 || p3.color == c0 { | ||||
|         false | ||||
|     } else { | ||||
|         true | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn pos_different(p1: &Point, p2: &Point) -> bool { | ||||
|     if p1.x == p2.x && p1.y == p2.y { | ||||
|         false | ||||
|     } else { | ||||
|         true | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  * Le temps a attendre a un angle est toujours le meme, peu importe les kpps. | ||||
|  * Donc le nombre de point a rajouter est proportionelle a la vitesse du laser. | ||||
|  * | ||||
|  *  kpps / | ||||
|  * */ | ||||
| 
 | ||||
| fn color_same(p1: &Point, p2: &Point) -> bool { | ||||
|     if p1.color.r == p2.color.r && p1.color.g == p2.color.g && p1.color.b == p2.color.b { | ||||
|         true | ||||
|     } else { | ||||
|         false | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // quand on rajoute un point, on le rejoute ou et avec quelle couleur
 | ||||
| //  -> j'ai l'impression qu'on le rajoute surtout a l'arriver
 | ||||
| 
 | ||||
| fn get_prev(pl: &[Point], id: usize) -> (Option<&Point>, usize) { | ||||
|     for i in (0..id).rev() { | ||||
|         if pos_different(&pl[id], &pl[i]) { | ||||
|             return (Some(&pl[i]), id - i); | ||||
|         } | ||||
|     } | ||||
|     (None, id - 0) | ||||
| } | ||||
| 
 | ||||
| fn get_next(pl: &[Point], id: usize) -> (Option<&Point>, usize) { | ||||
|     for i in id..(pl.len() - 1) { | ||||
|         if pos_different(&pl[id], &pl[i]) { | ||||
|             return (Some(&pl[i]), i - id); | ||||
|         } | ||||
|     } | ||||
|     (None, pl.len() - id - 1) | ||||
| } | ||||
| 
 | ||||
| fn max(v1: f64, v2: f64) -> f64 { | ||||
|     if v1 > v2 { | ||||
|         v1 | ||||
|     } else { | ||||
|         v2 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Transformers for AngleOptimisation { | ||||
|     fn apply(&self, pl: &[Point], _ws: &WorldState) -> Vec<Point> { | ||||
|         let c0 = Color { r: 0, g: 0, b: 0 }; | ||||
|         let mut v = vec![]; | ||||
| 
 | ||||
|         let to_add_max = _ws.kpps as f64 / self.coef; // for 180 deg
 | ||||
|         let to_add_min = max(to_add_max / 1.5, 2.); // for 180 deg
 | ||||
| 
 | ||||
|         let mut first_blanc = pl[0].clone(); | ||||
|         first_blanc.color = Color{r:0, g:0, b:0}; | ||||
|         for _ in 0..(to_add_max as u32) { | ||||
|             v.push(first_blanc); // push first
 | ||||
|         } | ||||
|         v.push(pl[0]); | ||||
|         for i in 1..(pl.len() - 1) { | ||||
|             let node = &pl[i]; | ||||
|             if let ((Some(prev), dist_prev), (Some(next), dist_next)) = | ||||
|                 (get_prev(pl, i), get_next(pl, i)) | ||||
|             { | ||||
|                 let d1 = Point::diff(node, prev).normalize(); | ||||
|                 let d2 = Point::diff(next, node).normalize(); | ||||
|                 let angle = (d1.cross(&d2) as f64).acos(); | ||||
|                 let to_add = ((to_add_max - to_add_min) * (angle / PI) + to_add_min) as usize; | ||||
|                 println!("\nangle: {}", (angle / PI * 180.)); | ||||
|                 dbg!(to_add); | ||||
|                 dbg!(to_add_max); | ||||
|                 for _ in 0..to_add { | ||||
|                     v.push(*node); | ||||
|                 } | ||||
|             } else { | ||||
|                 v.push(*node); | ||||
|             }; | ||||
|             //v.push(*node); // push node
 | ||||
|         } | ||||
|         for _ in 0..(to_add_max as u32) { | ||||
|             v.push(pl[pl.len() - 2]); // push last
 | ||||
|         } | ||||
|         for _ in 0..(to_add_max as u32) { | ||||
|             v.push(pl[pl.len() - 1]); // push last
 | ||||
|         } | ||||
|         println!("\tbefore: {}\tafter: {}", pl.len(), v.len()); | ||||
| 
 | ||||
|         v | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  *  |\ | ||||
|  *  | \ | ||||
|  *  |  \ | ||||
|  *  |  | | ||||
|  * */ | ||||
| @ -1,24 +1,27 @@ | ||||
| use crate::transformer::Transformers; | ||||
| use crate::point::Point; | ||||
| use crate::transformer::Transformers; | ||||
| use crate::worldstate::WorldState; | ||||
| 
 | ||||
| use serde::{Serialize,Deserialize}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| /// Flip Horizontal
 | ||||
| 
 | ||||
| #[derive(Serialize,Deserialize,Debug,Clone,Copy)] | ||||
| #[derive(Serialize, Deserialize, Debug, Clone, Copy)] | ||||
| pub struct FlipHorizontal { | ||||
|     x: f32, | ||||
| } | ||||
| 
 | ||||
| impl Transformers for FlipHorizontal { | ||||
|     fn apply(&self, point_list: &[Point], _ws: &WorldState) -> Vec<Point> { | ||||
|     point_list.iter() | ||||
|         .map(| pt | { | ||||
|             let dx = pt.x - self.x; | ||||
|             Point { x: dx - 2. * dx, | ||||
|                 ..*pt | ||||
|             } | ||||
|         }).collect() | ||||
|         point_list | ||||
|             .iter() | ||||
|             .map(|pt| { | ||||
|                 let dx = pt.x - self.x; | ||||
|                 Point { | ||||
|                     x: dx - 2. * dx, | ||||
|                     ..*pt | ||||
|                 } | ||||
|             }) | ||||
|             .collect() | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,24 +1,27 @@ | ||||
| use crate::transformer::Transformers; | ||||
| use crate::point::Point; | ||||
| use crate::transformer::Transformers; | ||||
| use crate::worldstate::WorldState; | ||||
| 
 | ||||
| use serde::{Serialize,Deserialize}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| /// Flip Vertical
 | ||||
| 
 | ||||
| #[derive(Serialize,Deserialize,Debug,Clone,Copy)] | ||||
| #[derive(Serialize, Deserialize, Debug, Clone, Copy)] | ||||
| pub struct FlipVertical { | ||||
|     y: f32, | ||||
| } | ||||
| 
 | ||||
| impl Transformers for FlipVertical { | ||||
|     fn apply(&self, point_list: &[Point], _ws: &WorldState) -> Vec<Point> { | ||||
|     point_list.iter() | ||||
|         .map(| pt | { | ||||
|             let dy = pt.y - self.y; | ||||
|             Point { y: dy - 2. * dy, | ||||
|                 ..*pt | ||||
|             } | ||||
|         }).collect() | ||||
|         point_list | ||||
|             .iter() | ||||
|             .map(|pt| { | ||||
|                 let dy = pt.y - self.y; | ||||
|                 Point { | ||||
|                     y: dy - 2. * dy, | ||||
|                     ..*pt | ||||
|                 } | ||||
|             }) | ||||
|             .collect() | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,58 +1,48 @@ | ||||
| use crate::transformer::Transformers; | ||||
| use crate::point::Point; | ||||
| use crate::transformer::Transformers; | ||||
| use crate::worldstate::WorldState; | ||||
| 
 | ||||
| use serde::{Serialize,Deserialize}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| /// Translate
 | ||||
| 
 | ||||
| #[derive(Serialize,Deserialize,Debug,Clone,Copy)] | ||||
| #[derive(Serialize, Deserialize, Debug, Clone, Copy)] | ||||
| pub struct Grid { | ||||
|     width: f32, | ||||
|     height: f32 | ||||
|     height: f32, | ||||
| } | ||||
| 
 | ||||
| fn square_box(size: f32, color: u32) -> Vec<(f32, f32, u32)> { | ||||
|     vec![ | ||||
| 	(-size, -size, 0), | ||||
| 	(-size, -size, color), | ||||
| 
 | ||||
| 	(0.0, -size, color), | ||||
| 	(0.0, -size, color), | ||||
| 	
 | ||||
| 	(size, -size, color), | ||||
| 	(size, -size, color), | ||||
| 
 | ||||
| 	(size, 0.0, color), | ||||
| 	(size, 0.0, color), | ||||
| 
 | ||||
| 	(size, size,  color), | ||||
| 	(size, size,  color), | ||||
| 
 | ||||
| 	(0.0, size,  color), | ||||
| 	(0.0, size,  color), | ||||
| 
 | ||||
| 	(-size, size,  color), | ||||
| 	(-size, size,  color), | ||||
| 
 | ||||
| 	(-size, 0.0,  color), | ||||
| 	(-size, 0.0,  color),	
 | ||||
| 
 | ||||
| 	(-size, -size,  color), | ||||
| 	(-size, -size,  color), | ||||
| 
 | ||||
| 	(-size, -size, 0) | ||||
|         (-size, -size, 0), | ||||
|         (-size, -size, color), | ||||
|         (0.0, -size, color), | ||||
|         (0.0, -size, color), | ||||
|         (size, -size, color), | ||||
|         (size, -size, color), | ||||
|         (size, 0.0, color), | ||||
|         (size, 0.0, color), | ||||
|         (size, size, color), | ||||
|         (size, size, color), | ||||
|         (0.0, size, color), | ||||
|         (0.0, size, color), | ||||
|         (-size, size, color), | ||||
|         (-size, size, color), | ||||
|         (-size, 0.0, color), | ||||
|         (-size, 0.0, color), | ||||
|         (-size, -size, color), | ||||
|         (-size, -size, color), | ||||
|         (-size, -size, 0), | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| impl Transformers for Grid { | ||||
|     fn apply(&self, _point_list: &[Point], _ws: &WorldState) -> Vec<Point> { | ||||
| 	let mut sq1 = square_box(1000.0, 255 << 8); | ||||
| 	let mut line = square_box(2000.0, 255); | ||||
| 	line.append(&mut sq1); | ||||
|         let mut sq1 = square_box(1000.0, 255 << 8); | ||||
|         let mut line = square_box(2000.0, 255); | ||||
|         line.append(&mut sq1); | ||||
| 
 | ||||
| 	let line: Vec<Point> = line.into_iter() | ||||
| 	    .map(|tpl| tpl.into()).collect(); | ||||
| 	line | ||||
|         let line: Vec<Point> = line.into_iter().map(|tpl| tpl.into()).collect(); | ||||
|         line | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,27 +0,0 @@ | ||||
| use crate::transformer::Transformers; | ||||
| use crate::point::Point; | ||||
| use crate::worldstate::WorldState; | ||||
| 
 | ||||
| use serde::{Serialize, Deserialize}; | ||||
| 
 | ||||
| /// Converts helios Geometry to Helios
 | ||||
| #[allow(dead_code)] | ||||
| #[derive(Serialize, Deserialize, Debug, Clone, Copy)] | ||||
| pub struct HeliosToEtherdream { | ||||
| } | ||||
| 
 | ||||
| impl Transformers for HeliosToEtherdream { | ||||
| 	fn apply(&self, point_list: &[Point], _ws: &WorldState) -> Vec<Point> { | ||||
| 		// debug!("list helios {:?}", point_list);
 | ||||
| 		let out = point_list.iter().map(|pt| { | ||||
| 			Point { | ||||
| 				x: 8.0 * (pt.x - 2047.0), | ||||
| 				y: 8.0 * (pt.y - 2047.0), | ||||
| 				..*pt | ||||
| 			} | ||||
| 		}).collect(); | ||||
| 		// debug!("list etherdream {:?}", out);
 | ||||
| 		out | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -1,20 +1,18 @@ | ||||
| use crate::transformer::Transformers; | ||||
| use crate::point::Point; | ||||
| use crate::worldstate::{WorldState,EDH}; | ||||
| use serde::{Serialize,Deserialize}; | ||||
| use crate::transformer::Transformers; | ||||
| use crate::worldstate::{WorldState, EDH}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| /// Homography
 | ||||
| 
 | ||||
| #[derive(Serialize,Deserialize,Debug,Clone,Copy)] | ||||
| #[derive(Serialize, Deserialize, Debug, Clone, Copy)] | ||||
| pub struct Homography {} | ||||
| 
 | ||||
| impl Transformers for Homography { | ||||
|     fn apply(&self, point_list: &[Point], ws: &WorldState) -> Vec<Point> { | ||||
| 	let edh : &EDH = &ws.edh; | ||||
|         let edh: &EDH = &ws.edh; | ||||
| 
 | ||||
| 	point_list.iter() | ||||
| 	    .map(| point | edh.apply(point)) | ||||
| 	    .collect() | ||||
|         point_list.iter().map(|point| edh.apply(point)).collect() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -24,41 +22,67 @@ mod tests { | ||||
| 
 | ||||
|     #[test] | ||||
|     fn identity_matrix_let_point_unchanged() { | ||||
| 	let p0 = Point { x: -1500.0, y: 1500.0, ..Point::default() }; | ||||
|         let p0 = Point { | ||||
|             x: -1500.0, | ||||
|             y: 1500.0, | ||||
|             ..Point::default() | ||||
|         }; | ||||
| 
 | ||||
| 	let edh = EDH::new(vec![ | ||||
| 	    vec![ 1.0, 0.0, 0.0 ], | ||||
| 	    vec![ 0.0, 1.0, 0.0 ], | ||||
| 	    vec![ 0.0, 0.0, 1.0 ] | ||||
| 	]).unwrap(); | ||||
|         let edh = EDH::new(vec![ | ||||
|             vec![1.0, 0.0, 0.0], | ||||
|             vec![0.0, 1.0, 0.0], | ||||
|             vec![0.0, 0.0, 1.0], | ||||
|         ]) | ||||
|         .unwrap(); | ||||
| 
 | ||||
| 	let ws = WorldState { edh : edh, ..WorldState::default() }; | ||||
|         let ws = WorldState { | ||||
|             edh: edh, | ||||
|             ..WorldState::default() | ||||
|         }; | ||||
| 
 | ||||
| 	let homography = Homography{}; | ||||
| 	let result = homography.apply(&[p0], &ws); | ||||
|         let homography = Homography {}; | ||||
|         let result = homography.apply(&[p0], &ws); | ||||
| 
 | ||||
| 	assert_eq!(result, vec![Point { x: -1500.0, | ||||
| 					y: 1500.0, | ||||
| 					..Point::default() }]); | ||||
|         assert_eq!( | ||||
|             result, | ||||
|             vec![Point { | ||||
|                 x: -1500.0, | ||||
|                 y: 1500.0, | ||||
|                 ..Point::default() | ||||
|             }] | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn rotation_matrix_rotate_the_point() { | ||||
| 	let p0 = Point { x: -1500.0, y: 1500.0, ..Point::default() }; | ||||
|         let p0 = Point { | ||||
|             x: -1500.0, | ||||
|             y: 1500.0, | ||||
|             ..Point::default() | ||||
|         }; | ||||
| 
 | ||||
| 	let edh = EDH::new(vec![ | ||||
| 	    vec![ 1.24107321e-03, 1.00500127e-03, 7.15439347e-01], | ||||
| 	    vec![-9.93223912e-04, 1.22652939e-03,-6.98671238e-01], | ||||
| 	    vec![ 1.06017142e-17,-4.69459541e-17, 3.32700590e-05] | ||||
| 	]).unwrap(); | ||||
|         let edh = EDH::new(vec![ | ||||
|             vec![1.24107321e-03, 1.00500127e-03, 7.15439347e-01], | ||||
|             vec![-9.93223912e-04, 1.22652939e-03, -6.98671238e-01], | ||||
|             vec![1.06017142e-17, -4.69459541e-17, 3.32700590e-05], | ||||
|         ]) | ||||
|         .unwrap(); | ||||
| 
 | ||||
| 	let ws = WorldState { edh : edh, ..WorldState::default() }; | ||||
|         let ws = WorldState { | ||||
|             edh: edh, | ||||
|             ..WorldState::default() | ||||
|         }; | ||||
| 
 | ||||
| 	let homography = Homography{}; | ||||
| 	let result = homography.apply(&[p0], &ws); | ||||
|         let homography = Homography {}; | ||||
|         let result = homography.apply(&[p0], &ws); | ||||
| 
 | ||||
| 	assert_eq!(result, vec![Point { x: 10860.557, | ||||
| 					y: 79078.87, | ||||
| 					..Point::default() }]); | ||||
|         assert_eq!( | ||||
|             result, | ||||
|             vec![Point { | ||||
|                 x: 10860.557, | ||||
|                 y: 79078.87, | ||||
|                 ..Point::default() | ||||
|             }] | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,28 +0,0 @@ | ||||
| use log::debug; | ||||
| use crate::transformer::Transformers; | ||||
| use crate::point::Point; | ||||
| use crate::worldstate::WorldState; | ||||
| 
 | ||||
| use serde::{Serialize, Deserialize}; | ||||
| 
 | ||||
| /// Converts helios Geometry to Helios
 | ||||
| #[allow(dead_code)] | ||||
| #[derive(Serialize, Deserialize, Debug, Clone, Copy)] | ||||
| pub struct Intensity { | ||||
| } | ||||
| 
 | ||||
| impl Transformers for Intensity { | ||||
| 	fn apply(&self, point_list: &[Point], ws: &WorldState) -> Vec<Point> { | ||||
| 		// debug!("list helios {:?}", point_list);
 | ||||
| 		let out = point_list.iter().map(|pt| { | ||||
| 			Point { | ||||
| 				x: pt.x, | ||||
| 				y: pt.y, | ||||
| 				color: pt.color * ws.intensity | ||||
| 			} | ||||
| 		}).collect(); | ||||
| 		debug!("list intensity {:?}", out); | ||||
| 		out | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -1,34 +1,49 @@ | ||||
| use crate::point::{Color, Point}; | ||||
| use crate::transformer::Transformers; | ||||
| use crate::point::Point; | ||||
| use crate::worldstate::WorldState; | ||||
| 
 | ||||
| use serde::{Serialize,Deserialize}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| /// Replicate
 | ||||
| 
 | ||||
| #[allow(dead_code)] | ||||
| #[derive(Serialize,Deserialize,Debug,Clone,Copy)] | ||||
| #[derive(Serialize, Deserialize, Debug, Clone, Copy)] | ||||
| pub enum Replicate { | ||||
|     Until(usize), | ||||
|     Times(usize) | ||||
|     Times(usize), | ||||
| } | ||||
| 
 | ||||
| impl Transformers for Replicate { | ||||
|     fn apply(&self, point_list: &[Point], _ws: &WorldState) -> Vec<Point> { | ||||
| 	let mut point_list2 = vec![]; | ||||
| 	match self { | ||||
| 	    Replicate::Until(n) => { | ||||
| 		while point_list2.len() < *n { | ||||
| 		    point_list2.append(&mut point_list.to_vec()); | ||||
| 		} | ||||
| 	    }, | ||||
| 	    Replicate::Times(n) => { | ||||
| 		for _ in 0..*n { | ||||
| 		    point_list2.append(&mut point_list.to_vec());		    
 | ||||
| 		} | ||||
| 	    } | ||||
| 	} | ||||
| 	point_list2 | ||||
|         let mut point_list2 = vec![]; | ||||
| 
 | ||||
|         match self { | ||||
|             Replicate::Until(n) => { | ||||
|                 while point_list2.len() < *n { | ||||
|                     if point_list.len() == 0 { | ||||
|                         // to prevent infinit loop in case of empty frame
 | ||||
|                         point_list2.append( | ||||
|                             &mut vec![ | ||||
|                                 Point { | ||||
|                                     x: 0., | ||||
|                                     y: 0., | ||||
|                                     color: Color { r: 0, g: 0, b: 0 } | ||||
|                                 }; | ||||
|                                 *n | ||||
|                             ] | ||||
|                             .to_vec(), | ||||
|                         ); | ||||
|                     } else { | ||||
|                         point_list2.append(&mut point_list.to_vec()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             Replicate::Times(n) => { | ||||
|                 for _ in 0..*n { | ||||
|                     point_list2.append(&mut point_list.to_vec()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         point_list2 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,35 +1,32 @@ | ||||
| use crate::transformer::Transformers; | ||||
| use crate::point::Point; | ||||
| use crate::transformer::Transformers; | ||||
| use crate::worldstate::WorldState; | ||||
| 
 | ||||
| use serde::{Serialize,Deserialize}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| //use std::f32::consts::PI;
 | ||||
| 
 | ||||
| /// Rotate
 | ||||
| 
 | ||||
| #[derive(Serialize,Deserialize,Debug,Clone,Copy)] | ||||
| #[derive(Serialize, Deserialize, Debug, Clone, Copy)] | ||||
| pub struct Rotate { | ||||
|     cx: f32, | ||||
|     cy: f32, | ||||
|     angle: f32, | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| impl Transformers for Rotate { | ||||
|     fn apply(&self, point_list: &[Point], _ws: &WorldState) -> Vec<Point> { | ||||
|     point_list.iter() | ||||
|         .map(| pt | { | ||||
|             let dx = pt.x - self.cx; | ||||
|             let dy = pt.y - self.cy; | ||||
|             let cos = self.angle.cos(); | ||||
|             let sin = self.angle.sin(); | ||||
|             let x = (dx * cos - dy * sin) + self.cx; 
 | ||||
|             let y = (dx * sin + dy * cos) + self.cy; 
 | ||||
|             Point { x, | ||||
|                 y, | ||||
|                 ..*pt | ||||
|             } | ||||
|         }).collect() | ||||
|         point_list | ||||
|             .iter() | ||||
|             .map(|pt| { | ||||
|                 let dx = pt.x - self.cx; | ||||
|                 let dy = pt.y - self.cy; | ||||
|                 let cos = self.angle.cos(); | ||||
|                 let sin = self.angle.sin(); | ||||
|                 let x = (dx * cos - dy * sin) + self.cx; | ||||
|                 let y = (dx * sin + dy * cos) + self.cy; | ||||
|                 Point { x, y, ..*pt } | ||||
|             }) | ||||
|             .collect() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,24 +1,25 @@ | ||||
| use crate::transformer::Transformers; | ||||
| use crate::point::Point; | ||||
| use crate::transformer::Transformers; | ||||
| use crate::worldstate::WorldState; | ||||
| use serde::{Serialize,Deserialize}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| /// Translate
 | ||||
| 
 | ||||
| #[derive(Serialize,Deserialize,Debug,Clone,Copy)] | ||||
| #[derive(Serialize, Deserialize, Debug, Clone, Copy)] | ||||
| pub struct Translate { | ||||
|     pub x: f32, | ||||
|     pub y: f32 | ||||
|     pub y: f32, | ||||
| } | ||||
| 
 | ||||
| impl Transformers for Translate { | ||||
|     fn apply(&self, point_list: &[Point], _ws: &WorldState) -> Vec<Point> { | ||||
| 	point_list.iter() | ||||
| 	    .map(| pt | { | ||||
| 		Point { x: pt.x + self.x, | ||||
| 			y: pt.y + self.y, | ||||
| 			..*pt | ||||
| 		} | ||||
| 	    }).collect() | ||||
|         point_list | ||||
|             .iter() | ||||
|             .map(|pt| Point { | ||||
|                 x: pt.x + self.x, | ||||
|                 y: pt.y + self.y, | ||||
|                 ..*pt | ||||
|             }) | ||||
|             .collect() | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,54 +1,54 @@ | ||||
| use crate::point::{Point, Color}; | ||||
| use nalgebra::base::{Matrix3, Matrix1x3}; | ||||
| use crate::errors::{LJError, LJResult}; | ||||
| use crate::point::{Color, Point}; | ||||
| use log::debug; | ||||
| use crate::redis_ctrl::Resampler; | ||||
| use nalgebra::base::{Matrix1x3, Matrix3}; | ||||
| 
 | ||||
| #[derive(Debug, Default)] | ||||
| pub struct EDH { | ||||
| 	pub matrix: Matrix3<f32>, | ||||
|     pub matrix: Matrix3<f32>, | ||||
| } | ||||
| 
 | ||||
| impl EDH { | ||||
| 	pub fn new(vec: Vec<Vec<f32>>) -> LJResult<EDH> { | ||||
| 		if vec.len() != 3 || | ||||
| 			vec[0].len() != 3 || | ||||
| 			vec[1].len() != 3 || | ||||
| 			vec[2].len() != 3 { | ||||
| 			return Err(Box::new(LJError::BadEDH)); | ||||
| 		} | ||||
|     pub fn new(vec: Vec<Vec<f32>>) -> LJResult<EDH> { | ||||
|         if vec.len() != 3 || vec[0].len() != 3 || vec[1].len() != 3 || vec[2].len() != 3 { | ||||
|             return Err(Box::new(LJError::BadEDH)); | ||||
|         } | ||||
| 
 | ||||
| 		// this is the matrix already transposed.
 | ||||
| 		let matrix = Matrix3::new(vec[0][0], vec[1][0], vec[2][0], | ||||
| 		                          vec[0][1], vec[1][1], vec[2][1], | ||||
| 		                          vec[0][2], vec[1][2], vec[2][2]); | ||||
|         // this is the matrix already transposed.
 | ||||
|         let matrix = Matrix3::new( | ||||
|             vec[0][0], vec[1][0], vec[2][0], vec[0][1], vec[1][1], vec[2][1], vec[0][2], vec[1][2], | ||||
|             vec[2][2], | ||||
|         ); | ||||
| 
 | ||||
| 		Ok(EDH { matrix }) | ||||
| 	} | ||||
|         Ok(EDH { matrix }) | ||||
|     } | ||||
| 
 | ||||
| 	pub fn apply(&self, point: &Point) -> Point { | ||||
| 		let p = Matrix1x3::new(point.x, point.y, 1.0); | ||||
| 		let p = p * self.matrix; | ||||
| 		let new_p = Point { x: p[0] / p[2], y: p[1] / p[2], ..*point }; | ||||
|     pub fn apply(&self, point: &Point) -> Point { | ||||
|         let p = Matrix1x3::new(point.x, point.y, 1.0); | ||||
|         let p = p * self.matrix; | ||||
|         let new_p = Point { | ||||
|             x: p[0] / p[2], | ||||
|             y: p[1] / p[2], | ||||
|             ..*point | ||||
|         }; | ||||
| 
 | ||||
| 		debug!("{:?} => {:?}", point, new_p); | ||||
|         debug!("{:?} => {:?}", point, new_p); | ||||
| 
 | ||||
| 		new_p | ||||
| 	} | ||||
|         new_p | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Default)] | ||||
| pub struct WorldState { | ||||
| 	pub edh: EDH, | ||||
| 	pub resampler: Resampler, | ||||
| 	pub client_key: String, | ||||
| 	pub intensity: u8, | ||||
| 	pub kpps: u32, | ||||
| 	pub color: Color, | ||||
| 	pub draw_black: bool, | ||||
| 	pub draw_grid: bool, | ||||
| 	pub grid: Vec<Point>, | ||||
| 	pub color_balance: Color, | ||||
|     pub edh: EDH, | ||||
|     pub resampler: Vec<f32>, | ||||
|     pub client_key: String, | ||||
|     pub intensity: u8, | ||||
|     pub kpps: u32, | ||||
|     pub color: Color, | ||||
|     pub draw_black: bool, | ||||
|     pub draw_grid: bool, | ||||
|     pub grid: Vec<Point>, | ||||
| } | ||||
| 
 | ||||
| impl WorldState {} | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| use lj_rust::conf::{Conf, DacFamily, HeliosConf, EtherDreamConf}; | ||||
| use lj_rust::conf::{Conf, DacFamily, EtherDreamConf, HeliosConf}; | ||||
| 
 | ||||
| #[test] | ||||
| fn it_loads_a_valid_conf() { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user