mirror of
https://github.com/4yn/slidershim.git
synced 2026-04-26 01:45:00 -05:00
refactor to 'lights', add diva protocol
This commit is contained in:
parent
287c9c63f6
commit
f356624502
|
|
@ -3,7 +3,7 @@ use serde_json::Value;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
input::config::DeviceMode, lighting::config::LedMode, output::config::OutputMode, system,
|
device::config::DeviceMode, lighting::config::LightsMode, output::config::OutputMode, system,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -11,7 +11,7 @@ pub struct Config {
|
||||||
pub raw: String,
|
pub raw: String,
|
||||||
pub device_mode: DeviceMode,
|
pub device_mode: DeviceMode,
|
||||||
pub output_mode: OutputMode,
|
pub output_mode: OutputMode,
|
||||||
pub led_mode: LedMode,
|
pub lights_mode: LightsMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
|
@ -22,7 +22,7 @@ impl Config {
|
||||||
raw: s.to_string(),
|
raw: s.to_string(),
|
||||||
device_mode: DeviceMode::from_serde_value(&v)?,
|
device_mode: DeviceMode::from_serde_value(&v)?,
|
||||||
output_mode: OutputMode::from_serde_value(&v)?,
|
output_mode: OutputMode::from_serde_value(&v)?,
|
||||||
led_mode: LedMode::from_serde_value(&v)?,
|
lights_mode: LightsMode::from_serde_value(&v)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ use std::sync::{atomic::Ordering, Arc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
input::{brokenithm::BrokenithmJob, config::DeviceMode, device::HidDeviceJob},
|
device::{brokenithm::BrokenithmJob, config::DeviceMode, diva::DivaSliderJob, hid::HidJob},
|
||||||
lighting::{config::LedMode, lighting::LedJob},
|
lighting::{config::LightsMode, lighting::LightsJob},
|
||||||
output::{config::OutputMode, output::OutputJob},
|
output::{config::OutputMode, output::OutputJob},
|
||||||
shared::{
|
shared::{
|
||||||
utils::LoopTimer,
|
utils::LoopTimer,
|
||||||
|
|
@ -18,10 +18,10 @@ use crate::{
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
state: SliderState,
|
state: SliderState,
|
||||||
config: Config,
|
config: Config,
|
||||||
device_worker: Option<ThreadWorker>,
|
device_thread_worker: Option<ThreadWorker>,
|
||||||
brokenithm_worker: Option<AsyncHaltableWorker>,
|
device_async_haltable_worker: Option<AsyncHaltableWorker>,
|
||||||
output_worker: Option<AsyncWorker>,
|
output_worker: Option<AsyncWorker>,
|
||||||
led_worker: Option<AsyncWorker>,
|
lights_worker: Option<AsyncWorker>,
|
||||||
timers: Vec<(&'static str, Arc<AtomicF64>)>,
|
timers: Vec<(&'static str, Arc<AtomicF64>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,7 +30,7 @@ impl Context {
|
||||||
info!("Context creating");
|
info!("Context creating");
|
||||||
info!("Device config {:?}", config.device_mode);
|
info!("Device config {:?}", config.device_mode);
|
||||||
info!("Output config {:?}", config.output_mode);
|
info!("Output config {:?}", config.output_mode);
|
||||||
info!("LED config {:?}", config.led_mode);
|
info!("Lights config {:?}", config.lights_mode);
|
||||||
|
|
||||||
let state = SliderState::new();
|
let state = SliderState::new();
|
||||||
let mut timers = vec![];
|
let mut timers = vec![];
|
||||||
|
|
@ -39,12 +39,12 @@ impl Context {
|
||||||
DeviceMode::None => (None, None),
|
DeviceMode::None => (None, None),
|
||||||
DeviceMode::Brokenithm {
|
DeviceMode::Brokenithm {
|
||||||
ground_only,
|
ground_only,
|
||||||
led_enabled,
|
lights_enabled,
|
||||||
} => (
|
} => (
|
||||||
None,
|
None,
|
||||||
Some(AsyncHaltableWorker::new(
|
Some(AsyncHaltableWorker::new(
|
||||||
"brokenithm",
|
"brokenithm",
|
||||||
BrokenithmJob::new(&state, ground_only, led_enabled),
|
BrokenithmJob::new(&state, ground_only, lights_enabled),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
DeviceMode::Hardware { spec } => (
|
DeviceMode::Hardware { spec } => (
|
||||||
|
|
@ -53,7 +53,19 @@ impl Context {
|
||||||
timers.push(("d", timer.fork()));
|
timers.push(("d", timer.fork()));
|
||||||
Some(ThreadWorker::new(
|
Some(ThreadWorker::new(
|
||||||
"device",
|
"device",
|
||||||
HidDeviceJob::from_config(&state, spec),
|
HidJob::from_config(&state, spec),
|
||||||
|
timer,
|
||||||
|
))
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
DeviceMode::DivaSlider { port } => (
|
||||||
|
{
|
||||||
|
let timer = LoopTimer::new();
|
||||||
|
timers.push(("d", timer.fork()));
|
||||||
|
Some(ThreadWorker::new(
|
||||||
|
"diva",
|
||||||
|
DivaSliderJob::new(&state, port),
|
||||||
timer,
|
timer,
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
|
|
@ -72,14 +84,14 @@ impl Context {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let led_worker = match &config.led_mode {
|
let lights_worker = match &config.lights_mode {
|
||||||
LedMode::None => None,
|
LightsMode::None => None,
|
||||||
_ => {
|
_ => {
|
||||||
let timer = LoopTimer::new();
|
let timer = LoopTimer::new();
|
||||||
timers.push(("l", timer.fork()));
|
timers.push(("l", timer.fork()));
|
||||||
Some(AsyncWorker::new(
|
Some(AsyncWorker::new(
|
||||||
"led",
|
"lights",
|
||||||
LedJob::new(&state, &config.led_mode),
|
LightsJob::new(&state, &config.lights_mode),
|
||||||
timer,
|
timer,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
@ -88,10 +100,10 @@ impl Context {
|
||||||
Self {
|
Self {
|
||||||
state,
|
state,
|
||||||
config,
|
config,
|
||||||
device_worker,
|
device_thread_worker: device_worker,
|
||||||
brokenithm_worker,
|
device_async_haltable_worker: brokenithm_worker,
|
||||||
output_worker,
|
output_worker,
|
||||||
led_worker,
|
lights_worker,
|
||||||
timers,
|
timers,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
|
@ -69,7 +69,7 @@ async fn serve_file(path: &str) -> Result<Response<Body>, Infallible> {
|
||||||
async fn handle_brokenithm(
|
async fn handle_brokenithm(
|
||||||
ws_stream: WebSocketStream<Upgraded>,
|
ws_stream: WebSocketStream<Upgraded>,
|
||||||
state: SliderState,
|
state: SliderState,
|
||||||
led_enabled: bool,
|
lights_enabled: bool,
|
||||||
) {
|
) {
|
||||||
let (mut ws_write, mut ws_read) = ws_stream.split();
|
let (mut ws_write, mut ws_read) = ws_stream.split();
|
||||||
|
|
||||||
|
|
@ -153,7 +153,7 @@ async fn handle_brokenithm(
|
||||||
// info!("Websocket read task done");
|
// info!("Websocket read task done");
|
||||||
};
|
};
|
||||||
|
|
||||||
match led_enabled {
|
match lights_enabled {
|
||||||
false => {
|
false => {
|
||||||
select! {
|
select! {
|
||||||
_ = read_task => {}
|
_ = read_task => {}
|
||||||
|
|
@ -163,14 +163,14 @@ async fn handle_brokenithm(
|
||||||
true => {
|
true => {
|
||||||
let msg_write_handle = msg_write.clone();
|
let msg_write_handle = msg_write.clone();
|
||||||
let state_handle = state.clone();
|
let state_handle = state.clone();
|
||||||
let led_task = async move {
|
let lights_task = async move {
|
||||||
loop {
|
loop {
|
||||||
let mut led_data = vec![0; 93];
|
let mut lights_data = vec![0; 93];
|
||||||
{
|
{
|
||||||
let lights_handle = state_handle.lights.lock();
|
let lights_handle = state_handle.lights.lock();
|
||||||
(&mut led_data).copy_from_slice(&lights_handle.ground);
|
(&mut lights_data).copy_from_slice(&lights_handle.ground);
|
||||||
}
|
}
|
||||||
msg_write_handle.send(Message::Binary(led_data)).ok();
|
msg_write_handle.send(Message::Binary(lights_data)).ok();
|
||||||
|
|
||||||
sleep(Duration::from_millis(50)).await;
|
sleep(Duration::from_millis(50)).await;
|
||||||
}
|
}
|
||||||
|
|
@ -179,7 +179,7 @@ async fn handle_brokenithm(
|
||||||
select! {
|
select! {
|
||||||
_ = read_task => {}
|
_ = read_task => {}
|
||||||
_ = write_task => {}
|
_ = write_task => {}
|
||||||
_ = led_task => {}
|
_ = lights_task => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +188,7 @@ async fn handle_brokenithm(
|
||||||
async fn handle_websocket(
|
async fn handle_websocket(
|
||||||
mut request: Request<Body>,
|
mut request: Request<Body>,
|
||||||
state: SliderState,
|
state: SliderState,
|
||||||
led_enabled: bool,
|
lights_enabled: bool,
|
||||||
) -> Result<Response<Body>, Infallible> {
|
) -> Result<Response<Body>, Infallible> {
|
||||||
let res = match handshake::server::create_response_with_body(&request, || Body::empty()) {
|
let res = match handshake::server::create_response_with_body(&request, || Body::empty()) {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
|
|
@ -202,7 +202,7 @@ async fn handle_websocket(
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
handle_brokenithm(ws_stream, state, led_enabled).await;
|
handle_brokenithm(ws_stream, state, lights_enabled).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -229,7 +229,7 @@ async fn handle_request(
|
||||||
remote_addr: SocketAddr,
|
remote_addr: SocketAddr,
|
||||||
state: SliderState,
|
state: SliderState,
|
||||||
ground_only: bool,
|
ground_only: bool,
|
||||||
led_enabled: bool,
|
lights_enabled: bool,
|
||||||
) -> Result<Response<Body>, Infallible> {
|
) -> Result<Response<Body>, Infallible> {
|
||||||
let method = request.method();
|
let method = request.method();
|
||||||
let path = request.uri().path();
|
let path = request.uri().path();
|
||||||
|
|
@ -251,7 +251,7 @@ async fn handle_request(
|
||||||
true => serve_file("index-go.html").await,
|
true => serve_file("index-go.html").await,
|
||||||
},
|
},
|
||||||
(filename, false) => serve_file(&filename[1..]).await,
|
(filename, false) => serve_file(&filename[1..]).await,
|
||||||
("/ws", true) => handle_websocket(request, state, led_enabled).await,
|
("/ws", true) => handle_websocket(request, state, lights_enabled).await,
|
||||||
_ => error_response().await,
|
_ => error_response().await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -259,15 +259,15 @@ async fn handle_request(
|
||||||
pub struct BrokenithmJob {
|
pub struct BrokenithmJob {
|
||||||
state: SliderState,
|
state: SliderState,
|
||||||
ground_only: bool,
|
ground_only: bool,
|
||||||
led_enabled: bool,
|
lights_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BrokenithmJob {
|
impl BrokenithmJob {
|
||||||
pub fn new(state: &SliderState, ground_only: &bool, led_enabled: &bool) -> Self {
|
pub fn new(state: &SliderState, ground_only: &bool, lights_enabled: &bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
ground_only: *ground_only,
|
ground_only: *ground_only,
|
||||||
led_enabled: *led_enabled,
|
lights_enabled: *lights_enabled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -277,14 +277,14 @@ impl AsyncHaltableJob for BrokenithmJob {
|
||||||
async fn run<F: Future<Output = ()> + Send>(self, stop_signal: F) {
|
async fn run<F: Future<Output = ()> + Send>(self, stop_signal: F) {
|
||||||
let state = self.state.clone();
|
let state = self.state.clone();
|
||||||
let ground_only = self.ground_only;
|
let ground_only = self.ground_only;
|
||||||
let led_enabled = self.led_enabled;
|
let lights_enabled = self.lights_enabled;
|
||||||
let make_svc = make_service_fn(|conn: &AddrStream| {
|
let make_svc = make_service_fn(|conn: &AddrStream| {
|
||||||
let remote_addr = conn.remote_addr();
|
let remote_addr = conn.remote_addr();
|
||||||
let make_svc_state = state.clone();
|
let make_svc_state = state.clone();
|
||||||
async move {
|
async move {
|
||||||
Ok::<_, Infallible>(service_fn(move |request: Request<Body>| {
|
Ok::<_, Infallible>(service_fn(move |request: Request<Body>| {
|
||||||
let svc_state = make_svc_state.clone();
|
let svc_state = make_svc_state.clone();
|
||||||
handle_request(request, remote_addr, svc_state, ground_only, led_enabled)
|
handle_request(request, remote_addr, svc_state, ground_only, lights_enabled)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -15,7 +15,10 @@ pub enum DeviceMode {
|
||||||
},
|
},
|
||||||
Brokenithm {
|
Brokenithm {
|
||||||
ground_only: bool,
|
ground_only: bool,
|
||||||
led_enabled: bool,
|
lights_enabled: bool,
|
||||||
|
},
|
||||||
|
DivaSlider {
|
||||||
|
port: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,21 +35,24 @@ impl DeviceMode {
|
||||||
"yuancon" => DeviceMode::Hardware {
|
"yuancon" => DeviceMode::Hardware {
|
||||||
spec: HardwareSpec::Yuancon,
|
spec: HardwareSpec::Yuancon,
|
||||||
},
|
},
|
||||||
|
"diva" => DeviceMode::DivaSlider {
|
||||||
|
port: v["divaSerialPort"].as_str()?.to_string(),
|
||||||
|
},
|
||||||
"brokenithm" => DeviceMode::Brokenithm {
|
"brokenithm" => DeviceMode::Brokenithm {
|
||||||
ground_only: false,
|
ground_only: false,
|
||||||
led_enabled: false,
|
lights_enabled: false,
|
||||||
},
|
},
|
||||||
"brokenithm-led" => DeviceMode::Brokenithm {
|
"brokenithm-led" => DeviceMode::Brokenithm {
|
||||||
ground_only: false,
|
ground_only: false,
|
||||||
led_enabled: true,
|
lights_enabled: true,
|
||||||
},
|
},
|
||||||
"brokenithm-ground" => DeviceMode::Brokenithm {
|
"brokenithm-ground" => DeviceMode::Brokenithm {
|
||||||
ground_only: true,
|
ground_only: true,
|
||||||
led_enabled: false,
|
lights_enabled: false,
|
||||||
},
|
},
|
||||||
"brokenithm-ground-led" => DeviceMode::Brokenithm {
|
"brokenithm-ground-led" => DeviceMode::Brokenithm {
|
||||||
ground_only: true,
|
ground_only: true,
|
||||||
led_enabled: true,
|
lights_enabled: true,
|
||||||
},
|
},
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
313
src-slider_io/src/device/diva.rs
Normal file
313
src-slider_io/src/device/diva.rs
Normal file
|
|
@ -0,0 +1,313 @@
|
||||||
|
use log::{error, info};
|
||||||
|
use serialport::SerialPort;
|
||||||
|
use std::{collections::VecDeque, num::Wrapping, thread::sleep, time::Duration};
|
||||||
|
|
||||||
|
use crate::{shared::worker::ThreadJob, state::SliderState};
|
||||||
|
|
||||||
|
struct DivaPacket {
|
||||||
|
command: u8,
|
||||||
|
len: u8,
|
||||||
|
data: Vec<u8>,
|
||||||
|
checksum: Wrapping<u8>,
|
||||||
|
raw: Option<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DivaPacket {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
command: 0,
|
||||||
|
len: 0,
|
||||||
|
data: Vec::with_capacity(256),
|
||||||
|
checksum: Wrapping(0),
|
||||||
|
raw: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_bytes(command: u8, data: &[u8]) -> Self {
|
||||||
|
Self {
|
||||||
|
command,
|
||||||
|
len: data.len() as u8,
|
||||||
|
data: data.iter().copied().collect(),
|
||||||
|
checksum: Wrapping(0),
|
||||||
|
raw: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_raw_escaped(byte: u8, raw: &mut Vec<u8>) {
|
||||||
|
match byte {
|
||||||
|
0xfd => {
|
||||||
|
raw.push(0xfd);
|
||||||
|
raw.push(0xfc);
|
||||||
|
}
|
||||||
|
0xff => {
|
||||||
|
raw.push(0xfd);
|
||||||
|
raw.push(0xfe);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
raw.push(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize(&mut self) -> &[u8] {
|
||||||
|
let mut raw: Vec<u8> = Vec::with_capacity(512);
|
||||||
|
let mut checksum = Wrapping(0);
|
||||||
|
|
||||||
|
raw.push(0xff);
|
||||||
|
checksum += Wrapping(0xffu8);
|
||||||
|
Self::push_raw_escaped(self.command, &mut raw);
|
||||||
|
checksum += Wrapping(self.command);
|
||||||
|
Self::push_raw_escaped(self.len, &mut raw);
|
||||||
|
checksum += Wrapping(self.len);
|
||||||
|
for i in &self.data {
|
||||||
|
Self::push_raw_escaped(*i, &mut raw);
|
||||||
|
checksum += Wrapping(*i);
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum = -checksum;
|
||||||
|
Self::push_raw_escaped(checksum.0, &mut raw);
|
||||||
|
|
||||||
|
self.raw = Some(raw);
|
||||||
|
return self.raw.as_ref().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DivaDeserializerState {
|
||||||
|
ExpectCommand,
|
||||||
|
ExpectLen,
|
||||||
|
ExpectData,
|
||||||
|
ExpectChecksum,
|
||||||
|
Done,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DivaDeserializer {
|
||||||
|
state: DivaDeserializerState,
|
||||||
|
escape: u8,
|
||||||
|
len: u8,
|
||||||
|
packet: DivaPacket,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DivaDeserializer {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
state: DivaDeserializerState::Done,
|
||||||
|
escape: 1,
|
||||||
|
len: 0,
|
||||||
|
packet: DivaPacket::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(&mut self, data: &[u8], out: &mut VecDeque<DivaPacket>) {
|
||||||
|
for c in data {
|
||||||
|
match c {
|
||||||
|
0xff => {
|
||||||
|
self.packet = DivaPacket::new();
|
||||||
|
self.packet.checksum = Wrapping(0xff);
|
||||||
|
self.state = DivaDeserializerState::ExpectCommand;
|
||||||
|
}
|
||||||
|
0xfd => {
|
||||||
|
self.escape = 1;
|
||||||
|
}
|
||||||
|
c => {
|
||||||
|
let c = c + self.escape;
|
||||||
|
self.escape = 0;
|
||||||
|
|
||||||
|
self.packet.checksum += Wrapping(c);
|
||||||
|
match self.state {
|
||||||
|
DivaDeserializerState::ExpectCommand => {
|
||||||
|
self.packet.command = c;
|
||||||
|
self.state = DivaDeserializerState::ExpectLen;
|
||||||
|
}
|
||||||
|
DivaDeserializerState::ExpectLen => {
|
||||||
|
self.len = c;
|
||||||
|
self.packet.len = c;
|
||||||
|
self.state = match c {
|
||||||
|
0 => DivaDeserializerState::ExpectChecksum,
|
||||||
|
_ => DivaDeserializerState::ExpectData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
DivaDeserializerState::ExpectData => {
|
||||||
|
self.packet.data.push(c);
|
||||||
|
self.len -= 1;
|
||||||
|
|
||||||
|
if self.len == 0 {
|
||||||
|
self.state = DivaDeserializerState::ExpectChecksum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DivaDeserializerState::ExpectChecksum => {
|
||||||
|
debug_assert!(self.packet.checksum == Wrapping(0));
|
||||||
|
if self.packet.checksum == Wrapping(0) {
|
||||||
|
out.push_back(DivaPacket::new());
|
||||||
|
std::mem::swap(&mut self.packet, out.back_mut().unwrap());
|
||||||
|
}
|
||||||
|
self.state = DivaDeserializerState::Done;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DivaSliderBootstrap {
|
||||||
|
Init,
|
||||||
|
AwaitReset,
|
||||||
|
AwaitInfo,
|
||||||
|
AwaitStart,
|
||||||
|
ReadLoop,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DivaSliderJob {
|
||||||
|
state: SliderState,
|
||||||
|
port: String,
|
||||||
|
packets: VecDeque<DivaPacket>,
|
||||||
|
deserializer: DivaDeserializer,
|
||||||
|
serial_port: Option<Box<dyn SerialPort>>,
|
||||||
|
bootstrap: DivaSliderBootstrap,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DivaSliderJob {
|
||||||
|
pub fn new(state: &SliderState, port: &String) -> Self {
|
||||||
|
Self {
|
||||||
|
state: state.clone(),
|
||||||
|
port: port.clone(),
|
||||||
|
packets: VecDeque::with_capacity(100),
|
||||||
|
deserializer: DivaDeserializer::new(),
|
||||||
|
serial_port: None,
|
||||||
|
bootstrap: DivaSliderBootstrap::Init,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThreadJob for DivaSliderJob {
|
||||||
|
fn setup(&mut self) -> bool {
|
||||||
|
info!(
|
||||||
|
"Serial port for diva slider opening at {} {:?}",
|
||||||
|
self.port.as_str(),
|
||||||
|
115200
|
||||||
|
);
|
||||||
|
match serialport::new(&self.port, 152000).open() {
|
||||||
|
Ok(serial_port_buf) => {
|
||||||
|
info!("Serial port opened");
|
||||||
|
self.serial_port = Some(serial_port_buf);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Serial port could not open: {}", e);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tick(&mut self) -> bool {
|
||||||
|
let mut work = false;
|
||||||
|
|
||||||
|
let serial_port = self.serial_port.as_mut().unwrap();
|
||||||
|
|
||||||
|
let bytes_avail = serial_port.bytes_to_read().unwrap_or(0);
|
||||||
|
if bytes_avail > 0 {
|
||||||
|
let mut read_buf = vec![0 as u8; bytes_avail as usize];
|
||||||
|
serial_port.read_exact(&mut read_buf).ok();
|
||||||
|
self.deserializer.deserialize(&read_buf, &mut self.packets);
|
||||||
|
work = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.bootstrap {
|
||||||
|
DivaSliderBootstrap::Init => {
|
||||||
|
let mut reset_packet = DivaPacket::from_bytes(0x10, &[]);
|
||||||
|
serial_port.write(reset_packet.serialize()).ok();
|
||||||
|
|
||||||
|
self.bootstrap = DivaSliderBootstrap::AwaitReset;
|
||||||
|
work = true;
|
||||||
|
}
|
||||||
|
DivaSliderBootstrap::AwaitReset => {
|
||||||
|
if let Some(ack_packet) = self.packets.pop_front() {
|
||||||
|
info!(
|
||||||
|
"Diva slider ack reset {:?} {:?}",
|
||||||
|
ack_packet.command, ack_packet.data
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut info_packet = DivaPacket::from_bytes(0xf0, &[]);
|
||||||
|
serial_port.write(info_packet.serialize()).ok();
|
||||||
|
|
||||||
|
self.bootstrap = DivaSliderBootstrap::AwaitInfo;
|
||||||
|
work = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DivaSliderBootstrap::AwaitInfo => {
|
||||||
|
if let Some(ack_packet) = self.packets.pop_front() {
|
||||||
|
info!(
|
||||||
|
"Diva slider ack info {:?} {:?}",
|
||||||
|
ack_packet.command, ack_packet.data
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut start_packet = DivaPacket::from_bytes(0x03, &[]);
|
||||||
|
serial_port.write(start_packet.serialize()).ok();
|
||||||
|
|
||||||
|
self.bootstrap = DivaSliderBootstrap::AwaitStart;
|
||||||
|
work = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DivaSliderBootstrap::AwaitStart => {
|
||||||
|
if let Some(ack_packet) = self.packets.pop_front() {
|
||||||
|
info!(
|
||||||
|
"Diva slider ack start {:?} {:?}",
|
||||||
|
ack_packet.command, ack_packet.data
|
||||||
|
);
|
||||||
|
|
||||||
|
self.bootstrap = DivaSliderBootstrap::ReadLoop;
|
||||||
|
work = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DivaSliderBootstrap::ReadLoop => {
|
||||||
|
while let Some(data_packet) = self.packets.pop_front() {
|
||||||
|
if data_packet.command == 0x01 && data_packet.len == 32 {
|
||||||
|
let mut input_handle = self.state.input.lock();
|
||||||
|
input_handle
|
||||||
|
.ground
|
||||||
|
.copy_from_slice(&data_packet.data[0..32]);
|
||||||
|
work = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut send_lights = false;
|
||||||
|
let mut lights_buf = [0; 97];
|
||||||
|
{
|
||||||
|
let mut lights_handle = self.state.lights.lock();
|
||||||
|
if lights_handle.dirty {
|
||||||
|
send_lights = true;
|
||||||
|
lights_buf[0] = 0x3f;
|
||||||
|
lights_buf[1..97].copy_from_slice(&lights_handle.ground[0..96]);
|
||||||
|
lights_handle.dirty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if send_lights {
|
||||||
|
let mut lights_packet = DivaPacket::from_bytes(0x02, &lights_buf);
|
||||||
|
serial_port.write(lights_packet.serialize()).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: async worker?
|
||||||
|
sleep(Duration::from_millis(10));
|
||||||
|
|
||||||
|
work
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for DivaSliderJob {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
match self.bootstrap {
|
||||||
|
DivaSliderBootstrap::AwaitStart | DivaSliderBootstrap::ReadLoop => {
|
||||||
|
info!("Diva slider sending stop");
|
||||||
|
|
||||||
|
let serial_port = self.serial_port.as_mut().unwrap();
|
||||||
|
let mut stop_packet = DivaPacket::from_bytes(0x04, &[]);
|
||||||
|
serial_port.write(stop_packet.serialize()).ok();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ enum WriteType {
|
||||||
Interrupt,
|
Interrupt,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HidDeviceJob {
|
pub struct HidJob {
|
||||||
state: SliderState,
|
state: SliderState,
|
||||||
|
|
||||||
vid: u16,
|
vid: u16,
|
||||||
|
|
@ -44,7 +44,7 @@ pub struct HidDeviceJob {
|
||||||
handle: Option<DeviceHandle<GlobalContext>>,
|
handle: Option<DeviceHandle<GlobalContext>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HidDeviceJob {
|
impl HidJob {
|
||||||
fn new(
|
fn new(
|
||||||
state: SliderState,
|
state: SliderState,
|
||||||
vid: u16,
|
vid: u16,
|
||||||
|
|
@ -212,7 +212,7 @@ impl HidDeviceJob {
|
||||||
|
|
||||||
const TIMEOUT: Duration = Duration::from_millis(20);
|
const TIMEOUT: Duration = Duration::from_millis(20);
|
||||||
|
|
||||||
impl ThreadJob for HidDeviceJob {
|
impl ThreadJob for HidJob {
|
||||||
fn setup(&mut self) -> bool {
|
fn setup(&mut self) -> bool {
|
||||||
match self.get_handle() {
|
match self.get_handle() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
|
@ -283,7 +283,7 @@ impl ThreadJob for HidDeviceJob {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for HidDeviceJob {
|
impl Drop for HidJob {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(handle) = self.handle.as_mut() {
|
if let Some(handle) = self.handle.as_mut() {
|
||||||
handle.release_interface(0).ok();
|
handle.release_interface(0).ok();
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|
||||||
mod acio;
|
pub mod diva;
|
||||||
|
|
||||||
pub mod brokenithm;
|
pub mod brokenithm;
|
||||||
pub mod device;
|
pub mod hid;
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
// use serialport::SerialPort;
|
|
||||||
// use std::io::{BufRead, BufReader};
|
|
||||||
|
|
||||||
// struct ArcadeSlider {
|
|
||||||
// serial_port: BufReader<Box<dyn SerialPort>>,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl ArcadeSlider {
|
|
||||||
// fn new() -> Self {
|
|
||||||
// let serial_port = serialport::new("COM1", 152000).open().unwrap();
|
|
||||||
// let serial_port_buf = BufReader::new(serial_port);
|
|
||||||
// Self {
|
|
||||||
// serial_port: serial_port_buf,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn recv(&mut self) {
|
|
||||||
// let mut consumed = 0;
|
|
||||||
// {
|
|
||||||
// let d = self.serial_port.fill_buf().unwrap();
|
|
||||||
|
|
||||||
// let mut packets = vec![];
|
|
||||||
// let mut packet = vec![];
|
|
||||||
|
|
||||||
// let mut bytes_taken = 0;
|
|
||||||
// let mut in_escape = 0;
|
|
||||||
// let mut checksum = 0;
|
|
||||||
// for b in d.iter() {
|
|
||||||
// bytes_taken += 1;
|
|
||||||
|
|
||||||
// match b {
|
|
||||||
// 0xff => {
|
|
||||||
// consumed += bytes_taken;
|
|
||||||
// bytes_taken = 0;
|
|
||||||
// in_escape = 0;
|
|
||||||
// }
|
|
||||||
// 0xfd => {
|
|
||||||
// in_escape = 1;
|
|
||||||
// }
|
|
||||||
// _ => {
|
|
||||||
// let b = b + in_escape;
|
|
||||||
// in_escape = 0;
|
|
||||||
// packet.push(b);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// self.serial_port.consume(consumed);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn send(&mut self) {}
|
|
||||||
// }
|
|
||||||
|
|
@ -9,7 +9,7 @@ mod config;
|
||||||
mod shared;
|
mod shared;
|
||||||
mod state;
|
mod state;
|
||||||
|
|
||||||
mod input;
|
mod device;
|
||||||
mod lighting;
|
mod lighting;
|
||||||
mod output;
|
mod output;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ pub enum ReactiveLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum LedMode {
|
pub enum LightsMode {
|
||||||
None,
|
None,
|
||||||
Reactive {
|
Reactive {
|
||||||
layout: ReactiveLayout,
|
layout: ReactiveLayout,
|
||||||
|
|
@ -23,32 +23,32 @@ pub enum LedMode {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LedMode {
|
impl LightsMode {
|
||||||
pub fn from_serde_value(v: &Value) -> Option<Self> {
|
pub fn from_serde_value(v: &Value) -> Option<Self> {
|
||||||
Some(match v["ledMode"].as_str()? {
|
Some(match v["ledMode"].as_str()? {
|
||||||
"none" => LedMode::None,
|
"none" => LightsMode::None,
|
||||||
"reactive-4" => LedMode::Reactive {
|
"reactive-4" => LightsMode::Reactive {
|
||||||
layout: ReactiveLayout::Even { splits: 4 },
|
layout: ReactiveLayout::Even { splits: 4 },
|
||||||
sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?,
|
sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?,
|
||||||
},
|
},
|
||||||
"reactive-8" => LedMode::Reactive {
|
"reactive-8" => LightsMode::Reactive {
|
||||||
layout: ReactiveLayout::Even { splits: 8 },
|
layout: ReactiveLayout::Even { splits: 8 },
|
||||||
sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?,
|
sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?,
|
||||||
},
|
},
|
||||||
"reactive-16" => LedMode::Reactive {
|
"reactive-16" => LightsMode::Reactive {
|
||||||
layout: ReactiveLayout::Even { splits: 16 },
|
layout: ReactiveLayout::Even { splits: 16 },
|
||||||
sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?,
|
sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?,
|
||||||
},
|
},
|
||||||
"reactive-voltex" => LedMode::Reactive {
|
"reactive-voltex" => LightsMode::Reactive {
|
||||||
layout: ReactiveLayout::Voltex,
|
layout: ReactiveLayout::Voltex,
|
||||||
sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?,
|
sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?,
|
||||||
},
|
},
|
||||||
"attract" => LedMode::Attract,
|
"attract" => LightsMode::Attract,
|
||||||
"test" => LedMode::Test,
|
"test" => LightsMode::Test,
|
||||||
"websocket" => LedMode::Websocket {
|
"websocket" => LightsMode::Websocket {
|
||||||
url: v["ledWebsocketUrl"].as_str()?.to_string(),
|
url: v["ledWebsocketUrl"].as_str()?.to_string(),
|
||||||
},
|
},
|
||||||
"serial" => LedMode::Serial {
|
"serial" => LightsMode::Serial {
|
||||||
port: v["ledSerialPort"].as_str()?.to_string(),
|
port: v["ledSerialPort"].as_str()?.to_string(),
|
||||||
},
|
},
|
||||||
_ => return None,
|
_ => return None,
|
||||||
|
|
|
||||||
|
|
@ -13,18 +13,18 @@ use crate::{
|
||||||
state::{SliderLights, SliderState},
|
state::{SliderLights, SliderState},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::config::{LedMode, ReactiveLayout};
|
use super::config::{LightsMode, ReactiveLayout};
|
||||||
|
|
||||||
pub struct LedJob {
|
pub struct LightsJob {
|
||||||
state: SliderState,
|
state: SliderState,
|
||||||
mode: LedMode,
|
mode: LightsMode,
|
||||||
serial_port: Option<Box<dyn SerialPort>>,
|
serial_port: Option<Box<dyn SerialPort>>,
|
||||||
started: Instant,
|
started: Instant,
|
||||||
timer: Interval,
|
timer: Interval,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LedJob {
|
impl LightsJob {
|
||||||
pub fn new(state: &SliderState, mode: &LedMode) -> Self {
|
pub fn new(state: &SliderState, mode: &LightsMode) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
mode: mode.clone(),
|
mode: mode.clone(),
|
||||||
|
|
@ -41,7 +41,7 @@ impl LedJob {
|
||||||
lights: &mut SliderLights,
|
lights: &mut SliderLights,
|
||||||
) {
|
) {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
LedMode::Reactive { layout, .. } => {
|
LightsMode::Reactive { layout, .. } => {
|
||||||
let flat_input = flat_input.unwrap();
|
let flat_input = flat_input.unwrap();
|
||||||
|
|
||||||
match layout {
|
match layout {
|
||||||
|
|
@ -116,7 +116,7 @@ impl LedJob {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LedMode::Attract => {
|
LightsMode::Attract => {
|
||||||
let theta = self
|
let theta = self
|
||||||
.started
|
.started
|
||||||
.elapsed()
|
.elapsed()
|
||||||
|
|
@ -128,7 +128,7 @@ impl LedJob {
|
||||||
lights.paint(idx, &[color.red, color.green, color.blue]);
|
lights.paint(idx, &[color.red, color.green, color.blue]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LedMode::Serial { .. } => {
|
LightsMode::Serial { .. } => {
|
||||||
// https://github.com/jmontineri/OpeNITHM/blob/89e9a43f7484e8949cd31bbff79c32f21ea3ec1d/Firmware/OpeNITHM/SerialProcessor.h
|
// 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/SerialProcessor.cpp
|
||||||
// https://github.com/jmontineri/OpeNITHM/blob/89e9a43f7484e8949cd31bbff79c32f21ea3ec1d/Firmware/OpeNITHM/SerialLeds.h
|
// https://github.com/jmontineri/OpeNITHM/blob/89e9a43f7484e8949cd31bbff79c32f21ea3ec1d/Firmware/OpeNITHM/SerialLeds.h
|
||||||
|
|
@ -155,10 +155,10 @@ impl LedJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AsyncJob for LedJob {
|
impl AsyncJob for LightsJob {
|
||||||
async fn setup(&mut self) -> bool {
|
async fn setup(&mut self) -> bool {
|
||||||
match &self.mode {
|
match &self.mode {
|
||||||
LedMode::Serial { port } => {
|
LightsMode::Serial { port } => {
|
||||||
info!(
|
info!(
|
||||||
"Serial port for led opening at {} {:?}",
|
"Serial port for led opening at {} {:?}",
|
||||||
port.as_str(),
|
port.as_str(),
|
||||||
|
|
@ -187,11 +187,11 @@ impl AsyncJob for LedJob {
|
||||||
|
|
||||||
// Do the IO here
|
// Do the IO here
|
||||||
match self.mode {
|
match self.mode {
|
||||||
LedMode::Reactive { sensitivity, .. } => {
|
LightsMode::Reactive { sensitivity, .. } => {
|
||||||
let input_handle = self.state.input.lock();
|
let input_handle = self.state.input.lock();
|
||||||
flat_input = Some(input_handle.to_flat(&sensitivity));
|
flat_input = Some(input_handle.to_flat(&sensitivity));
|
||||||
}
|
}
|
||||||
LedMode::Serial { .. } => {
|
LightsMode::Serial { .. } => {
|
||||||
if let Some(serial_port) = self.serial_port.as_mut() {
|
if let Some(serial_port) = self.serial_port.as_mut() {
|
||||||
let mut serial_data_avail = serial_port.bytes_to_read().unwrap_or(0);
|
let mut serial_data_avail = serial_port.bytes_to_read().unwrap_or(0);
|
||||||
if serial_data_avail >= 100 {
|
if serial_data_avail >= 100 {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user