init: set your eyes

This commit is contained in:
alban 2023-07-24 23:32:18 +02:00
commit cc9966bf73
10 changed files with 285 additions and 0 deletions

19
.gitignore vendored Normal file
View File

@ -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

17
Cargo.toml Normal file
View File

@ -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"] }

25
README.md Normal file
View File

@ -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

17
copyme.settings.toml Normal file
View File

@ -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/"

24
src/conf.rs Normal file
View File

@ -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<Conf, Box<dyn Error>> {
let settings = Config::builder()
.add_source(config::File::with_name(path))
.build()?;
let conf: Conf = settings.try_deserialize()?;
Ok(conf)
}
}

20
src/draw.rs Normal file
View File

@ -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<Vec<Point>, Box<dyn std::error::Error>> {
let mut v: Vec<Point> = 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)
}

38
src/framerate.rs Normal file
View File

@ -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<Self, Box<dyn std::error::Error>> {
Ok(Framerate {
prev_trace_time: Instant::now(),
framerate,
})
}
pub fn handle_time(&mut self) -> Result<(), Box<dyn std::error::Error>> {
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(())
}
}

19
src/logs.rs Normal file
View File

@ -0,0 +1,19 @@
use env_logger::Builder;
use log::LevelFilter;
use crate::Conf;
pub fn init_logging(config: &Result<Conf, Box<dyn std::error::Error>>) {
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();
}

66
src/main.rs Normal file
View File

@ -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<dyn std::error::Error>> {
// 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<Point> = 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(())
}

40
src/point.rs Normal file
View File

@ -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<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
}
}
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<Point> for (f32, f32, u32) {
fn from(pt : Point ) -> (f32, f32, u32) {
(pt.x,pt.y,pt.color.into())
}
}