mirror of https://github.com/4yn/slidershim
229 lines
6.9 KiB
Rust
229 lines
6.9 KiB
Rust
use std::{
|
|
ops::DerefMut,
|
|
thread,
|
|
time::{Duration, Instant},
|
|
};
|
|
|
|
use log::{error, info};
|
|
|
|
use palette::{FromColor, Hsv, Srgb};
|
|
use serialport::{ClearBuffer, SerialPort, StopBits};
|
|
|
|
use crate::slider_io::{
|
|
config::{LedMode, ReactiveLayout},
|
|
controller_state::{FullState, LedState},
|
|
utils::Buffer,
|
|
voltex::VoltexState,
|
|
worker::ThreadJob,
|
|
};
|
|
|
|
pub struct LedJob {
|
|
state: FullState,
|
|
mode: LedMode,
|
|
serial_port: Option<Box<dyn SerialPort>>,
|
|
}
|
|
|
|
impl LedJob {
|
|
pub fn new(state: &FullState, mode: &LedMode) -> Self {
|
|
Self {
|
|
state: state.clone(),
|
|
mode: mode.clone(),
|
|
serial_port: None,
|
|
}
|
|
}
|
|
|
|
fn calc_lights(
|
|
&self,
|
|
flat_controller_state: Option<&Vec<bool>>,
|
|
serial_buffer: Option<&Buffer>,
|
|
led_state: &mut LedState,
|
|
) {
|
|
match self.mode {
|
|
LedMode::Reactive { layout, .. } => {
|
|
let flat_controller_state = flat_controller_state.unwrap();
|
|
|
|
match layout {
|
|
ReactiveLayout::Even { splits } => {
|
|
let buttons_per_split = 32 / splits;
|
|
|
|
let banks: Vec<bool> = flat_controller_state
|
|
.chunks(32 / splits)
|
|
.take(splits)
|
|
.map(|x| x.contains(&true))
|
|
.collect();
|
|
|
|
for idx in 0..31 {
|
|
led_state.paint(
|
|
idx,
|
|
match (idx + 1) % buttons_per_split {
|
|
0 => &[255, 0, 255],
|
|
_ => match banks[idx / buttons_per_split] {
|
|
true => &[255, 0, 255],
|
|
false => &[255, 255, 0],
|
|
},
|
|
},
|
|
);
|
|
}
|
|
}
|
|
ReactiveLayout::Voltex => {
|
|
led_state.led_state.fill(0);
|
|
|
|
// Fixed
|
|
led_state.paint(3, &[0, 0, 64]);
|
|
for idx in 0..5 {
|
|
led_state.paint(7 + idx * 4, &[64, 64, 64]);
|
|
}
|
|
led_state.paint(27, &[64, 0, 0]);
|
|
|
|
let voltex_state = VoltexState::from_flat(flat_controller_state);
|
|
|
|
// Left laser
|
|
for (idx, state) in voltex_state.laser[0..2].iter().enumerate() {
|
|
if *state {
|
|
led_state.paint(0 + idx * 4, &[0, 0, 255]);
|
|
led_state.paint(1 + idx * 4, &[0, 0, 255]);
|
|
led_state.paint(2 + idx * 4, &[0, 0, 255]);
|
|
}
|
|
}
|
|
|
|
// Right laser
|
|
for (idx, state) in voltex_state.laser[2..4].iter().enumerate() {
|
|
if *state {
|
|
led_state.paint(24 + idx * 4, &[255, 0, 0]);
|
|
led_state.paint(25 + idx * 4, &[255, 0, 0]);
|
|
led_state.paint(26 + idx * 4, &[255, 0, 0]);
|
|
}
|
|
}
|
|
|
|
// Buttons
|
|
for (idx, state) in voltex_state.bt.iter().enumerate() {
|
|
if *state {
|
|
led_state.paint(8 + idx * 4, &[255, 255, 255]);
|
|
led_state.paint(10 + idx * 4, &[255, 255, 255]);
|
|
}
|
|
}
|
|
|
|
// Fx
|
|
for (idx, state) in voltex_state.fx.iter().enumerate() {
|
|
if *state {
|
|
led_state.paint(9 + idx * 8, &[255, 0, 0]);
|
|
led_state.paint(11 + idx * 8, &[255, 0, 0]);
|
|
led_state.paint(13 + idx * 8, &[255, 0, 0]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LedMode::Attract => {
|
|
let now = Instant::now();
|
|
let theta = (now - led_state.start).div_duration_f64(Duration::from_secs(4)) % 1.0;
|
|
for idx in 0..31 {
|
|
let slice_theta = (&theta + (idx as f64) / 32.0) % 1.0;
|
|
let color = Srgb::from_color(Hsv::new(slice_theta * 360.0, 1.0, 1.0)).into_format::<u8>();
|
|
led_state.paint(idx, &[color.red, color.green, color.blue]);
|
|
}
|
|
}
|
|
LedMode::Serial { .. } => {
|
|
// https://github.com/jmontineri/OpeNITHM/blob/89e9a43f7484e8949cd31bbff79c32f21ea3ec1d/Firmware/OpeNITHM/SerialProcessor.h
|
|
// https://github.com/jmontineri/OpeNITHM/blob/89e9a43f7484e8949cd31bbff79c32f21ea3ec1d/Firmware/OpeNITHM/SerialProcessor.cpp
|
|
// https://github.com/jmontineri/OpeNITHM/blob/89e9a43f7484e8949cd31bbff79c32f21ea3ec1d/Firmware/OpeNITHM/SerialLeds.h
|
|
// https://github.com/jmontineri/OpeNITHM/blob/89e9a43f7484e8949cd31bbff79c32f21ea3ec1d/Firmware/OpeNITHM/SerialLeds.cpp
|
|
if let Some(serial_buffer) = serial_buffer {
|
|
// println!("buffer {:?}", serial_buffer.data);
|
|
if serial_buffer.data[0] == 0xaa && serial_buffer.data[1] == 0xaa {
|
|
for (idx, buf_chunk) in serial_buffer.data[2..95]
|
|
.chunks(3)
|
|
.take(31)
|
|
.rev()
|
|
.enumerate()
|
|
{
|
|
led_state.paint(idx, &[(*buf_chunk)[1], (*buf_chunk)[2], (*buf_chunk)[0]]);
|
|
}
|
|
println!("leds {:?}", led_state.led_state);
|
|
}
|
|
}
|
|
}
|
|
_ => panic!("Not implemented"),
|
|
}
|
|
|
|
led_state.dirty = true;
|
|
}
|
|
}
|
|
|
|
impl ThreadJob for LedJob {
|
|
fn setup(&mut self) -> bool {
|
|
match &self.mode {
|
|
LedMode::Serial { port } => {
|
|
info!(
|
|
"Serial port for led opening at {} {:?}",
|
|
port.as_str(),
|
|
115200
|
|
);
|
|
self.serial_port = match serialport::new(port, 115200).open() {
|
|
Ok(s) => {
|
|
info!("Serial port opened");
|
|
Some(s)
|
|
}
|
|
Err(e) => {
|
|
error!("Serial port could not open, exiting thread early");
|
|
None
|
|
}
|
|
};
|
|
|
|
self.serial_port.is_some()
|
|
}
|
|
_ => true,
|
|
}
|
|
}
|
|
|
|
fn tick(&mut self) {
|
|
let mut flat_controller_state: Option<Vec<bool>> = None;
|
|
let mut serial_buffer: Option<Buffer> = None;
|
|
|
|
// Do the IO here
|
|
match self.mode {
|
|
LedMode::Reactive { sensitivity, .. } => {
|
|
let controller_state_handle = self.state.controller_state.lock().unwrap();
|
|
flat_controller_state = Some(controller_state_handle.flat(&sensitivity));
|
|
}
|
|
LedMode::Serial { .. } => {
|
|
if let Some(serial_port) = self.serial_port.as_mut() {
|
|
let mut serial_data_avail = serial_port.bytes_to_read().unwrap_or(0);
|
|
if serial_data_avail < 100 {
|
|
return;
|
|
}
|
|
|
|
if serial_data_avail % 100 == 0 {
|
|
let mut serial_buffer_working = Buffer::new();
|
|
serial_port
|
|
.as_mut()
|
|
.read_exact(&mut serial_buffer_working.data[..100])
|
|
.ok()
|
|
.unwrap();
|
|
serial_data_avail -= 100;
|
|
serial_buffer = Some(serial_buffer_working);
|
|
}
|
|
|
|
if serial_data_avail > 0 {
|
|
serial_port.clear(ClearBuffer::All);
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
// Then calculate and transfer
|
|
{
|
|
let mut led_state_handle = self.state.led_state.lock().unwrap();
|
|
self.calc_lights(
|
|
flat_controller_state.as_ref(),
|
|
serial_buffer.as_ref(),
|
|
led_state_handle.deref_mut(),
|
|
);
|
|
}
|
|
thread::sleep(Duration::from_millis(30));
|
|
}
|
|
|
|
fn teardown(&mut self) {}
|
|
}
|