init: set your eyes
This commit is contained in:
commit
cc9966bf73
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal 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
17
Cargo.toml
Normal 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
25
README.md
Normal 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
17
copyme.settings.toml
Normal 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
24
src/conf.rs
Normal 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
20
src/draw.rs
Normal 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
38
src/framerate.rs
Normal 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
19
src/logs.rs
Normal 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
66
src/main.rs
Normal 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
40
src/point.rs
Normal 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())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user