Skip to content

Commit

Permalink
webapi: Move known animations logic to animations storage
Browse files Browse the repository at this point in the history
  • Loading branch information
mrozycki committed Dec 19, 2024
1 parent d87009c commit 8abc347
Show file tree
Hide file tree
Showing 20 changed files with 391 additions and 218 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 13 additions & 25 deletions animation-wrapper/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
process::{Command, Stdio},
};

use serde::Deserialize;
use serde::{Deserialize, Serialize};

use crate::unwrap::PluginUnwrapError;

Expand All @@ -19,30 +19,30 @@ pub enum PluginConfigError {
NonUtf8DirectoryName,
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Deserialize)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PluginType {
#[default]
Native,
Wasm,
}

#[derive(Debug, Clone, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginManifest {
display_name: String,
pub display_name: String,
#[serde(default)]
plugin_type: PluginType,
pub plugin_type: PluginType,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginConfig {
pub(crate) animation_id: String,
pub(crate) manifest: PluginManifest,
pub(crate) path: PathBuf,
pub animation_id: String,
pub manifest: PluginManifest,
pub path: PathBuf,
}

impl PluginConfig {
pub fn new(path: PathBuf) -> Result<Self, PluginConfigError> {
pub fn from_path(path: &Path) -> Result<Self, PluginConfigError> {
let manifest: PluginManifest =
serde_json::from_slice(&std::fs::read(path.join("manifest.json")).map_err(|e| {
PluginConfigError::InvalidManifest {
Expand All @@ -63,24 +63,12 @@ impl PluginConfig {
Ok(Self {
animation_id,
manifest,
path,
path: path.to_owned(),
})
}

pub fn animation_id(&self) -> &str {
&self.animation_id
}

pub fn animation_name(&self) -> &str {
&self.manifest.display_name
}

pub fn plugin_type(&self) -> PluginType {
self.manifest.plugin_type
}

pub fn path(&self) -> &Path {
self.path.as_path()
pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, PluginConfigError> {
Self::from_path(path.as_ref())
}

pub fn executable_path(&self) -> PathBuf {
Expand Down
132 changes: 17 additions & 115 deletions animator/src/controller.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use std::collections::HashMap;
use std::error::Error;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use animation_api::event::Event;
use animation_api::schema::{Configuration, ParameterValue};
use animation_wrapper::config::PluginConfig;
use chrono::{DateTime, Duration, Utc};
use client::combined::{CombinedLightClient, CombinedLightClientBuilder};
use client::combined::CombinedLightClient;
#[cfg(feature = "audio")]
use events::beat_generator::BeatEventGenerator;
use events::event_generator::EventGenerator;
Expand All @@ -22,7 +21,7 @@ use rustmas_light_client::LightClientError;
use tokio::sync::{mpsc, Mutex};
use tokio::task::JoinHandle;

use crate::factory::{AnimationFactory, AnimationFactoryError};
use crate::factory::AnimationFactoryError;
use crate::plugin::{AnimationPluginError, Plugin};
use crate::ControllerConfig;

Expand Down Expand Up @@ -71,7 +70,6 @@ pub struct Controller {
animation_join_handle: JoinHandle<()>,
event_generator_join_handle: JoinHandle<()>,
state: Arc<Mutex<ControllerState>>,
animation_factory: AnimationFactory,
event_sender: mpsc::Sender<Event>,
}

Expand All @@ -81,10 +79,9 @@ enum PollFrameResult {
}

impl Controller {
fn new<P: AsRef<Path>>(
points: Vec<(f64, f64, f64)>,
plugin_dir: P,
fn new(
client: Box<dyn rustmas_light_client::LightClient + Sync + Send>,
points_count: usize,
) -> Self {
let now = Utc::now();
let (event_sender, event_receiver) = mpsc::channel(16);
Expand All @@ -97,16 +94,14 @@ impl Controller {
event_generators: Self::start_generators(event_sender.clone()),
}));

let animation_join_handle = tokio::spawn(Self::run(state.clone(), client, points.len()));
let animation_join_handle = tokio::spawn(Self::run(state.clone(), client, points_count));
let event_generator_join_handle =
tokio::spawn(Self::event_loop(state.clone(), event_receiver));
let animation_factory = AnimationFactory::new(plugin_dir, points);

Self {
state,
animation_join_handle,
event_generator_join_handle,
animation_factory,
event_sender,
}
}
Expand Down Expand Up @@ -220,25 +215,19 @@ impl Controller {
}
}

pub fn builder() -> ControllerBuilder {
ControllerBuilder {
points: None,
plugin_dir_: None,
client_builder: CombinedLightClient::builder(),
}
}

pub fn builder_from(config: &ControllerConfig) -> Result<ControllerBuilder, Box<dyn Error>> {
ControllerBuilder {
points: None,
plugin_dir_: Some(config.plugin_path.clone()),
client_builder: CombinedLightClient::builder().with_config(&config.lights)?,
pub fn from_config(
config: &ControllerConfig,
feedback: Option<mpsc::Sender<lightfx::Frame>>,
point_count: usize,
) -> Result<Self, Box<dyn Error>> {
let mut light_client_builder =
CombinedLightClient::builder().with_config(&config.lights)?;
if let Some(sender) = feedback {
light_client_builder =
light_client_builder.with(client::feedback::FeedbackLightClient::new(sender));
}
.points_from_file(&config.points_path)
}

pub fn points(&self) -> &[(f64, f64, f64)] {
self.animation_factory.points()
Ok(Self::new(light_client_builder.build(), point_count))
}

pub async fn restart_event_generators(&self) {
Expand Down Expand Up @@ -296,28 +285,10 @@ impl Controller {
})
}

pub async fn reload_animation(&self) -> Result<Configuration, ControllerError> {
let mut state = self.state.lock().await;
let Some(id) = state
.animation
.as_ref()
.map(|a| a.plugin_config().animation_id())
else {
return Err(ControllerError::NoAnimationSelected);
};
info!("Reloading animation \"{}\"", id);
let animation = self.animation_factory.make(id).await?;
let configuration = animation.configuration().await?;
state.set_animation(Some(animation)).await?;
Ok(configuration)
}

pub async fn switch_animation(
&self,
animation_id: &str,
animation: Box<dyn Plugin>,
) -> Result<Configuration, ControllerError> {
info!("Trying to switch animation to \"{}\"", animation_id);
let animation = self.animation_factory.make(animation_id).await?;
let configuration = animation.configuration().await?;
let mut state = self.state.lock().await;
state.set_animation(Some(animation)).await?;
Expand Down Expand Up @@ -377,73 +348,4 @@ impl Controller {

Ok(())
}

pub fn discover_animations(&mut self) -> Result<(), ControllerError> {
self.animation_factory.discover()?;
Ok(())
}

pub fn list_animations(&self) -> &HashMap<String, PluginConfig> {
self.animation_factory.list()
}
}

pub struct ControllerBuilder {
points: Option<Vec<(f64, f64, f64)>>,
plugin_dir_: Option<PathBuf>,
client_builder: CombinedLightClientBuilder,
}

impl ControllerBuilder {
pub fn points_from_file<P: AsRef<Path>>(mut self, path: P) -> Result<Self, Box<dyn Error>> {
fn points_from_path(path: &Path) -> Result<Vec<(f64, f64, f64)>, ControllerError> {
let points: Vec<_> = csv::ReaderBuilder::new()
.has_headers(false)
.from_path(path)
.map_err(|e| ControllerError::InternalError {
reason: format!("Could not read CSV file: {}", e),
})?
.deserialize()
.filter_map(|record: Result<(f64, f64, f64), _>| record.ok())
.collect();
info!(
"Loaded {} points from {}",
points.len(),
path.to_string_lossy()
);
Ok(points)
}

let path = path.as_ref();
self.points = Some(points_from_path(path)?);
Ok(self)
}

pub fn lights(
mut self,
config: &[rustmas_light_client::LightsConfig],
) -> Result<Self, Box<dyn Error>> {
self.client_builder = self.client_builder.with_config(config)?;
Ok(self)
}

pub fn lights_feedback(mut self, sender: mpsc::Sender<lightfx::Frame>) -> Self {
self.client_builder = self
.client_builder
.with(client::feedback::FeedbackLightClient::new(sender));
self
}

pub fn plugin_dir<P: AsRef<Path>>(mut self, path: P) -> Self {
self.plugin_dir_ = Some(path.as_ref().into());
self
}

pub fn build(self) -> Controller {
Controller::new(
self.points.unwrap(),
self.plugin_dir_.unwrap(),
self.client_builder.build(),
)
}
}
Loading

0 comments on commit 8abc347

Please sign in to comment.