diff --git a/Cargo.lock b/Cargo.lock index b68696b..17df933 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2186,7 +2186,6 @@ dependencies = [ "socketcan 3.5.0", "spidev", "strum 0.27.1", - "taurus", "thiserror 1.0.69", "tokio", "tokio-serial", @@ -2740,51 +2739,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" -[[package]] -name = "cxx" -version = "1.0.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a71ea7f29c73f7ffa64c50b83c9fe4d3a6d4be89a86b009eb80d5a6d3429d741" -dependencies = [ - "cc", - "cxxbridge-cmd", - "cxxbridge-flags", - "cxxbridge-macro", - "foldhash 0.1.5", - "link-cplusplus", -] - -[[package]] -name = "cxxbridge-cmd" -version = "1.0.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f44296c8693e9ea226a48f6a122727f77aa9e9e338380cb021accaeeb7ee279" -dependencies = [ - "clap", - "codespan-reporting 0.12.0", - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f69c181c176981ae44ba9876e2ea41ce8e574c296b38d06925ce9214fb8e4" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8faff5d4467e0709448187df29ccbf3b0982cc426ee444a193f87b11afb565a8" -dependencies = [ - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.114", -] - [[package]] name = "d3d12" version = "0.19.0" @@ -5282,15 +5236,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4" -[[package]] -name = "link-cplusplus" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" -dependencies = [ - "cc", -] - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -6590,11 +6535,6 @@ dependencies = [ "ttf-parser 0.25.1", ] -[[package]] -name = "oximath" -version = "0.1.0" -source = "git+https://github.com/Reboot-Codes/oxifluxion#c2befd1db5f4d4632769b9bfa1aad4eb698139a7" - [[package]] name = "palette" version = "0.7.6" @@ -8676,15 +8616,6 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" -[[package]] -name = "taurus" -version = "0.1.0" -source = "git+https://codeberg.org/Reboot-Codes/taurus#a733016b79f53f2f76cc4e1b61f86d871e9302b3" -dependencies = [ - "cxx", - "oximath", -] - [[package]] name = "tempfile" version = "3.19.1" diff --git a/Cargo.toml b/Cargo.toml index 071b82d..e5f28ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,14 +3,42 @@ resolver = "2" members = [ "clover-hub", + "clover-hub-macros", "toolbox/carbon-steel", "toolbox/ratchet", "core/libs/tesseract", - "core/libs/carbon-fiber" -, "clover-hub-macros"] + "core/libs/carbon-fiber", +] # [profile.dev] # opt-level = 1 -# +# # [profile.dev.package."*"] # opt-level = 3 + +[workspace.dependencies] +clover-hub = { path = "./clover-hub" } +clover-hub-macros = { path = "./clover-hub-macros" } + +carbon-steel = { path = "./toolbox/carbon-steel" } +ratchet = { path = "./toolbox/ratchet" } + +tesseract = { path = "./core/libs/tesseract" } +carbon-fiber = { path = "./core/libs/carbon-fiber" } + +tokio = { + version = "1.42.0", + features = [ + "signal", + "macros", + "sync", + "rt-multi-thread", + ] +} +tokio-util = "0.7.12" +serde = { version = "1.0", features = ["derive"] } +serde_json_lenient = "0.2.4" +sea-orm = { version = "1.1.1", features = [ + "runtime-tokio-rustls", + "sqlx-sqlite", +] } diff --git a/clover-hub/Cargo.toml b/clover-hub/Cargo.toml index e5ed15d..11d59c6 100644 --- a/clover-hub/Cargo.toml +++ b/clover-hub/Cargo.toml @@ -56,9 +56,11 @@ chrono = { version = "0.4", features = ["serde"] } regex = "1.10.4" uuid = { version = "1.8.0", features = ["serde", "v4"] } thiserror = "1.0" -tokio-util = "0.7.12" +tokio-util = { workspace = true } overflow-proof = "0.1.0" decorum = "0.4.0" +queues = "1.1.0" +anyhow = "1.0.97" # HTTP/WS nexus = { path = "../../nexus" } @@ -66,14 +68,9 @@ url = "2.5.0" image = { version = "0.25.5", features = ["serde"] } # Adding stuff to rust that shoulda been there in the first place. -tokio = { version = "1.42.0", features = [ - "signal", - "macros", - "sync", - "rt-multi-thread", -] } +tokio = { workspace = true } tokio-stream = { version = "0.1.15", features = ["fs"] } -serde = { version = "1.0", features = ["derive"] } +serde = { workspace = true } serde_json = "1.0" futures = { version = "0.3", default-features = false } @@ -81,21 +78,19 @@ futures = { version = "0.3", default-features = false } rand = "0.8.5" # Tensor Calculations -taurus = { git = "https://codeberg.org/Reboot-Codes/taurus" } bollard = "0.17.1" simple-error = "0.3.1" git2 = "0.20.4" os_path = "0.8.0" # Storage -serde_json_lenient = "0.2.4" -sea-orm = { version = "1.1.1", features = [ - "runtime-tokio-rustls", - "sqlx-sqlite", -] } +serde_json_lenient = { workspace = true } +sea-orm = { workspace = true } # SystemUI bevy = "0.15.0" + +# Busses can = { version = "0.2.0", optional = true } socketcan = { version = "3.3.1", optional = true } bluer = { version = "0.17.3", optional = true } @@ -104,8 +99,7 @@ i2c = { version = "0.1.0", optional = true } i2cdev = { version = "0.6.1", optional = true } serialport = { version = "4.7.1", optional = true } tokio-serial = { version = "5.4.5", optional = true } -queues = "1.1.0" -anyhow = "1.0.97" + strum = { version = "0.27.1", features = ["derive"] } base64 = "0.22.1" rmp-serde = "1.3.0" diff --git a/clover-hub/src/main.rs b/clover-hub/src/main.rs index 61e26ab..28d281c 100644 --- a/clover-hub/src/main.rs +++ b/clover-hub/src/main.rs @@ -1,11 +1,23 @@ +//! # CloverHub +//! The Rust-based nerve centre for all communication between modules, their components, you, and configuration tools. It is built to be secure and performant, while allowing for flexible implementation for everything from spicing up your cosplay, to surgical body mods. +//! +//! ## CLI +//! Found here in [`cli`], will spin up the required tokio threads. +//! +//! ## [Server](server) +//! Contains the modular, core logic to run a clover instance. You probably want this. +//! +//! ## [TUI](tui) +//! Terminal User Interface to manage a clover instance over UART/SSH. +//! + #![feature(stmt_expr_attributes)] -#![feature(let_chains)] #![feature(ascii_char)] #![feature(trivial_bounds)] -mod server; -mod tui; -mod utils; +pub mod server; +pub mod tui; +pub mod utils; use clap::{ Arg, @@ -31,10 +43,10 @@ use crate::tui::tui_main; pub struct Empty {} -const DEFAULT_PORT_STR: &str = "6699"; -const DEFAULT_PORT: u16 = 6699; +pub const DEFAULT_PORT_STR: &str = "6699"; +pub const DEFAULT_PORT: u16 = 6699; -fn cli() -> Command { +pub fn cli() -> Command { Command::new("clover") .about("Central command and control for the Clover system.") .subcommand_required(true) @@ -73,7 +85,7 @@ fn cli() -> Command { ) } -fn port_arg() -> Arg { +pub fn port_arg() -> Arg { Arg::new("port") .short('p') .long("port") @@ -85,11 +97,11 @@ fn port_arg() -> Arg { )) } -fn aio_args() -> Vec { +pub fn aio_args() -> Vec { vec![port_arg()] } -fn unwrap_port_arg(arg: Result) -> u16 { +pub fn unwrap_port_arg(arg: Result) -> u16 { match arg { Ok(val) => val, Err(e) => { @@ -102,7 +114,7 @@ fn unwrap_port_arg(arg: Result) -> u16 { } } -fn get_signal_handle( +pub fn get_signal_handle( big_boy_token: CancellationToken, cancellation_token: CancellationToken, server_token: Option, @@ -144,7 +156,7 @@ fn get_signal_handle( // taken from https://stackoverflow.com/questions/77585473/rust-tokio-how-to-handle-more-signals-than-just-sigint-i-e-sigquit#77591939 /// Waits for a signal that requests a graceful shutdown, like SIGTERM or SIGINT. #[cfg(unix)] -async fn wait_for_signal_impl(server_token: Option) { +pub async fn wait_for_signal_impl(server_token: Option) { use log::debug; use tokio::signal::unix::{ signal, @@ -179,7 +191,7 @@ async fn wait_for_signal_impl(server_token: Option) { /// Waits for a signal that requests a graceful shutdown, Ctrl-C (SIGINT). #[cfg(windows)] -async fn wait_for_signal_impl() { +pub async fn wait_for_signal_impl() { use tokio::signal::windows; // Infos here: @@ -197,7 +209,8 @@ async fn wait_for_signal_impl() { }; } -async fn run(big_boy_token: CancellationToken) { +/// Run the primary CloverHub process. +pub async fn run(big_boy_token: CancellationToken) { let matches = Box::leak(Box::new(cli().get_matches())); let subcommand = matches.subcommand(); @@ -339,8 +352,8 @@ async fn run(big_boy_token: CancellationToken) { } #[tokio::main] -async fn main() -> Result<(), Box> { - // TODO:: Create a logger that will send logs to a FIFO buffer to send over WS via EvtBuzz +pub async fn main() -> Result<(), Box> { + // TODO: Create a logger that will send logs to a FIFO buffer to send to CarbonSteel clients env_logger::Builder::new() .parse_filters(&env::var("CLOVER_LOG").unwrap_or("info".to_string())) .init(); diff --git a/clover-hub/src/server/appd/mod.rs b/clover-hub/src/server/appd/mod.rs index 9b4270b..bc7ae99 100644 --- a/clover-hub/src/server/appd/mod.rs +++ b/clover-hub/src/server/appd/mod.rs @@ -1,3 +1,8 @@ +//! # AppDaemon +//! +//! The Application Daemon (a.k.a. AppD), handles external Podman applications and utility scripts in coordination with [`super::warehouse`]. Primary thread execution starts with [`appd_main`]. +//! + pub mod docker; pub mod ipc; pub mod models; diff --git a/clover-hub/src/server/inference_engine/mod.rs b/clover-hub/src/server/inference_engine/mod.rs index 419cc6e..9168f41 100644 --- a/clover-hub/src/server/inference_engine/mod.rs +++ b/clover-hub/src/server/inference_engine/mod.rs @@ -1,3 +1,10 @@ +//! # Inference Engine +//! +//! Manages Machine Learning models and their respective accelerators. +//! +//! Uses [`onnx`] and [`candle`] to handle Analytical Models and Language Models respectively. Primary thread execution starts with [`inference_engine_main`]. +//! + pub mod ipc; use ipc::handle_ipc_msg; diff --git a/clover-hub/src/server/mod.rs b/clover-hub/src/server/mod.rs index 30802ca..3e409db 100644 --- a/clover-hub/src/server/mod.rs +++ b/clover-hub/src/server/mod.rs @@ -1,3 +1,18 @@ +//! # CloverHub Server +//! +//! Contains the modular, core logic to run a Clover instance. Process control is found here in [`server_main`]. +//! +//! Primary server components in startup order are: +//! +//! - **[`warehouse`]:** Manages external repositories, and handles configuration parsing from them and the base configuration. +//! - **[`modman`]:** Manages communication with non-networked [Modules](modman::models::Module) and their [Components](modman::models::CloverComponentMeta). +//! - **[`renderer`]:** Uses graphical acceleration to render and display graphics on any connected [display components](modman::components::video::displays::models::PhysicalDisplayComponent). +//! - **[`inference_engine`]:** Manages Machine Learning models and their respective accelerators. +//! - **[`appd`]:** The Application Daemon (a.k.a. AppDaemon, AppD), handles external Podman applications and utility scripts in coordination with [`warehouse`]. +//! +//! Generally, threads spawned here have a similar structure with IPC recv/send sub-threads, and Startup/Shutdown functions. Following this pattern ensures maintainability and ease of use. +//! + pub mod appd; pub mod inference_engine; pub mod modman; diff --git a/clover-hub/src/server/modman/busses/mod.rs b/clover-hub/src/server/modman/busses/mod.rs index 7ea069d..a364541 100644 --- a/clover-hub/src/server/modman/busses/mod.rs +++ b/clover-hub/src/server/modman/busses/mod.rs @@ -1,3 +1,8 @@ +//! # ModMan Proxies +//! +//! A.k.a. `busses`, Proxies allow Modules to access Zenoh securely without needing a network bridge. [Each bus](proxies) is compiled into ModMan, and enabled via features. +//! + pub mod models; pub mod proxies; diff --git a/clover-hub/src/server/modman/busses/proxies/mod.rs b/clover-hub/src/server/modman/busses/proxies/mod.rs index 59ca794..b844567 100644 --- a/clover-hub/src/server/modman/busses/proxies/mod.rs +++ b/clover-hub/src/server/modman/busses/proxies/mod.rs @@ -1,3 +1,8 @@ +//! # Supported ModMan Proxy Busses +//! +//! All busses here are officially supported, and are designed to work with the CarbonFiber reference API to provide standardized communication according to the [Module's configuration](crate::server::modman::modules) +//! + pub mod app; #[cfg(feature = "bt_classic")] pub mod bt_classic; diff --git a/clover-hub/src/server/modman/components/audio/mod.rs b/clover-hub/src/server/modman/components/audio/mod.rs index 0bb97d3..aab026f 100644 --- a/clover-hub/src/server/modman/components/audio/mod.rs +++ b/clover-hub/src/server/modman/components/audio/mod.rs @@ -1,2 +1,7 @@ +//! # Audio I/O Components +//! +//! Audio components can be either a Microphone or a Speaker, which can be configured to play along with a stream using a Media Player activity ([CarbonSteel]/[Tesseract]), to work with the [Gesture system](crate::server::modman::gestures) in the case of a speaker, and/or to work with an app (when part of an [App Module](crate::server::modman::modules#configuration)) to run ML inference on what's heard in the case of a microphone. +//! + pub mod impls; pub mod models; diff --git a/clover-hub/src/server/modman/components/mod.rs b/clover-hub/src/server/modman/components/mod.rs index 13419b9..6d2370c 100644 --- a/clover-hub/src/server/modman/components/mod.rs +++ b/clover-hub/src/server/modman/components/mod.rs @@ -1,3 +1,8 @@ +//! # Module Components +//! +//! Modules are a high-level, grouping of components. Components provide the actual functional control surfaces to send control events or get data events from! +//! + pub mod audio; pub mod models; pub mod movement; diff --git a/clover-hub/src/server/modman/components/movement/mod.rs b/clover-hub/src/server/modman/components/movement/mod.rs index 0bb97d3..a3d9508 100644 --- a/clover-hub/src/server/modman/components/movement/mod.rs +++ b/clover-hub/src/server/modman/components/movement/mod.rs @@ -1,2 +1,7 @@ +//! Autonomous Physical Interaction +//! +//! Clover seperates movement into it's own category of module to ensure safety, security, and reliability. Motor components take in position or acceleration values based on the configuration, and can optionally return position data. Aesthetic features which have minimal impact on the user can be configured to work with the [Gesture](crate::server::modman::gestures) system, but anything more advanced should be done with an app through [Tesseract]. +//! + pub mod impls; pub mod models; diff --git a/clover-hub/src/server/modman/components/sensors/mod.rs b/clover-hub/src/server/modman/components/sensors/mod.rs index 0bb97d3..e8b801f 100644 --- a/clover-hub/src/server/modman/components/sensors/mod.rs +++ b/clover-hub/src/server/modman/components/sensors/mod.rs @@ -1,2 +1,9 @@ +//! # Sensors, LEDs, etc +//! +//! A sensor component is a bidirectional data component that encompasses everything from temperature, light, and capacitance measurements, to accent LEDs. Sensor components are used for anything that doesn't have a specialized component. +//! +//! For example, you'd use a sensor component for a bend sensor, however, you'd use a [movement component](super::movement) for a brushless motor with an encoder. For more complex LED matrices, you'll want to define a [display component](super::video::displays) instead to take advantage of the hardware accelerated, centralized, and programmatically efficient renderer; however, the development libraries have smooth timing functions for things like LED strips when a sensor component is accessed as something like an LED/set of LEDs, or a servo, etc. +//! + pub mod impls; pub mod models; diff --git a/clover-hub/src/server/modman/components/video/cameras/mod.rs b/clover-hub/src/server/modman/components/video/cameras/mod.rs index 0bb97d3..63ea6c4 100644 --- a/clover-hub/src/server/modman/components/video/cameras/mod.rs +++ b/clover-hub/src/server/modman/components/video/cameras/mod.rs @@ -1,2 +1,9 @@ +//! # Cameras +//! +//! Camera components take in a video stream from the outside for processing and/or reproduction. If you'd like to display video, please see the [display component](super::displays) docs. Camera components are managed by modman, CarbonFiber and Tesseract will handle this for you when given the proper permissions. +//! +//! Camera components can be defined as a block device via video4linux, or as an RTSP/RTMP stream that Clover is authorized to reproduce. If your component needs extra authentication, or a specific process to authenticate, create an application that performs those steps, then exposes one of those streams, then register the component with an [App Module](crate::server::modman::modules#configuration). +//! + pub mod impls; pub mod models; diff --git a/clover-hub/src/server/modman/components/video/displays/mod.rs b/clover-hub/src/server/modman/components/video/displays/mod.rs index 0bb97d3..2cc2d02 100644 --- a/clover-hub/src/server/modman/components/video/displays/mod.rs +++ b/clover-hub/src/server/modman/components/video/displays/mod.rs @@ -1,2 +1,7 @@ +//! # Video Displays +//! +//! Display components are registered with ModMan, and then registered with [Renderer](crate::server::renderer) to make use of hardware graphical acceleration and a unified rendering pipeline, they can then also play along with a stream using a Media Player activity ([CarbonSteel]/[Tesseract]) +//! + pub mod impls; pub mod models; diff --git a/clover-hub/src/server/modman/components/video/mod.rs b/clover-hub/src/server/modman/components/video/mod.rs index bbfb238..87b32a7 100644 --- a/clover-hub/src/server/modman/components/video/mod.rs +++ b/clover-hub/src/server/modman/components/video/mod.rs @@ -1,3 +1,8 @@ +//! # Video I/O Components +//! +//! Video Components are comprised of either a [Camera](cameras), or a [Display](displays). Both require a host board with hardware accelerated en-/decoding to keep everything at a usable framerate. To make use of hardware accelerated inference, you'll need to define an [App Module](crate::server::modman::modules#configuration), and then make use of [the Inference Engine](crate::server::inference_engine) in your app via Tesseract. +//! + pub mod cameras; pub mod displays; use serde::{ diff --git a/clover-hub/src/server/modman/mod.rs b/clover-hub/src/server/modman/mod.rs index ff59b2d..e5662e8 100644 --- a/clover-hub/src/server/modman/mod.rs +++ b/clover-hub/src/server/modman/mod.rs @@ -1,3 +1,11 @@ +//! # ModMan +//! +//! Short for Module Manager. +//! +//! Manages [communication](busses) with [Modules](modules) and their [Components](components), as well as managing message generation for [Gestures](gestures). +//! Primary execution starts at [`modman_main`] +//! + pub mod busses; pub mod components; pub mod gestures; diff --git a/clover-hub/src/server/modman/models.rs b/clover-hub/src/server/modman/models.rs index 6d2f663..48088fc 100644 --- a/clover-hub/src/server/modman/models.rs +++ b/clover-hub/src/server/modman/models.rs @@ -1,3 +1,8 @@ +//! # Clover ModMan Data Structures +//! +//! [Modules](Module) are comprised of [Components](CloverComponent) and their [Metadata](CloverComponentMeta). +//! + use crate::server::{ modman::components::{ audio::models::{ @@ -32,7 +37,7 @@ use super::components::models::CloverComponentTrait; // TODO: Define defaults via `Default` trait impl. -/// Modules contain [Components](CloverComponent). +/// Modules are comprised of [Components](CloverComponent) and their [Metadata](CloverComponentMeta). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Module { pub module_type: String, diff --git a/clover-hub/src/server/modman/modules.rs b/clover-hub/src/server/modman/modules.rs index a80a93e..708cf4f 100644 --- a/clover-hub/src/server/modman/modules.rs +++ b/clover-hub/src/server/modman/modules.rs @@ -1,3 +1,45 @@ +//! # Clover Module Management +//! +//! This *rust* module defines how clover modules should be registered to begin usage. +//! +//! ## Configuration +//! +//! Module configuration is located in [`models`](super::models), which provides two types of modules: +//! +//! - Basic modules, controlled by ModMan, which get commands that are generated from [Gestures](super::gestures), +//! - and App modules, which are controlled by [Apps](crate::server::appd), and only use their module manifest entry to give them permissions. +//! +//! In general, when we need to register a module, we look for it on a [bus](super::busses) (modules share a Bus (or direct Zenoh) connection with their components), and then try to double check that all the [Components](super::components) are present (and perhaps run a few checks if they're configured). We do the inverse when stopping the server process. +//! +//! ## Security +//! +//! Supported modules are required to use an asynchronous signature for all communications with Zenoh over a ModMan Proxy. An asymmetric encryption key is registered during the registration flow to ensure that all messages are legitimate. +//! +//! This is due to Clover's security first design. No security is not an option. +//! +//! ### Security Levels +//! +//! Level 1 and 2 are designed for simple modules that do not have movement components. Regardless, Level 3 or 4 (using post-quantum encryption algorithms) is suggested for production modules and especially for modules with movement components. +//! +//! If a module does not use Post-Quantum Level 3 or 4 security and have a movement component, users will be warned of this fact using a non-dismissible UI component if the configuration application is CORE/Spanner compliant! +//! +//! #### Level 1 +//! +//! The async key provided to Clover is private to a single instance and should be deleted after registration, or be hidden during normal usage if that is not feasible. (such as when using an embossed QR code on a module without a built-in display.) +//! +//! #### Level 2 +//! +//! Clover will generate an async key and provide it to the module during the registration flow to ensure that messages from Clover are legitimate. +//! +//! #### Level 3 +//! +//! Similar to Level 2, however, all messages are encrypted using symmetric encryption, with the key attached to the message, encrypted using the private key of the transmitting party, a.k.a. Hybrid Cryptography. +//! +//! #### Level 4 +//! +//! Similar to Level 3, however, the asymmetric keys are changed constantly to ensure perfect forward secrecy. +//! + use super::{ components::models::CloverComponentTrait, models::{ diff --git a/clover-hub/src/server/renderer/mod.rs b/clover-hub/src/server/renderer/mod.rs index 5c9e6c9..096a875 100644 --- a/clover-hub/src/server/renderer/mod.rs +++ b/clover-hub/src/server/renderer/mod.rs @@ -1,3 +1,12 @@ +//! # Renderer +//! +//! Uses graphical acceleration to render and display graphics on any connected [display components](super::modman::components::video::displays). Primary thread execution starts with [`renderer_main`]. +//! +//! The renderer service is *only* responsible for creating, managing, destroying, and writing to an arbitrary number of OpenGL/Vulkan contexts who's frames are captured and sent to displays registered with modman when permitted by the user. +//! +//! The internal bevy-based engine is found within the [System UI](system_ui) package. +//! + pub mod ipc; pub mod models; pub mod system_ui; diff --git a/clover-hub/src/server/renderer/system_ui/mod.rs b/clover-hub/src/server/renderer/system_ui/mod.rs index 1acb0c7..ace97c7 100644 --- a/clover-hub/src/server/renderer/system_ui/mod.rs +++ b/clover-hub/src/server/renderer/system_ui/mod.rs @@ -1,3 +1,30 @@ +//! # Clover SystemUI +//! +//! The job of SystemUI (and its various APIs) is to create a structured higherachy of attention to ensure that the instance's user is not overwhelmed, and has complete control of Clover. +//! +//! SystemUI has complete control over the content sent to displays through a direct connection to the GPU, or via a bus controlled by Modman. SystemUI will then compose a Composition of Views following X/Y/Z position resolved to relative to the origin of the display, blending modes when handling transparency, and more. SystemUI is heavily based on Bevy's ECS and learning the basics to how that works is suggested before working with CloverHub's default SystemUI implementation. +//! +//! ## Compositions +//! +//! SystemUI as a compositor must analyze the positions of Views within the context of one or more displays. (The latter scenario of multiple displays being handled by one composition is known as a virtual display, similar to ones in desktop operating systems.) A composition has a context that holds the theme, view positions, and global user input. Each client on EvtBuzz that is authorized to do so may own an arbitrary number of top level Composition Views in which it holds an absolute control of context within said view. Drawing over a view is possible if authorized, and Users may choose to permit the view covered to be informed of this in its context. +//! +//! ## Views +//! +//! Views are containers for graphics and context for a specific EvtBuzz Client and/or its inheritants. To save on system resources, views are split into the following types, ordered in least to most overhead. +//! +//! ### Composition Views +//! +//! Composition views use the Component API to render canned graphics commands in commonly used manners (like Buttons, Prompts, etc) and should automatically respect the User's theme unless authorized. (Verified using 3rd party ratings on app listings.) Composition views use the least resources as they are using known values (even when using custom components) which makes rendering easier on SystemUI as it already uses this system internally when composing all top-level views and their subviews when possible. They may also compose other views within themselves as previously mentioned with no extra overhead other than the new content to be rendered with or without passed in context when authorized by the framed view (minus when used as a top level view in a SystemUI implementation, of course). +//! +//! ### Canvas Views +//! +//! Canvas views act as a frame for direct graphics commands to the underlying Bevy library. For security, they are provided with their own ECS world which is composed into the one for the composition as a whole. They have more overhead than composition views as they must stream all operations to SystemUI through EvtBuzz (and Modman and/or the App Daemon if needed), but are less bandwidth-heavy than Stream Views as they rely on the GPU to expand the commands into actual graphics. It's possible to compose views inside of canvases using Tesseract to use a similar security flow to Compositon Views' Frames. +//! +//! ### Stream Views +//! +//! Stream views are used when rendering on the GPU is not possible (e.g. video stream) and all frames must be manually sent to SystemUI. Obviously, this is the heaviest View and is discouraged in favor of Canvas Views when possible. Stream views may compose other views within themselves as authorized via a frame-by-frame stream, however, this should not be used to send View data externally. For that, use CarbonSteel's [Mirroring API]. +//! + pub mod plugins; pub mod systems; diff --git a/clover-hub/src/server/warehouse/config/mod.rs b/clover-hub/src/server/warehouse/config/mod.rs index c446ac8..1a4d92f 100644 --- a/clover-hub/src/server/warehouse/config/mod.rs +++ b/clover-hub/src/server/warehouse/config/mod.rs @@ -1 +1,6 @@ +//! # Clover Base Configuration +//! +//! The base [configuration](models::Config) defines basic details for how to connect to services not included in clover (like Zenoh, Podman/Docker, etc). +//! + pub mod models; diff --git a/clover-hub/src/server/warehouse/config/models.rs b/clover-hub/src/server/warehouse/config/models.rs index 53e2520..5e27693 100644 --- a/clover-hub/src/server/warehouse/config/models.rs +++ b/clover-hub/src/server/warehouse/config/models.rs @@ -13,18 +13,23 @@ use std::{ use crate::server::modman::models::ModManConfig; use crate::server::renderer::models::RendererConfig; +/// Clover Base Configuration, generally pulled from `/opt/clover/config.jsonc`. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { + /// Uses Podman by default. pub docker_daemon: String, pub repos: HashMap, #[serde(default)] pub data_dir: OsPath, + // TODO: REMOVE. Use Zenoh storage #[serde(skip)] pub db: Option>, pub primary_api_key: String, /// Default gesture pack to use pub default_gesture_pack: String, + /// Hardware Configuration for Modman. pub modman: ModManConfig, + /// Hardware Configuration for Renderer. pub renderer: RendererConfig, } diff --git a/clover-hub/src/server/warehouse/mod.rs b/clover-hub/src/server/warehouse/mod.rs index f0614e8..7dff110 100644 --- a/clover-hub/src/server/warehouse/mod.rs +++ b/clover-hub/src/server/warehouse/mod.rs @@ -1,3 +1,12 @@ +//! # Warehouse +//! +//! Manages external repositories, and handles configuration parsing from them and the [base configuration](config). +//! +//! Before starting its sibling components, CHServer will first call [`setup_warehouse`] to get the Clover warehouse/primary storage directory (by default: `/opt/clover`) ready for use. +//! +//! Later, after an inital connection to Zenoh is initalized, Warehouse will then monitor for [events](ipc) using its primary service defined in [`warehouse_main`]. +//! + pub mod config; pub mod db; pub mod ipc; @@ -32,6 +41,8 @@ use tokio::io::{ }; use tokio_util::sync::CancellationToken; +/// The primary startup Error enum. +/// Warehouse will return this enum in [`setup_warehouse`]. // TODO: Move to snafu crate. #[derive(Debug, Clone)] pub enum Error { @@ -46,6 +57,11 @@ pub enum Error { FailedToUpdateRepoDirectoryStructure { error: SimpleError }, } +/// Warehouse's data directory preparation function. +/// +/// 1. Ensures that the data directory exists, +/// 2. Loads the core [configuration file](config) (paired management devices, permanently attached hardware, core Modules to initalize, etc), +/// 3. and preps [Repository storage](repos). pub async fn setup_warehouse(data_dir: String, store: Arc) -> Result<(), Error> { let mut err = None; let mut data_dir_path = OsPath::new().join(data_dir.clone()); @@ -220,6 +236,7 @@ pub async fn gen_user() -> UserConfig { } } +/// Main service function for Warehouse. Maintains an ongoing connection to Zenoh, and will manage filesystem operations as needed. pub async fn warehouse_main( store: Arc, user: NexusUser, @@ -227,6 +244,7 @@ pub async fn warehouse_main( ) { info!("Starting Warehouse..."); + // TODO: Move to Zenoh's persistent storage let db_raw = Database::connect(format!( "sqlite://{}?mode=rwc", store.config.lock().await.data_dir.join("/db.sqlite") diff --git a/clover-hub/src/server/warehouse/repos/impls.rs b/clover-hub/src/server/warehouse/repos/impls.rs index 6e95f3c..478d8d2 100644 --- a/clover-hub/src/server/warehouse/repos/impls.rs +++ b/clover-hub/src/server/warehouse/repos/impls.rs @@ -906,7 +906,7 @@ impl ManifestCompilationFrom> for Optional } } -//* ---------------------------- +// ---------- Begin Actual Value Compilation Implementations ---------- impl Manifest { pub async fn compile( diff --git a/clover-hub/src/server/warehouse/repos/mod.rs b/clover-hub/src/server/warehouse/repos/mod.rs index 7230dba..4e96cb5 100644 --- a/clover-hub/src/server/warehouse/repos/mod.rs +++ b/clover-hub/src/server/warehouse/repos/mod.rs @@ -1,3 +1,12 @@ +//! # Clover Repository Management +//! +//! Clover relies upon external git repositories to load [Applications](crate::server::appd), communicate with [Modules](crate::server::modman::modules), and use [Gesture Packs](crate::server::modman::gestures). +//! +//! The @data/repos path holds an RFQDN tree of all git repositories that Clover has registered. On [startup](super::setup_warehouse), Clover will prune repos that are not registered in the store. +//! +//! Each repo contains a [Manifest](models) (pro tip: the magic manifest entry resolution/directives/etc are found here) which describes everything that clover might care about that's in that repository. +//! + pub mod impls; pub mod models; @@ -52,6 +61,26 @@ impl From for Error { } } +/// Get the source Reverse Fully Qualifed Domain Name for this distrobution of Clover. +/// +///
+/// +/// To comply with the AGPL and to avoid confusion about support, please change these strings to point to a domain you personally control the HTTP contents of! +/// +/// Example +/// ```rust +/// pub fn builtin_rfqdn(is_core: bool) -> String { +/// if is_core { +/// String::from("pages.codeberg.username.clover.core") +/// } else { +/// String::from("pages.codeberg.username.clover") +/// } +/// } +/// ``` +/// +///
+/// +// TODO: Make these a constant so all built-in strings get updated at once! pub fn builtin_rfqdn(is_core: bool) -> String { if is_core { String::from("com.reboot-codes.clover.CORE") @@ -60,6 +89,7 @@ pub fn builtin_rfqdn(is_core: bool) -> String { } } +/// Replace `@here`, `@base`, and `@builtin` manifest value directives. pub fn replace_simple_directives(value: String, resolution_ctx: ResolutionCtx) -> String { debug!( "replace_simple_directives (provided): {} + {:#?}", @@ -294,6 +324,9 @@ where } } +/// Source of filesystem structures like `/opt/clover/repos/com/reboot-codes/clover/@repo`. +/// +/// Using `@repo` for the actual repository keeps everything unique and organized and allows for nested repo bases (e.g. an unstable repo for testing out the latest apps). For this reason `@repo` is a banned directory name in Clover-compatible remote repositories. pub async fn update_repo_dir_structure( repo_dir_path: OsPath, store: Arc, @@ -329,7 +362,8 @@ pub async fn update_repo_dir_structure( } } -/// Used to resolve repo manifest entry **values** that may have directives (`@import`, `@base`, `@here`) in them. +/// Used to resolve repo manifest entry **values** that may have directives (`@import`, `@base`, `@here`, `@builtin`) in them. +/// Hands off to [replace_simple_directives] if it isn't an import. pub async fn resolve_entry_value( value: String, resolution_ctx: ResolutionCtx, @@ -485,6 +519,8 @@ pub async fn resolve_entry_value( } } +/// Downloads repository updates from their origin remote using git. +/// Git implicitly supports both HTTP(S) and SSH, so users have options when getting updates. pub async fn download_repo_updates( store: Arc, repo_dir_path: OsPath, diff --git a/clover-hub/src/server/warehouse/repos/models.rs b/clover-hub/src/server/warehouse/repos/models.rs index 6a50129..d909da2 100644 --- a/clover-hub/src/server/warehouse/repos/models.rs +++ b/clover-hub/src/server/warehouse/repos/models.rs @@ -1,3 +1,42 @@ +//! # Clover's Modular Repository Manifests +//! +//! Manifests can import components and use directives to help keep things organized and reduce the ammount of boilerplate required to write the repository's manifest in an effort to make creating clover compatible projects as easy as possible. +//! +//! ## How Value Compilation Works +//! +//! Manifest values are defined with a Raw version which is then resolved and de-'directive'd by it's compiled variant (drops `Raw` at the begining of the struct/enum, this is important for the [`ManifestCompile`] macro to work). +//! +//! For example (add after `// ---------- ***RAW*** Manfiest Entry Types Start Here ----------`): +//! +//! ```rust +//! #[derive(Debug, Clone, Serialize, Deserialize)] +//! pub struct RawContainerSpec { +//! #[serde(default)] +//! pub interface: OptionalSingleManifestSpecEntry, +//! #[serde(default)] +//! pub build: OptionalSingleManifestSpecEntry, +//! } +//! ``` +//! +//! will become (add after `// ---------- ***COMPILED*** Manfiest Entry Types Start Here ----------`): +//! +//! ```rust +//! #[derive(Debug, Clone, Serialize, Deserialize, ManifestCompile)] +//! pub struct ContainerSpec { +//! #[serde(default)] +//! pub interface: OptionalBoolean, +//! #[serde(default)] +//! pub build: Optional, +//! } +//! ``` +//! +//! when the manifest is [compiled](super::impls), e.g.: +//! +//! ```rust +//! ContainerSpec.compile(raw_container_spec, ...); +//! ``` +//! + use crate::server::appd::models::BuildConfig; #[cfg(feature = "core")] use clover_hub_macros::ManifestCompile; @@ -9,6 +48,7 @@ use serde::{ use simple_error::SimpleError; use std::collections::HashMap; +/// Import Resolution result enum. // TODO: Define defaults via `Default` trait impl for enums that returns its none variant. pub enum Resolution { /// Raw file content from a resolved `@import`. Should be deserialized prior to use! @@ -20,6 +60,8 @@ pub enum Resolution { /// If there were other directives, they've been replaced with the correct value if provided in the ResolutionCtx. NoImport(String), } + +/// Used when resolving imports, contains the path of the current file calling for the resolution, the repository's RFQDN, and the most relevant Clover distro's RFQDN. #[derive(Debug, Clone)] pub struct ResolutionCtx { /// Used for the `@base` directive, if configured in the repo manifest, the base RFQDN for this repo. @@ -133,6 +175,8 @@ pub enum Optional { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] pub struct RequiredString(pub String); +// ---------- ***RAW*** Manifiest Entry Types Start Here ---------- + #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ManifestSpec { pub name: Option, @@ -215,6 +259,8 @@ pub struct RawStaticGestureSpec { pub static_url: String, } +// ---------- ***COMPILED*** Manifest Entry Types Start Here ---------- + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Manifest { #[serde(default)] @@ -283,7 +329,10 @@ pub struct StaticGestureSpec { pub static_url: RequiredString, } +/// Used in conjunction with the [clover_hub_macros] crate provide a function to compile possibly disjointed manifest files into a single object in-memory. +// TODO: Specify trait bounds (resolve async_fn_in_trait). pub trait ManifestCompilationFrom { + /// Perform the compilation on the RAW manifest value type to get the COMPILED manifest value with its dependencies and directives resolved. Put the *parsed* (use [Deserialize]), *`Raw`* value specification in the `spec` parameter. async fn compile( spec: T, resolution_ctx: ResolutionCtx,