better brokenithm

This commit is contained in:
4yn
2022-02-07 02:01:34 +08:00
parent fcf80538b4
commit cc24e628aa
20 changed files with 556 additions and 152 deletions

View File

@@ -9,7 +9,6 @@ mod slider_io;
use std::sync::{Arc, Mutex};
// use env_logger;
use log::info;
use tauri::{
@@ -32,9 +31,18 @@ fn quit_app() {
fn main() {
// Setup logger
let log_file_path = slider_io::Config::get_log_file_path().unwrap();
simple_logging::log_to_file(log_file_path.as_path(), log::LevelFilter::Debug).unwrap();
// simple_logging::log_to_file("./log.txt", log::LevelFilter::Debug).unwrap();
#[cfg(debug_assertions)]
env_logger::Builder::new()
.filter_level(log::LevelFilter::Debug)
.init();
#[cfg(not(debug_assertions))]
{
let log_file_path = slider_io::Config::get_log_file_path().unwrap();
simple_logging::log_to_file(log_file_path.as_path(), log::LevelFilter::Debug).unwrap();
// simple_logging::log_to_file("./log.txt", log::LevelFilter::Debug).unwrap();
}
let config = Arc::new(Mutex::new(Some(slider_io::Config::default())));
let manager = Arc::new(Mutex::new(slider_io::Manager::new()));
@@ -92,6 +100,22 @@ fn main() {
quit_app();
});
// Show logs
app.listen_global("openLogfile", |_| {
let log_file_path = slider_io::Config::get_log_file_path();
if let Some(log_file_path) = log_file_path {
open::that(log_file_path.as_path()).ok();
}
});
// Show brokenithm qr
app.listen_global("openBrokenithmQr", |_| {
let brokenithm_qr_path = slider_io::Config::get_brokenithm_qr_path();
if let Some(brokenithm_qr_path) = brokenithm_qr_path {
open::that(brokenithm_qr_path.as_path()).ok();
}
});
// UI ready event
let app_handle = app.handle();
let config_clone = Arc::clone(&config);
@@ -109,11 +133,6 @@ fn main() {
if let Ok(ips) = ips {
app_handle.emit_all("listIps", &ips).unwrap();
}
let log_file_path = slider_io::Config::get_log_file_path().unwrap();
app_handle
.emit_all("updateLogPath", log_file_path.as_path().to_str().unwrap())
.unwrap();
});
// UI update event

View File

@@ -10,7 +10,12 @@ use hyper::{
use log::{error, info};
use path_clean::PathClean;
use std::{convert::Infallible, env::current_exe, future::Future, net::SocketAddr};
use tokio::fs::File;
use tokio::{
fs::File,
select,
sync::mpsc,
time::{sleep, Duration},
};
use tokio_tungstenite::WebSocketStream;
use tokio_util::codec::{BytesCodec, FramedRead};
use tungstenite::{handshake, Message};
@@ -51,67 +56,111 @@ async fn serve_file(path: &str) -> Result<Response<Body>, Infallible> {
async fn handle_brokenithm(ws_stream: WebSocketStream<Upgraded>, state: FullState) {
let (mut ws_write, mut ws_read) = ws_stream.split();
loop {
match ws_read.next().await {
Some(msg) => match msg {
Ok(msg) => match msg {
Message::Text(msg) => {
let mut chars = msg.chars();
let head = chars.next().unwrap();
match head {
'a' => {
ws_write
.send(Message::Text("alive".to_string()))
.await
.unwrap();
}
'b' => {
let flat_state: Vec<bool> = chars
.map(|x| match x {
'0' => false,
'1' => true,
_ => unreachable!(),
})
.collect();
let mut controller_state_handle = state.controller_state.lock().unwrap();
for (idx, c) in flat_state[0..32].iter().enumerate() {
controller_state_handle.ground_state[idx] = match c {
false => 0,
true => 255,
}
}
for (idx, c) in flat_state[32..38].iter().enumerate() {
controller_state_handle.air_state[idx] = match c {
false => 0,
true => 1,
}
}
// println!(
// "{:?} {:?}",
// controller_state_handle.ground_state, controller_state_handle.air_state
// );
}
_ => {
break;
}
}
}
Message::Close(_) => {
info!("Websocket connection closed");
let (msg_write, mut msg_read) = mpsc::unbounded_channel::<Message>();
let write_task = async move {
// info!("Websocket write task open");
loop {
match msg_read.recv().await {
Some(msg) => match ws_write.send(msg).await.ok() {
Some(_) => {}
None => {
break;
}
_ => {}
},
Err(e) => {
error!("Websocket connection error: {}", e);
None => {
break;
}
},
None => {
break;
}
}
}
// info!("Websocket write task done");
};
let msg_write_handle = msg_write.clone();
let state_handle = state.clone();
let read_task = async move {
// info!("Websocket read task open");
loop {
match ws_read.next().await {
Some(msg) => match msg {
Ok(msg) => match msg {
Message::Text(msg) => {
let mut chars = msg.chars();
let head = chars.next().unwrap();
match head {
'a' => {
msg_write_handle
.send(Message::Text("alive".to_string()))
.ok();
}
'b' => {
let flat_state: Vec<bool> = chars
.map(|x| match x {
'0' => false,
'1' => true,
_ => unreachable!(),
})
.collect();
let mut controller_state_handle = state_handle.controller_state.lock().unwrap();
for (idx, c) in flat_state[0..32].iter().enumerate() {
controller_state_handle.ground_state[idx] = match c {
false => 0,
true => 255,
}
}
for (idx, c) in flat_state[32..38].iter().enumerate() {
controller_state_handle.air_state[idx] = match c {
false => 0,
true => 1,
}
}
}
_ => {
break;
}
}
}
Message::Close(_) => {
info!("Websocket connection closed");
break;
}
_ => {}
},
Err(e) => {
error!("Websocket connection error: {}", e);
break;
}
},
None => {
break;
}
}
}
// info!("Websocket read task done");
};
let msg_write_handle = msg_write.clone();
let state_handle = state.clone();
let led_task = async move {
loop {
let mut led_data = vec![0; 93];
{
let led_state_handle = state_handle.led_state.lock().unwrap();
(&mut led_data).copy_from_slice(&led_state_handle.led_state);
}
msg_write_handle.send(Message::Binary(led_data)).ok();
sleep(Duration::from_millis(50)).await;
}
};
info!("Websocket handling");
select! {
_ = read_task => {}
_ = write_task => {}
_ = led_task => {}
};
info!("Websocket done");
}
async fn handle_websocket(

View File

@@ -1,8 +1,12 @@
use directories::ProjectDirs;
use image::Luma;
use log::{info, warn};
use qrcode::QrCode;
use serde_json::Value;
use std::{convert::TryFrom, fs, path::PathBuf};
use crate::slider_io::utils::list_ips;
#[derive(Debug, Clone)]
pub enum DeviceMode {
None,
@@ -12,6 +16,38 @@ pub enum DeviceMode {
Brokenithm { ground_only: bool },
}
#[derive(Debug, Clone, Copy)]
pub enum OutputPolling {
Sixty,
Hundred,
ThreeHundred,
FiveHundred,
Thousand,
}
impl OutputPolling {
pub fn from_str(s: &str) -> Option<Self> {
match s {
"60" => Some(OutputPolling::Sixty),
"100" => Some(OutputPolling::Hundred),
"330" => Some(OutputPolling::ThreeHundred),
"500" => Some(OutputPolling::FiveHundred),
"1000" => Some(OutputPolling::Thousand),
_ => None,
}
}
pub fn to_t_u64(&self) -> u64 {
match self {
OutputPolling::Sixty => 16,
OutputPolling::Hundred => 10,
OutputPolling::ThreeHundred => 3,
OutputPolling::FiveHundred => 2,
OutputPolling::Thousand => 1,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum KeyboardLayout {
Tasoller,
@@ -31,14 +67,17 @@ pub enum OutputMode {
None,
Keyboard {
layout: KeyboardLayout,
polling: OutputPolling,
sensitivity: u8,
},
Gamepad {
layout: GamepadLayout,
polling: OutputPolling,
sensitivity: u8,
},
Websocket {
url: String,
polling: OutputPolling,
},
}
@@ -92,30 +131,37 @@ impl Config {
"none" => OutputMode::None,
"kb-32-tasoller" => OutputMode::Keyboard {
layout: KeyboardLayout::Tasoller,
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
},
"kb-32-yuancon" => OutputMode::Keyboard {
layout: KeyboardLayout::Yuancon,
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
},
"kb-8-deemo" => OutputMode::Keyboard {
layout: KeyboardLayout::Deemo,
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
},
"kb-voltex" => OutputMode::Keyboard {
layout: KeyboardLayout::Voltex,
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
},
"gamepad-voltex" => OutputMode::Gamepad {
layout: GamepadLayout::Voltex,
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
},
"gamepad-neardayo" => OutputMode::Gamepad {
layout: GamepadLayout::Neardayo,
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
},
"websocket" => OutputMode::Websocket {
url: v["outputWebsocketUrl"].as_str()?.to_string(),
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
},
_ => panic!("Invalid output mode"),
},
@@ -158,6 +204,7 @@ impl Config {
"ledMode": "none",
"keyboardSensitivity": 20,
"outputWebsocketUrl": "localhost:3000",
"outputPolling": "60",
"ledSensitivity": 20,
"ledWebsocketUrl": "localhost:3001",
"ledSerialPort": "COM5"
@@ -176,6 +223,28 @@ impl Config {
return Some(Box::new(log_path));
}
pub fn get_brokenithm_qr_path() -> Option<Box<PathBuf>> {
let project_dir = ProjectDirs::from("me", "impress labs", "slidershim").unwrap();
let config_dir = project_dir.config_dir();
fs::create_dir_all(config_dir).unwrap();
let brokenithm_qr_path = config_dir.join("brokenithm.png");
let ips = list_ips().ok()?;
let link = "http://imp.ress.me/t/sshelper?d=".to_string()
+ &ips
.into_iter()
.filter(|s| s.as_str().chars().filter(|x| *x == '.').count() == 3)
.map(|s| base64::encode_config(s, base64::URL_SAFE_NO_PAD))
.collect::<Vec<String>>()
.join(";");
let qr = QrCode::new(link).ok()?;
let image = qr.render::<Luma<u8>>().build();
image.save(brokenithm_qr_path.as_path()).ok()?;
return Some(Box::new(brokenithm_qr_path));
}
fn get_saved_path() -> Option<Box<PathBuf>> {
let project_dir = ProjectDirs::from("me", "impress labs", "slidershim").unwrap();
let config_dir = project_dir.config_dir();

View File

@@ -102,8 +102,8 @@ impl HidDeviceJob {
.zip(led_state.led_state.chunks(3).rev())
{
buf_chunk[0] = state_chunk[2];
buf_chunk[1] = state_chunk[1];
buf_chunk[2] = state_chunk[0];
buf_chunk[1] = state_chunk[0];
buf_chunk[2] = state_chunk[1];
}
buf.data[96..240].fill(0);
},
@@ -140,8 +140,8 @@ impl HidDeviceJob {
.zip(led_state.led_state.chunks(3).rev())
{
buf_chunk[0] = state_chunk[2];
buf_chunk[1] = state_chunk[1];
buf_chunk[2] = state_chunk[0];
buf_chunk[1] = state_chunk[0];
buf_chunk[2] = state_chunk[1];
}
buf.data[96..240].fill(0);
},

View File

@@ -137,7 +137,7 @@ impl LedJob {
{
led_state.paint(idx, &[(*buf_chunk)[1], (*buf_chunk)[2], (*buf_chunk)[0]]);
}
println!("leds {:?}", led_state.led_state);
// println!("leds {:?}", led_state.led_state);
}
}
}
@@ -187,23 +187,21 @@ impl ThreadJob for LedJob {
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 {
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 % 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).unwrap();
if serial_data_avail > 0 {
serial_port.clear(ClearBuffer::All).unwrap();
}
}
}
}

View File

@@ -33,7 +33,7 @@ impl Manager {
let join_handle = thread::spawn(move || {
info!("Manager thread started");
let runtime = tokio::runtime::Builder::new_multi_thread()
.worker_threads(1)
.worker_threads(2)
.enable_all()
.build()
.unwrap();

View File

@@ -12,6 +12,7 @@ pub trait OutputHandler: Send {
pub struct OutputJob {
state: FullState,
t: u64,
sensitivity: u8,
handler: Box<dyn OutputHandler>,
}
@@ -21,17 +22,21 @@ impl OutputJob {
match mode {
OutputMode::Keyboard {
layout,
polling,
sensitivity,
} => Self {
state: state.clone(),
t: polling.to_t_u64(),
sensitivity: *sensitivity,
handler: Box::new(KeyboardOutput::new(layout.clone())),
},
OutputMode::Gamepad {
layout,
polling,
sensitivity,
} => Self {
state: state.clone(),
t: polling.to_t_u64(),
sensitivity: *sensitivity,
handler: Box::new(GamepadOutput::new(layout.clone())),
},
@@ -53,7 +58,7 @@ impl ThreadJob for OutputJob {
}
self.handler.tick(&flat_controller_state);
thread::sleep(Duration::from_millis(10));
thread::sleep(Duration::from_millis(self.t));
}
fn teardown(&mut self) {