commit cc9966bf73dd692f19e50790e2b17b44de0ff8ee Author: alban Date: Mon Jul 24 23:32:18 2023 +0200 init: set your eyes diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c124a83 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Configuration file +settings.* + +.idea diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..07f361c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "lj_templates_rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + + +[dependencies] +chrono = "0.4.26" +config = "0.13.3" +ctrlc = "3.4.0" +env_logger = "0.10.0" +log = "0.4.18" +redis = "0.23.0" +toml = "0.7.4" +serde = { version = "1.0.163", features = ["derive"] } diff --git a/README.md b/README.md new file mode 100644 index 0000000..3e80551 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# LJ Template for Rust + +**This project is a basic program for building rust programs compatible with the protonphoton LJ project.** + +Use this code as a basis and let the laser points be with you. + +**Crash course** + +``` + +$ git clone https://git.interhacker.space/protonphoton/lj_templates_rust && cd lj_templates_rust + +# Create a settings file, see the content for more details including redis server url +$ cp copyme.settings.toml settings.toml && $EDITOR settings.toml + +# Run the projet +$ cargo run + +``` + +## Associate projects + +**The LJ rust tracer projet and the nannou visualiser might be of help to simulate your project.** + +Todo: make a full tutorial for that \ No newline at end of file diff --git a/copyme.settings.toml b/copyme.settings.toml new file mode 100644 index 0000000..4637283 --- /dev/null +++ b/copyme.settings.toml @@ -0,0 +1,17 @@ +# file: settings.toml +# Rename me ! + +# The main key of your laser in LJ +laser_id = 0 + +# Your client id in LJ +client_id = 0 + +# Activate for more debug +debug = true + +# How many Frames Per Second +framerate = 20 + +# Redis URL as redis://IP:port/ +redis_url = "redis://127.0.0.1:6379/" diff --git a/src/conf.rs b/src/conf.rs new file mode 100644 index 0000000..06d242a --- /dev/null +++ b/src/conf.rs @@ -0,0 +1,24 @@ +use std::error::Error; +use config::Config; +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Conf { + pub debug: bool, + pub laser_id: u8, + pub client_id: u8, + pub redis_url: String, + pub framerate: u8, +} + + +impl Conf { + pub fn new(path: &str) -> Result> { + let settings = Config::builder() + .add_source(config::File::with_name(path)) + .build()?; + let conf: Conf = settings.try_deserialize()?; + Ok(conf) + } + +} diff --git a/src/draw.rs b/src/draw.rs new file mode 100644 index 0000000..67ee510 --- /dev/null +++ b/src/draw.rs @@ -0,0 +1,20 @@ +// This is where you can put your custom code + +use crate::{Color, Point}; + +const RADIUS: f32 = 2000.0; +const X_CENTER: f32 = 2047.0; +const Y_CENTER: f32 = 2047.0; + +pub fn draw() -> Result, Box> { + let mut v: Vec = vec![]; + for i in 0..128 { + let a = i as f32 / 128.0 * std::f32::consts::PI * 2.0; + v.push(Point { + x: (X_CENTER + a.cos() * RADIUS) as f32, + y: (Y_CENTER + a.sin() * RADIUS) as f32, + color: Color { r: 255, g: 255, b: 255 }, + }); + } + Ok(v) +} \ No newline at end of file diff --git a/src/framerate.rs b/src/framerate.rs new file mode 100644 index 0000000..e49923e --- /dev/null +++ b/src/framerate.rs @@ -0,0 +1,38 @@ +use log::{debug, warn}; +use std::time::{Duration, Instant}; +use std::{thread}; + +/// Converts helios Geometry to Helios +#[derive(Debug, Clone, Copy)] +pub struct Framerate { + prev_trace_time: Instant, + framerate: u8, +} + +impl Framerate { + pub fn new(framerate: u8) -> Result> { + Ok(Framerate { + prev_trace_time: Instant::now(), + framerate, + }) + } + pub fn handle_time(&mut self) -> Result<(), Box> { + let frame_time = 1000000000 / self.framerate 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 slower than expected {:?} > {:?}", nanotime_spent, frame_time, ); + } + + self.prev_trace_time = now; + Ok(()) + } +} diff --git a/src/logs.rs b/src/logs.rs new file mode 100644 index 0000000..7306689 --- /dev/null +++ b/src/logs.rs @@ -0,0 +1,19 @@ +use env_logger::Builder; +use log::LevelFilter; +use crate::Conf; + +pub fn init_logging(config: &Result>) { + 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(); + return; + } + env_logger::init(); +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e357c78 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,66 @@ +/// +/// Configure udev: +/// https://github.com/Grix/helios_dac/blob/master/docs/udev_rules_for_linux.md +/// +mod conf; +mod point; +mod draw; +mod logs; +mod framerate; + +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use conf::Conf; +use point::{Point, Color}; +use log::{info, /* warn, */ error}; +use redis::{Client, Commands, Connection}; +use logs::init_logging; + +const DEFAULT_CONF_FILE: &str = "settings.toml"; + +pub fn main() { + match run_all() { + Ok(()) => {} + Err(err) => { + error!("Error: {}", err); + } + } +} + + +fn run_all() -> Result<(), Box> { + // 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() }); + let config = Conf::new(&filename); + init_logging(&config); + let config = config?; + + let client = Client::open(config.redis_url.clone())?; + let mut con: Connection = client.get_connection()?; + + let mut framerate_handler = framerate::Framerate::new( config.framerate )?; + + info!("*** Starting up ***"); + info!("{:?}", config); + + // Dispatch based on redis requests + while running.load(Ordering::SeqCst) { + let _ = framerate_handler.handle_time(); + let points_list: Vec = draw::draw()?; + let v: Vec<(f32, f32, u32)> = points_list.iter().map(|pt| { + (pt.x, pt.y, u32::from(pt.color)) + } + ).collect(); + // println!("{:?}", v); + let _ = con.set(format!("/pl/{}/{}", config.client_id, config.laser_id), format!("{:?}", v))?; + } + Ok(()) +} + diff --git a/src/point.rs b/src/point.rs new file mode 100644 index 0000000..ad44400 --- /dev/null +++ b/src/point.rs @@ -0,0 +1,40 @@ +pub type Line = Vec<(f32, f32, u32)>; + +#[derive(Debug, Clone, Copy, Default, PartialEq)] +pub struct Point { + 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, +} + +impl From 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 + } +} + + +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 } } + } +} + +impl From for (f32, f32, u32) { + fn from(pt : Point ) -> (f32, f32, u32) { + (pt.x,pt.y,pt.color.into()) + } +}