diff --git a/Cargo.toml b/Cargo.toml index f2250d5..5a6cfe2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,4 @@ imageproc = "0.23.0" rusttype = "0.9.3" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } -phf = { version = "0.11", features = ["macros"] } +lazy_static = "1.4.0" diff --git a/README.md b/README.md index 9515509..513172c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ ```diff - WORK IN PROGRESS -``` # dach-decker diff --git a/src/main.rs b/src/main.rs index ffd3689..1e38530 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ use tokio::sync::mpsc; use tokio::sync::mpsc::error::TrySendError; use tokio::sync::Mutex; use tokio::task::JoinHandle; -use tracing::{debug, error, info, trace, warn}; +use tracing::{debug, error, info, trace, warn, info_span, Level}; mod modules; use elgato_streamdeck as streamdeck; use streamdeck::asynchronous::{AsyncStreamDeck, ButtonStateUpdate}; @@ -105,7 +105,16 @@ fn main() { } pub async fn start(config: Config, hid: HidApi, hw_devices: Vec<(streamdeck::info::Kind, String)>) { - init_devices(config, hid, hw_devices).await; + let devices = init_devices(config, hid, hw_devices).await; + + let mut handles: Vec> = Vec::new(); + + // start listeners + for device in devices { + handles.push(tokio::spawn(async move { + device.key_listener().await + })) + } loop { tokio::time::sleep(Duration::from_secs(2000)).await; @@ -124,6 +133,7 @@ pub async fn send_to_channel(sender: mpsc::Sender, event: HostEvent) true } +/// Handles everything related to a single device pub struct DeviceManager { modules: HashMap, mpsc::Sender)>, device: Arc, @@ -144,7 +154,6 @@ impl DeviceManager { } /// stops all modules of the device - #[tracing::instrument(skip_all, fields(serial = self.serial))] fn shutdown(self) { for (index, (_, handle, _)) in self.modules { trace!("Destroying module {}", index); @@ -195,15 +204,17 @@ impl DeviceManager { } /// This is the entry point for the application. This will check all devices for their config, -/// start the bridges and the device button listeners. -async fn init_devices(config: Config, hid: HidApi, devices: Vec<(streamdeck::info::Kind, String)>) { +/// start the modules and the device button listeners. +async fn init_devices(config: Config, hid: HidApi, devices: Vec<(streamdeck::info::Kind, String)>) -> Vec { // check if configuration is correct for device if devices.len() == 0 { error!("There are no Decks connected"); exit(1); } info!("There are {} Decks connected", devices.len()); - 'outer: for device in devices { + let mut device_managers = Vec::new(); + 'device: for device in devices { + let _span_device = info_span!("device", serial = device.1).entered(); // no pedals are supported if !device.0.is_visual() { continue; @@ -213,12 +224,12 @@ async fn init_devices(config: Config, hid: HidApi, devices: Vec<(streamdeck::inf // connect to deck or continue to next let deck = match AsyncStreamDeck::connect(&hid, device.0, &device.1) { Ok(deck) => { - info!("Successfully connected to {}", device.1); + info!("Successfully connected"); deck } Err(e) => { - error!("Cannot connect to Deck {}: {}", device.1, e); - continue 'outer; + error!("Cannot connect: {}", e); + continue 'device; } }; // set brightness @@ -227,15 +238,21 @@ async fn init_devices(config: Config, hid: HidApi, devices: Vec<(streamdeck::inf deck.reset().await.unwrap(); // initialize buttons let button_count = device.0.key_count(); + // save button senders let mut buttons_keys = HashMap::new(); for button in device_conf.buttons.clone().into_iter() { + let _span_button = info_span!("button", index=button.index).entered(); + if buttons_keys.get(&button.index).is_some() { + warn!("The button is configured twice"); + continue + } // if the index of the button is higher than the button count if button_count < button.index { warn!( "The button {} does not exist on Deck {}; skipping", button.index, device.1 ); - continue 'outer; + continue 'device; } // create a channel for the module to receive device events let (button_sender, button_receiver) = mpsc::channel(4); @@ -254,15 +271,12 @@ async fn init_devices(config: Config, hid: HidApi, devices: Vec<(streamdeck::inf warn!("The module \"{}\" does not exist.", button.module) } } - let manager = DeviceManager::new(device.1, deck, buttons_keys).await; - // start the device key listener - tokio::spawn(async move { - manager.key_listener().await; - }); + device_managers.push(DeviceManager::new(device.1, deck, buttons_keys).await); } else { info!("Deck {} is not configured; skipping", device.1); } } + device_managers } pub async fn execute_sh(command: &str) { diff --git a/src/modules.rs b/src/modules.rs index 1f49585..77b71cc 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -1,25 +1,33 @@ mod blank; mod counter; +use std::collections::HashMap; use std::pin::Pin; use std::{error::Error, sync::Arc}; -use self::blank::Blank; use self::counter::Counter; use crate::Button; use async_trait::async_trait; pub use elgato_streamdeck as streamdeck; use futures_util::Future; use image::DynamicImage; -use phf::phf_map; pub use streamdeck::info::ImageFormat; use streamdeck::info::Kind; use streamdeck::AsyncStreamDeck; pub use streamdeck::StreamDeckError; use tokio::sync::mpsc; use tokio::sync::Mutex; +use lazy_static::lazy_static; use tracing::{debug, error, info}; +lazy_static! { + static ref MODULE_MAP: HashMap<&'static str, ModuleFunction> = { + let mut m = HashMap::new(); + m.insert("counter", Counter::run as ModuleFunction); + m + }; +} + /// Events that are coming from the host #[derive(Clone, Copy, Debug)] pub enum HostEvent { @@ -27,22 +35,17 @@ pub enum HostEvent { ButtonPressed, /// The button was released ButtonReleased, - /// The channel was initialized and there were no events yet - Init, } type ModuleFuture = Pin> + Send>>; type ModuleFunction = fn(DeviceAccess, ChannelReceiver, Button) -> ModuleFuture; pub fn retrieve_module_from_name(name: String) -> Option { - match name.as_str() { - "counter" => Some(Counter::run), - _ => None, - } + MODULE_MAP.get(name.as_str()).copied() } /// starts a module -#[tracing::instrument(skip_all, fields(device = serial, button = button.index, module = button.module))] +#[tracing::instrument(name = "module", skip_all, fields(serial = serial, button = button.index, module = button.module))] pub async fn start_module( // Just for logging purpose serial: String,