memory improvements

This commit is contained in:
Fl1tzi 2023-05-20 00:54:25 +02:00
parent d216b86354
commit 366b08c850
6 changed files with 87 additions and 75 deletions

View file

@ -11,7 +11,7 @@ toml = "0.7.2"
tokio = { version = "1", features = ["full"] }
log = "0.4"
dirs = "4.0.0"
serde = { version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive", "rc"] }
image = "0.24.5"
async-trait = "0.1.66"
futures-util = "0.3.27"

View file

@ -1,13 +1,14 @@
use crate::{
modules::{retrieve_module_from_name, start_module, HostEvent},
Button, ConfigError, DeviceConfig,
skip_if_none, unwrap_or_error
};
use deck_driver as streamdeck;
use hidapi::HidApi;
use std::{
collections::HashMap,
fmt::{self, Display},
sync::Arc,
fmt::Display,
sync::Arc, cell::Cell,
};
use streamdeck::{
asynchronous::{AsyncStreamDeck, ButtonStateUpdate},
@ -16,24 +17,12 @@ use streamdeck::{
};
use tokio::{
process::Command,
sync::{
mpsc::{self, error::TrySendError},
Mutex,
},
task::JoinHandle,
sync::mpsc::{self, error::TrySendError},
runtime::Runtime,
};
use tracing::{debug, error, info_span, trace};
macro_rules! skip_if_none {
($res:expr) => {
match $res {
Some(v) => v,
None => continue,
}
};
}
pub type ModuleController = (Button, JoinHandle<()>, mpsc::Sender<HostEvent>);
pub type ModuleController = (Arc<Button>, mpsc::Sender<HostEvent>);
pub enum DeviceError {
DriverError(StreamDeckError),
@ -53,7 +42,8 @@ impl Display for DeviceError {
pub struct Device {
modules: HashMap<u8, ModuleController>,
device: Arc<AsyncStreamDeck>,
is_dropped: bool,
modules_runtime: Option<Runtime>,
config: DeviceConfig,
serial: String,
}
@ -61,7 +51,7 @@ impl Device {
pub async fn new(
serial: String,
kind: Kind,
device_conf: &DeviceConfig,
device_conf: DeviceConfig,
hid: &HidApi,
) -> Result<Device, DeviceError> {
// connect to deck or continue to next
@ -89,23 +79,38 @@ impl Device {
Ok(Device {
modules: HashMap::new(),
device: deck,
is_dropped: false,
modules_runtime: None,
config: device_conf,
serial,
})
}
pub fn create_module(&mut self, btn: &Button) -> Result<(), DeviceError> {
pub async fn init_modules(&mut self) {
if self.modules_runtime.is_none() {
self.modules_runtime = Some(Runtime::new().unwrap());
}
for i in 0..self.config.buttons.len() {
let button = self.config.buttons.get(i).unwrap().to_owned();
unwrap_or_error!(self._create_module(button).await);
}
}
async fn _create_module(&mut self, btn: Arc<Button>) -> Result<(), DeviceError> {
let runtime = self.modules_runtime.as_ref().expect("Runtime has to be created before module can be spawned");
let (button_sender, button_receiver) = mpsc::channel(4);
if let Some(module) = retrieve_module_from_name(btn.module.clone()) {
let b = btn.clone();
let ser = self.serial.clone();
let rx = Arc::new(Mutex::new(button_receiver));
let dev = self.device.clone();
let handle = tokio::spawn(async move {
start_module(ser, b, module, dev, rx).await;
});
if let Some(module) = retrieve_module_from_name(&btn.module) {
{
let ser = self.serial.clone();
let dev = self.device.clone();
let b = btn.clone();
runtime.spawn(async move {
start_module(ser, b, module, dev, Cell::new(button_receiver)).await
});
}
self.modules
.insert(btn.index, (btn.clone(), handle, button_sender));
.insert(btn.index, (btn.clone(), button_sender));
return Ok(());
} else {
return Err(DeviceError::Config(ConfigError::ModuleDoesNotExist(
@ -119,20 +124,14 @@ impl Device {
self.serial.clone()
}
/// stops all modules of the device
fn stop_all_modules(&self) {
for (index, (_, handle, _)) in self.modules.iter() {
trace!("Destroying module {}", index);
handle.abort();
fn drop(&mut self) {
if let Some(handle) = self.modules_runtime.take() {
handle.shutdown_background();
}
}
fn drop(&mut self) {
self.is_dropped = true
}
pub fn is_dropped(&self) -> bool {
self.is_dropped
self.modules_runtime.is_none()
}
pub fn has_modules(&self) -> bool {
@ -153,7 +152,7 @@ impl Device {
if let Some(on_click) = &options.0.on_click {
execute_sh(on_click).await;
} else {
send_to_channel(&options.2, HostEvent::ButtonPressed).await;
send_to_channel(&options.1, HostEvent::ButtonPressed).await;
}
}
ButtonStateUpdate::ButtonUp(i) => {
@ -161,7 +160,7 @@ impl Device {
if let Some(on_release) = &options.0.on_release {
execute_sh(on_release).await;
} else {
send_to_channel(&options.2, HostEvent::ButtonReleased).await;
send_to_channel(&options.1, HostEvent::ButtonReleased).await;
}
}
}
@ -170,7 +169,6 @@ impl Device {
Err(e) => match e {
StreamDeckError::HidError(e) => {
error!("Shutting down device because of: {e}");
self.stop_all_modules();
self.drop();
break;
}

View file

@ -1,5 +1,5 @@
use deck_driver as streamdeck;
use device::{Device, DeviceError};
use device::Device;
use hidapi::HidApi;
use serde::Deserialize;
use std::{
@ -11,6 +11,7 @@ use std::{
path::PathBuf,
process::exit,
time::Duration,
sync::Arc
};
use tracing::{debug, error, info, warn};
use tracing_subscriber::{
@ -23,7 +24,27 @@ mod device;
mod modules;
/// The name of the folder which holds the config
pub const CONFIG_FOLDER_NAME: &'static str = "dach-decker";
pub const CONFIG_FOLDER_NAME: &'static str = "virtual-deck";
#[macro_export]
macro_rules! skip_if_none {
($res:expr) => {
match $res {
Some(v) => v,
None => continue,
}
};
}
#[macro_export]
macro_rules! unwrap_or_error {
($res:expr) => {
match $res {
Ok(v) => v,
Err(e) => error!("{}", e)
}
};
}
/// The config structure
#[derive(Deserialize, Debug)]
@ -83,15 +104,7 @@ fn main() {
};
debug!("{:#?}", config);
// ------ START APPLICATION
let hid = match streamdeck::new_hidapi() {
Ok(v) => v,
Err(e) => {
error!("HidApi Error:\n{}", e);
exit(1);
}
};
let hid = streamdeck::new_hidapi().expect("Could not create HidApi");
// lets start some async
tokio::runtime::Builder::new_multi_thread()
.enable_all()
@ -130,7 +143,7 @@ pub async fn start(config: Config, mut hid: HidApi) {
config.device.iter().find(|d| d.serial == hw_device.1)
{
// start the device and its listener
if let Some(device) = start_device(hw_device, &hid, device_config).await
if let Some(device) = start_device(hw_device, &hid, device_config.clone()).await
{
devices.insert(device.serial(), device);
}
@ -151,17 +164,19 @@ pub async fn start(config: Config, mut hid: HidApi) {
pub async fn start_device(
device: (streamdeck::info::Kind, String),
hid: &HidApi,
device_config: &DeviceConfig,
device_config: DeviceConfig,
) -> Option<Device> {
match Device::new(device.1, device.0, device_config, &hid).await {
Ok(mut device) => {
info!("Connected");
// start all modules or print out the error
device_config.buttons.iter().for_each(|button| {
/* device_config.buttons.iter().for_each(|button| {
device
.create_module(&button)
.unwrap_or_else(|e| error!("{}", e))
});
});*/
device.init_modules().await;
device.key_listener().await;
if !device.has_modules() {
warn!("All modules have failed to start");
}
@ -199,7 +214,7 @@ pub struct DeviceConfig {
pub serial: String,
#[serde(default = "default_brightness")]
pub brightness: u8,
pub buttons: Vec<Button>,
pub buttons: Vec<Arc<Button>>,
}
fn default_brightness() -> u8 {

View file

@ -8,6 +8,7 @@ pub use deck_driver as streamdeck;
use futures_util::Future;
use image::DynamicImage;
use lazy_static::lazy_static;
use std::cell::Cell;
use std::collections::HashMap;
use std::pin::Pin;
use std::{error::Error, sync::Arc};
@ -16,7 +17,6 @@ use streamdeck::info::Kind;
use streamdeck::AsyncStreamDeck;
pub use streamdeck::StreamDeckError;
use tokio::sync::mpsc;
use tokio::sync::Mutex;
use tracing::{debug, error, info};
lazy_static! {
@ -37,9 +37,9 @@ pub enum HostEvent {
}
pub type ModuleFuture = Pin<Box<dyn Future<Output = Result<(), ReturnError>> + Send>>;
pub type ModuleFunction = fn(DeviceAccess, ChannelReceiver, Button) -> ModuleFuture;
pub type ModuleFunction = fn(DeviceAccess, ChannelReceiver, Arc<Button>) -> ModuleFuture;
pub fn retrieve_module_from_name(name: String) -> Option<ModuleFunction> {
pub fn retrieve_module_from_name(name: &String) -> Option<ModuleFunction> {
MODULE_MAP.get(name.as_str()).copied()
}
@ -48,10 +48,10 @@ pub fn retrieve_module_from_name(name: String) -> Option<ModuleFunction> {
pub async fn start_module(
// Just for logging purpose
serial: String,
button: Button,
button: Arc<Button>,
module_function: ModuleFunction,
device: Arc<AsyncStreamDeck>,
br: Arc<Mutex<mpsc::Receiver<HostEvent>>>,
br: Cell<mpsc::Receiver<HostEvent>>
) {
debug!("STARTED");
let da = DeviceAccess::new(device, button.index).await;
@ -110,13 +110,13 @@ impl DeviceAccess {
}
pub type ReturnError = Box<dyn Error + Send + Sync>;
pub type ChannelReceiver = Arc<Mutex<mpsc::Receiver<HostEvent>>>;
pub type ChannelReceiver = Cell<mpsc::Receiver<HostEvent>>;
#[async_trait]
pub trait Module {
async fn run(
device: DeviceAccess,
button_receiver: ChannelReceiver,
config: Button,
receiver: ChannelReceiver,
config: Arc<Button>,
) -> Result<(), ReturnError>;
}

View file

@ -4,6 +4,7 @@ use super::DeviceAccess;
use super::Module;
use super::ReturnError;
use async_trait::async_trait;
use std::sync::Arc;
pub struct Blank;
@ -12,7 +13,7 @@ impl Module for Blank {
async fn run(
_streamdeck: DeviceAccess,
_button_receiver: ChannelReceiver,
_config: Button,
_config: Arc<Button>,
) -> Result<(), ReturnError> {
Ok(())
}

View file

@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::sync::Arc;
use crate::Button;
@ -18,20 +18,18 @@ impl Module for Counter {
async fn run(
streamdeck: DeviceAccess,
button_receiver: ChannelReceiver,
config: Button,
_config: Arc<Button>,
) -> Result<(), ReturnError> {
let options = config.options.unwrap_or_default();
let mut button_receiver = button_receiver.into_inner();
let font_data: &[u8] = include_bytes!("../../fonts/SpaceGrotesk.ttf");
let font: Font<'static> = Font::try_from_bytes(font_data).unwrap();
let (h, w) = streamdeck.resolution();
let mut stream = button_receiver.lock().await;
let mut counter: u32 = 0;
loop {
if let Some(event) = stream.recv().await {
if let Some(event) = button_receiver.recv().await {
match event {
HostEvent::ButtonPressed => {
counter += 1;