mirror of
https://github.com/4yn/slidershim.git
synced 2024-11-12 00:40:49 +01:00
some error handling
This commit is contained in:
parent
6f052554ce
commit
afa5a46c65
@ -3,7 +3,7 @@
|
||||
- major
|
||||
- acio support
|
||||
- qol
|
||||
- proper error handling
|
||||
- more error handling
|
||||
- comments
|
||||
- when umiguri comes out
|
||||
- ouptut websocket
|
||||
|
@ -41,10 +41,9 @@ fn main() {
|
||||
{
|
||||
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 config = Arc::new(Mutex::new(Some(slider_io::Config::load())));
|
||||
let manager = Arc::new(Mutex::new(slider_io::Manager::new()));
|
||||
{
|
||||
let config_handle = config.lock().unwrap();
|
||||
|
@ -46,24 +46,6 @@ static BROKENITHM_BIN_FILES: phf::Map<&'static str, (&'static [u8], &'static str
|
||||
};
|
||||
|
||||
async fn serve_file(path: &str) -> Result<Response<Body>, Infallible> {
|
||||
// let mut pb = current_exe().unwrap();
|
||||
// pb.pop();
|
||||
// pb.push("res/www");
|
||||
// pb.push(path);
|
||||
// pb.clean();
|
||||
|
||||
// println!("CWD {:?}", std::env::current_dir());
|
||||
|
||||
// match File::open(&pb).await {
|
||||
// Ok(f) => {
|
||||
// info!("Serving file {:?}", pb);
|
||||
// let stream = FramedRead::new(f, BytesCodec::new());
|
||||
// let body = Body::wrap_stream(stream);
|
||||
// Ok(Response::new(body))
|
||||
// }
|
||||
// Err(_) => error_response().await,
|
||||
// }
|
||||
|
||||
match (
|
||||
BROKENITHM_STR_FILES.get(path),
|
||||
BROKENITHM_BIN_FILES.get(path),
|
||||
@ -120,33 +102,30 @@ async fn handle_brokenithm(
|
||||
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,
|
||||
}
|
||||
let chars = msg.chars().collect::<Vec<char>>();
|
||||
|
||||
match chars.len() {
|
||||
6 => {
|
||||
if chars[0] == 'a' {
|
||||
msg_write_handle
|
||||
.send(Message::Text("alive".to_string()))
|
||||
.ok();
|
||||
}
|
||||
for (idx, c) in flat_state[32..38].iter().enumerate() {
|
||||
controller_state_handle.air_state[idx] = match c {
|
||||
false => 0,
|
||||
true => 1,
|
||||
}
|
||||
39 => {
|
||||
if chars[0] == 'b' {
|
||||
let mut controller_state_handle = state_handle.controller_state.lock().unwrap();
|
||||
for (idx, c) in chars[0..32].iter().enumerate() {
|
||||
controller_state_handle.ground_state[idx] = match *c == '1' {
|
||||
false => 0,
|
||||
true => 255,
|
||||
}
|
||||
}
|
||||
for (idx, c) in chars[32..38].iter().enumerate() {
|
||||
controller_state_handle.air_state[idx] = match *c == '1' {
|
||||
false => 0,
|
||||
true => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -213,27 +213,10 @@ impl Config {
|
||||
})
|
||||
}
|
||||
|
||||
fn factory() -> Self {
|
||||
Self::from_str(
|
||||
r#"{
|
||||
"deviceMode": "none",
|
||||
"outputMode": "none",
|
||||
"ledMode": "none",
|
||||
"keyboardSensitivity": 20,
|
||||
"outputWebsocketUrl": "localhost:3000",
|
||||
"outputPolling": "60",
|
||||
"ledSensitivity": 20,
|
||||
"ledWebsocketUrl": "localhost:3001",
|
||||
"ledSerialPort": "COM5"
|
||||
}"#,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn get_log_file_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();
|
||||
fs::create_dir_all(config_dir).ok()?;
|
||||
|
||||
let log_path = config_dir.join("log.txt");
|
||||
|
||||
@ -243,7 +226,7 @@ impl Config {
|
||||
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();
|
||||
fs::create_dir_all(config_dir).ok()?;
|
||||
|
||||
let brokenithm_qr_path = config_dir.join("brokenithm.png");
|
||||
|
||||
@ -262,18 +245,35 @@ impl Config {
|
||||
return Some(Box::new(brokenithm_qr_path));
|
||||
}
|
||||
|
||||
fn get_saved_path() -> Option<Box<PathBuf>> {
|
||||
fn get_config_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();
|
||||
fs::create_dir_all(config_dir).ok()?;
|
||||
|
||||
let config_path = config_dir.join("config.json");
|
||||
|
||||
return Some(Box::new(config_path));
|
||||
}
|
||||
|
||||
fn default() -> Self {
|
||||
Self::from_str(
|
||||
r#"{
|
||||
"deviceMode": "none",
|
||||
"outputMode": "none",
|
||||
"ledMode": "none",
|
||||
"keyboardSensitivity": 20,
|
||||
"outputWebsocketUrl": "localhost:3000",
|
||||
"outputPolling": "60",
|
||||
"ledSensitivity": 20,
|
||||
"ledWebsocketUrl": "localhost:3001",
|
||||
"ledSerialPort": "COM5"
|
||||
}"#,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn load_saved() -> Option<Self> {
|
||||
let config_path = Self::get_saved_path()?;
|
||||
let config_path = Self::get_config_path()?;
|
||||
if !config_path.exists() {
|
||||
return None;
|
||||
}
|
||||
@ -282,22 +282,22 @@ impl Config {
|
||||
return Self::from_str(saved_data.as_str());
|
||||
}
|
||||
|
||||
pub fn default() -> Self {
|
||||
pub fn load() -> Self {
|
||||
Self::load_saved()
|
||||
.or_else(|| {
|
||||
warn!("Config loading from file failed, using default");
|
||||
Some(Self::factory())
|
||||
Some(Self::default())
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn save(&self) -> Option<()> {
|
||||
info!("Config saving...");
|
||||
let config_path = Self::get_saved_path()?;
|
||||
let config_path = Self::get_config_path()?;
|
||||
info!("Config saving to {:?}", config_path);
|
||||
fs::write(config_path.as_path(), self.raw.as_str()).unwrap();
|
||||
|
||||
info!("Config saved");
|
||||
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ impl HidDeviceJob {
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_impl(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
fn get_handle(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
info!("Device finding vid {} pid {}", self.vid, self.pid);
|
||||
let handle = rusb::open_device_with_vid_pid(self.vid, self.pid);
|
||||
if handle.is_none() {
|
||||
@ -212,7 +212,7 @@ const TIMEOUT: Duration = Duration::from_millis(20);
|
||||
|
||||
impl ThreadJob for HidDeviceJob {
|
||||
fn setup(&mut self) -> bool {
|
||||
match self.setup_impl() {
|
||||
match self.get_handle() {
|
||||
Ok(_) => {
|
||||
info!("Device OK");
|
||||
true
|
||||
@ -279,8 +279,7 @@ impl ThreadJob for HidDeviceJob {
|
||||
}
|
||||
|
||||
fn teardown(&mut self) {
|
||||
if self.handle.is_some() {
|
||||
let handle = self.handle.as_mut().unwrap();
|
||||
if let Some(handle) = self.handle.as_mut() {
|
||||
handle.release_interface(0).ok();
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use log::error;
|
||||
use std::error::Error;
|
||||
use vigem_client::{Client, TargetId, XButtons, XGamepad, Xbox360Wired};
|
||||
|
||||
use crate::slider_io::{config::GamepadLayout, output::OutputHandler, voltex::VoltexState};
|
||||
@ -9,25 +11,49 @@ pub struct GamepadOutput {
|
||||
}
|
||||
|
||||
impl GamepadOutput {
|
||||
pub fn new(layout: GamepadLayout) -> Self {
|
||||
let client = Client::connect().unwrap();
|
||||
pub fn new(layout: GamepadLayout) -> Option<Self> {
|
||||
let target = Self::get_target();
|
||||
let use_air = match layout {
|
||||
GamepadLayout::Neardayo => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
match target {
|
||||
Ok(target) => Some(Self {
|
||||
target,
|
||||
use_air,
|
||||
gamepad: XGamepad::default(),
|
||||
}),
|
||||
Err(e) => {
|
||||
error!("Gamepad connection error: {}", e);
|
||||
error!("Gamepad connection error: Is ViGEMBus missing?");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_target() -> Result<Xbox360Wired<Client>, Box<dyn Error>> {
|
||||
let client = Client::connect()?;
|
||||
|
||||
let mut target = Xbox360Wired::new(client, TargetId::XBOX360_WIRED);
|
||||
target.plugin().unwrap();
|
||||
target.wait_ready().unwrap();
|
||||
Self {
|
||||
target,
|
||||
use_air,
|
||||
gamepad: XGamepad::default(),
|
||||
target.plugin()?;
|
||||
target.wait_ready()?;
|
||||
Ok(target)
|
||||
}
|
||||
|
||||
fn update(&mut self) -> bool {
|
||||
match self.target.update(&self.gamepad) {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
error!("Gamepad update error: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputHandler for GamepadOutput {
|
||||
fn tick(&mut self, flat_controller_state: &Vec<bool>) {
|
||||
fn tick(&mut self, flat_controller_state: &Vec<bool>) -> bool {
|
||||
let voltex_state = VoltexState::from_flat(flat_controller_state);
|
||||
|
||||
let buttons = voltex_state
|
||||
@ -84,20 +110,26 @@ impl OutputHandler for GamepadOutput {
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if dirty {
|
||||
self.target.update(&self.gamepad).unwrap();
|
||||
match dirty {
|
||||
true => self.update(),
|
||||
false => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.gamepad = XGamepad::default();
|
||||
self.target.update(&self.gamepad).unwrap();
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GamepadOutput {
|
||||
fn drop(&mut self) {
|
||||
self.target.unplug().unwrap();
|
||||
match self.target.unplug() {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("Gamepad unplug error: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ impl KeyboardOutput {
|
||||
}
|
||||
|
||||
impl OutputHandler for KeyboardOutput {
|
||||
fn tick(&mut self, flat_controller_state: &Vec<bool>) {
|
||||
fn tick(&mut self, flat_controller_state: &Vec<bool>) -> bool {
|
||||
self.next_keys.fill(false);
|
||||
for (idx, x) in flat_controller_state.iter().enumerate() {
|
||||
if *x {
|
||||
@ -173,6 +173,7 @@ impl OutputHandler for KeyboardOutput {
|
||||
}
|
||||
}
|
||||
self.send();
|
||||
true
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
|
@ -98,7 +98,11 @@ impl Manager {
|
||||
|
||||
impl Drop for Manager {
|
||||
fn drop(&mut self) {
|
||||
self.tx_stop.take().unwrap().send(()).unwrap();
|
||||
self.join_handle.take().unwrap().join().unwrap();
|
||||
if let Some(tx_stop) = self.tx_stop.take() {
|
||||
tx_stop.send(()).ok();
|
||||
}
|
||||
if let Some(join_handle) = self.join_handle.take() {
|
||||
join_handle.join().ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use log::error;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::slider_io::{
|
||||
@ -6,48 +7,65 @@ use crate::slider_io::{
|
||||
};
|
||||
|
||||
pub trait OutputHandler: Send {
|
||||
fn tick(&mut self, flat_controller_state: &Vec<bool>);
|
||||
fn tick(&mut self, flat_controller_state: &Vec<bool>) -> bool;
|
||||
fn reset(&mut self);
|
||||
}
|
||||
|
||||
pub struct OutputJob {
|
||||
state: FullState,
|
||||
mode: OutputMode,
|
||||
t: u64,
|
||||
sensitivity: u8,
|
||||
handler: Box<dyn OutputHandler>,
|
||||
handler: Option<Box<dyn OutputHandler>>,
|
||||
}
|
||||
|
||||
impl OutputJob {
|
||||
pub fn new(state: &FullState, mode: &OutputMode) -> Self {
|
||||
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())),
|
||||
},
|
||||
_ => panic!("Not implemented"),
|
||||
Self {
|
||||
state: state.clone(),
|
||||
mode: mode.clone(),
|
||||
t: 0,
|
||||
sensitivity: 0,
|
||||
handler: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ThreadJob for OutputJob {
|
||||
fn setup(&mut self) -> bool {
|
||||
true
|
||||
match self.mode {
|
||||
OutputMode::Keyboard {
|
||||
layout,
|
||||
polling,
|
||||
sensitivity,
|
||||
} => {
|
||||
self.t = polling.to_t_u64();
|
||||
self.sensitivity = sensitivity;
|
||||
self.handler = Some(Box::new(KeyboardOutput::new(layout.clone())));
|
||||
|
||||
true
|
||||
}
|
||||
OutputMode::Gamepad {
|
||||
layout,
|
||||
polling,
|
||||
sensitivity,
|
||||
} => {
|
||||
self.t = polling.to_t_u64();
|
||||
self.sensitivity = sensitivity;
|
||||
let handler = GamepadOutput::new(layout.clone());
|
||||
match handler {
|
||||
Some(handler) => {
|
||||
self.handler = Some(Box::new(handler));
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("Not implemented");
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tick(&mut self) -> bool {
|
||||
@ -57,14 +75,17 @@ impl ThreadJob for OutputJob {
|
||||
flat_controller_state = controller_state_handle.flat(&self.sensitivity);
|
||||
}
|
||||
|
||||
self.handler.tick(&flat_controller_state);
|
||||
// thread::sleep(Duration::from_millis(self.t));
|
||||
if let Some(handler) = self.handler.as_mut() {
|
||||
handler.tick(&flat_controller_state);
|
||||
}
|
||||
spin_sleep::sleep(Duration::from_micros(self.t));
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn teardown(&mut self) {
|
||||
self.handler.reset();
|
||||
if let Some(handler) = self.handler.as_mut() {
|
||||
handler.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,9 +84,11 @@ impl LoopTimer {
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn reset(&mut self) {
|
||||
// self.buf = vec![Instant::now(); 100];
|
||||
// }
|
||||
#[allow(dead_code)]
|
||||
pub fn reset(&mut self) {
|
||||
self.buf = vec![Instant::now() - Duration::from_secs(10); 100];
|
||||
self.cur = 0;
|
||||
}
|
||||
|
||||
pub fn fork(&self) -> Arc<AtomicF64> {
|
||||
Arc::clone(&self.freq)
|
||||
|
@ -59,9 +59,9 @@ impl Drop for ThreadWorker {
|
||||
info!("Thread worker stopping {}", self.name);
|
||||
|
||||
self.stop_signal.store(true, Ordering::SeqCst);
|
||||
if self.thread.is_some() {
|
||||
self.thread.take().unwrap().join().ok();
|
||||
}
|
||||
if let Some(thread) = self.thread.take() {
|
||||
thread.join().ok();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,7 +88,7 @@ impl AsyncWorker {
|
||||
let task = tokio::spawn(async move {
|
||||
job
|
||||
.run(async move {
|
||||
recv_stop.await.unwrap();
|
||||
recv_stop.await.ok();
|
||||
})
|
||||
.await;
|
||||
});
|
||||
@ -105,7 +105,9 @@ impl Drop for AsyncWorker {
|
||||
fn drop(&mut self) {
|
||||
info!("Async worker stopping {}", self.name);
|
||||
|
||||
self.stop_signal.take().unwrap().send(()).unwrap();
|
||||
if let Some(stop_signal) = self.stop_signal.take() {
|
||||
stop_signal.send(()).ok();
|
||||
}
|
||||
self.task.take();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user