deckster/src/coordinator/mod.rs

128 lines
4.2 KiB
Rust

use std::path::Path;
use std::sync::Arc;
use std::thread;
use color_eyre::eyre::{ContextCompat, WrapErr};
use color_eyre::Result;
use nanoid::nanoid;
use tokio::sync::broadcast;
use deckster_shared::handler_communication::{HandlerCommand, HandlerEvent};
use deckster_shared::path::{KeyPath, KnobPath};
use loupedeck_serial::commands::VibrationPattern;
use loupedeck_serial::device::LoupedeckDevice;
use crate::coordinator::io_worker::{do_io_work, IoWorkerContext};
use crate::coordinator::mqtt::start_mqtt_client;
use crate::handler_runner;
use crate::handler_runner::KeyOrKnobHandlerConfig;
use crate::model::coordinator_config::Config;
use crate::model::get_default_host_id;
use crate::model::mqtt::HandlerHostsConfig;
mod graphics;
mod io_worker;
mod mqtt;
pub mod state;
pub async fn start(config_directory: &Path, config: Config) -> Result<()> {
let config = Arc::new(config);
log::info!("Discovering devices…");
let available_devices = LoupedeckDevice::discover()?;
let available_device = available_devices.first().wrap_err("No device connected.")?;
log::info!("Found {} device(s).", available_devices.len());
let (commands_sender, commands_receiver) = flume::bounded::<HandlerCommand>(5);
let events_sender = broadcast::Sender::<HandlerEvent>::new(5);
commands_sender
.send(HandlerCommand::SetActivePages {
knob_page_id: config.initial.knob_page.clone(),
key_page_id: config.initial.key_page.clone(),
})
.unwrap();
let handler_hosts_config = HandlerHostsConfig {
run_id: nanoid!().into_boxed_str(),
keys: config
.key_pages_by_id
.iter()
.flat_map(|(page_id, p)| {
p.keys.iter().map(|(position, k)| {
(
KeyPath {
page_id: page_id.clone(),
position: *position,
},
KeyOrKnobHandlerConfig {
host_id: k.host.clone(),
name: k.handler.clone(),
config: Arc::clone(&k.config),
},
)
})
})
.collect(),
knobs: config
.knob_pages_by_id
.iter()
.flat_map(|(page_id, p)| {
p.knobs.iter().filter_map(|(position, k)| {
if k.handler.is_empty() {
return None;
}
Some((
KnobPath {
page_id: page_id.clone(),
position,
},
KeyOrKnobHandlerConfig {
host_id: k.host.clone(),
name: k.handler.clone(),
config: Arc::clone(&k.config),
},
))
})
})
.collect(),
};
if let Some(mqtt_config) = &config.mqtt {
log::info!("Initializing MQTT client…");
start_mqtt_client(mqtt_config, &handler_hosts_config, commands_sender.clone(), events_sender.subscribe()).await;
}
log::info!("Initializing handler processes…");
handler_runner::start(
get_default_host_id(),
&config_directory.join("handlers"),
handler_hosts_config,
commands_sender.clone(),
events_sender.subscribe(),
)
.await?;
log::info!("Connecting to the device…");
let device = available_device.connect().wrap_err("Connecting to the device failed.")?;
log::info!("Connected.");
device.set_brightness(0.5);
device.vibrate(VibrationPattern::RiseFall);
let io_worker_context = IoWorkerContext::create(config_directory, Arc::clone(&config), device, commands_sender.clone(), events_sender);
let io_worker_thread = thread::Builder::new()
.name("deckster IO worker".to_owned())
.spawn(move || {
do_io_work(io_worker_context, commands_receiver);
})
.wrap_err("Could not spawn the worker thread")?;
log::info!("Ready.");
io_worker_thread.join().unwrap();
Ok(())
}