From e311583bf49a9a5851a80eb5faf1c7e94c3bb042 Mon Sep 17 00:00:00 2001 From: Reboot-Codes Date: Tue, 11 Aug 2026 11:04:45 -0700 Subject: [PATCH] chore: Re-Organize ModMan and begin Nix cleanup Also begin work on the CAN Bus support, and freeze UART for now since CAN is targeted for PDFC! --- Cargo.lock | 138 +++--- Cargo.toml | 4 +- clover-hub/Cargo.toml | 21 +- clover-hub/src/logging.rs | 2 - clover-hub/src/server/inference_engine/mod.rs | 3 +- clover-hub/src/server/mod.rs | 2 +- clover-hub/src/server/modman/busses/mod.rs | 34 +- clover-hub/src/server/modman/busses/models.rs | 27 ++ .../src/server/modman/busses/proxies/can_2.rs | 24 - .../server/modman/busses/proxies/can_fd.rs | 24 - .../modman/busses/proxies/group/can_2/mod.rs | 42 ++ .../modman/busses/proxies/{ => group}/i2c.rs | 0 .../server/modman/busses/proxies/group/mod.rs | 27 ++ .../modman/busses/proxies/{ => group}/spi.rs | 0 .../busses/proxies/{ => individual}/app.rs | 9 + .../proxies/{ => individual}/bt_classic.rs | 0 .../busses/proxies/{ => individual}/bt_le.rs | 0 .../modman/busses/proxies/individual/mod.rs | 7 + .../{uart.rs => individual/uart/mod.rs} | 108 ++--- .../busses/proxies/individual/uart/reader.rs | 23 + .../busses/proxies/individual/uart/rx.rs | 63 +++ .../busses/proxies/individual/uart/tx.rs | 127 ++++++ .../src/server/modman/busses/proxies/mod.rs | 27 +- .../server/modman/components/audio/impls.rs | 25 +- .../server/modman/components/audio/models.rs | 2 +- .../src/server/modman/components/models.rs | 2 +- .../modman/components/movement/impls.rs | 15 +- .../modman/components/movement/models.rs | 2 +- .../server/modman/components/sensors/impls.rs | 25 +- .../modman/components/sensors/models.rs | 2 +- .../modman/components/video/cameras/impls.rs | 15 +- .../modman/components/video/displays/impls.rs | 25 +- .../components/video/displays/models.rs | 3 +- .../modman/gestures/command_generator.rs | 3 +- clover-hub/src/server/modman/gestures/mod.rs | 2 +- clover-hub/src/server/modman/ipc/displays.rs | 4 +- clover-hub/src/server/modman/ipc/gestures.rs | 10 +- clover-hub/src/server/modman/ipc/mod.rs | 2 +- clover-hub/src/server/modman/mod.rs | 4 +- clover-hub/src/server/modman/models.rs | 410 ------------------ .../src/server/modman/models/components.rs | 96 ++++ clover-hub/src/server/modman/models/config.rs | 149 +++++++ .../src/server/modman/models/gestures.rs | 103 +++++ clover-hub/src/server/modman/models/mod.rs | 33 ++ .../src/server/modman/models/modules.rs | 34 ++ clover-hub/src/server/modman/models/store.rs | 56 +++ clover-hub/src/server/modman/modules.rs | 4 +- .../systems/simulated_controls/mod.rs | 1 - .../system_ui/systems/view_management/mod.rs | 18 +- .../src/server/warehouse/config/models.rs | 2 +- .../src/server/warehouse/repos/models.rs | 1 - clover-hub/src/tui/mod.rs | 11 +- clover-hub/src/utils.rs | 14 +- nix/default.nix | 1 + nix/nixos/clover-hub.nix | 1 + nix/nixos/default.nix | 1 + nix/packages/applications/clover-hub.nix | 1 + nix/packages/applications/default.nix | 1 + nix/packages/applications/spanner.nix | 1 + nix/packages/default.nix | 1 + .../libraries/carbon-fiber/default.nix | 1 + nix/packages/libraries/carbon-fiber/linux.nix | 1 + .../libraries/carbon-fiber/micropython.nix | 1 + .../libraries/carbon-fiber/zephyr.nix | 1 + nix/packages/libraries/carbon-steel.nix | 1 + nix/packages/libraries/default.nix | 1 + todo.md | 4 + 67 files changed, 1017 insertions(+), 785 deletions(-) delete mode 100644 clover-hub/src/server/modman/busses/proxies/can_2.rs delete mode 100644 clover-hub/src/server/modman/busses/proxies/can_fd.rs create mode 100644 clover-hub/src/server/modman/busses/proxies/group/can_2/mod.rs rename clover-hub/src/server/modman/busses/proxies/{ => group}/i2c.rs (100%) create mode 100644 clover-hub/src/server/modman/busses/proxies/group/mod.rs rename clover-hub/src/server/modman/busses/proxies/{ => group}/spi.rs (100%) rename clover-hub/src/server/modman/busses/proxies/{ => individual}/app.rs (68%) rename clover-hub/src/server/modman/busses/proxies/{ => individual}/bt_classic.rs (100%) rename clover-hub/src/server/modman/busses/proxies/{ => individual}/bt_le.rs (100%) create mode 100644 clover-hub/src/server/modman/busses/proxies/individual/mod.rs rename clover-hub/src/server/modman/busses/proxies/{uart.rs => individual/uart/mod.rs} (75%) create mode 100644 clover-hub/src/server/modman/busses/proxies/individual/uart/reader.rs create mode 100644 clover-hub/src/server/modman/busses/proxies/individual/uart/rx.rs create mode 100644 clover-hub/src/server/modman/busses/proxies/individual/uart/tx.rs delete mode 100644 clover-hub/src/server/modman/models.rs create mode 100644 clover-hub/src/server/modman/models/components.rs create mode 100644 clover-hub/src/server/modman/models/config.rs create mode 100644 clover-hub/src/server/modman/models/gestures.rs create mode 100644 clover-hub/src/server/modman/models/mod.rs create mode 100644 clover-hub/src/server/modman/models/modules.rs create mode 100644 clover-hub/src/server/modman/models/store.rs create mode 100644 nix/default.nix create mode 100644 nix/nixos/clover-hub.nix create mode 100644 nix/nixos/default.nix create mode 100644 nix/packages/applications/clover-hub.nix create mode 100644 nix/packages/applications/default.nix create mode 100644 nix/packages/applications/spanner.nix create mode 100644 nix/packages/default.nix create mode 100644 nix/packages/libraries/carbon-fiber/default.nix create mode 100644 nix/packages/libraries/carbon-fiber/linux.nix create mode 100644 nix/packages/libraries/carbon-fiber/micropython.nix create mode 100644 nix/packages/libraries/carbon-fiber/zephyr.nix create mode 100644 nix/packages/libraries/carbon-steel.nix create mode 100644 nix/packages/libraries/default.nix diff --git a/Cargo.lock b/Cargo.lock index cab0f1c..d9dd10c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1625,12 +1625,6 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" -[[package]] -name = "bitflags" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3" - [[package]] name = "bitflags" version = "1.3.2" @@ -1734,7 +1728,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af68112f5c60196495c8b0eea68349817855f565df5b04b2477916d09fb1a901" dependencies = [ "futures", - "hex 0.4.3", + "hex", "libc", "log", "macaddr", @@ -1759,7 +1753,7 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "hex 0.4.3", + "hex", "http", "http-body-util", "hyper", @@ -1918,15 +1912,29 @@ dependencies = [ ] [[package]] -name = "can" +name = "can-iso-tp" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "971b31136f95a97d341ad18bf0394c0dc36bf8a34d0275c77b2d16fcadab3c2e" +checksum = "7e3cc33fd363c3f532f0105fcdd22481b646a6607a5a27320b391a1a64832b93" dependencies = [ - "bitflags 1.3.2", - "bytes", - "embedded-can 0.3.0", - "socketcan 1.7.0", + "embedded-can", + "embedded-can-interface", +] + +[[package]] +name = "can-isotp-interface" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f94e711660d8503f5a40dc0356a382d3e0f6c46a79cab7d0c931fe01c5a697b8" + +[[package]] +name = "can-uds" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a451bb127f44f2d523b4fdb9013a0075254b8969c8e7b83fb2bdfb2aabf5cd17" +dependencies = [ + "embedded-can", + "embedded-can-interface", ] [[package]] @@ -2084,7 +2092,7 @@ dependencies = [ "bevy", "bluer", "bollard", - "can", + "can-iso-tp", "chrono", "clap", "clover-hub-macros", @@ -2094,7 +2102,9 @@ dependencies = [ "i2c", "i2cdev", "image", + "linux-socketcan-iso-tp", "log", + "nix 0.31.3", "opentelemetry-appender-tracing", "opentelemetry_sdk", "os_path", @@ -2107,13 +2117,14 @@ dependencies = [ "rodio 0.20.1", "sea-orm", "serde", + "serde_bytes", "serde_json", "serde_json_lenient", "serialport", "simple-error", - "socketcan 3.6.2", "spidev", "strum 0.27.2", + "strum_macros 0.28.0", "thiserror 1.0.69", "tokio", "tokio-serial", @@ -2850,19 +2861,20 @@ dependencies = [ [[package]] name = "embedded-can" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12719661dce6080d174aa0a9df1f61756022dc105c1761bcd37091a0ee5de635" +checksum = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438" dependencies = [ "nb", ] [[package]] -name = "embedded-can" -version = "0.4.1" +name = "embedded-can-interface" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438" +checksum = "a58850a2c48624af6385d155b7a603349a4e674b6f0e9706c9b190ea91b0cf1e" dependencies = [ + "embedded-can", "nb", ] @@ -3716,12 +3728,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" -[[package]] -name = "hex" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" - [[package]] name = "hex" version = "0.4.3" @@ -3856,7 +3862,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" dependencies = [ - "hex 0.4.3", + "hex", "hyper", "hyper-util", "pin-project-lite", @@ -3891,7 +3897,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" dependencies = [ - "hex 0.4.3", + "hex", "http-body-util", "hyper", "hyper-util", @@ -4231,12 +4237,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" -[[package]] -name = "itertools" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a9b56eb56058f43dc66e58f40a214b2ccbc9f3df51861b63d51dec7b65bc3f" - [[package]] name = "itertools" version = "0.13.0" @@ -4652,6 +4652,20 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +[[package]] +name = "linux-socketcan-iso-tp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58254506a925d8211ba485f81a3d4bde61f0784d1263744140f18668bc800cf3" +dependencies = [ + "can-isotp-interface", + "can-uds", + "embedded-can", + "libc", + "socket2 0.5.10", + "socketcan", +] + [[package]] name = "litemap" version = "0.8.2" @@ -5022,16 +5036,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "nix" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb3ddedaa14746434a02041940495bf11325c22f6d36125d3bdd56090d50a79" -dependencies = [ - "bitflags 0.4.0", - "libc", -] - [[package]] name = "nix" version = "0.26.4" @@ -5068,6 +5072,7 @@ dependencies = [ "cfg-if", "cfg_aliases 0.2.1", "libc", + "memoffset 0.9.1", ] [[package]] @@ -7330,6 +7335,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -7427,7 +7442,7 @@ dependencies = [ "base64 0.22.1", "bs58", "chrono", - "hex 0.4.3", + "hex", "indexmap 1.9.3", "indexmap 2.14.0", "schemars 0.9.0", @@ -7697,19 +7712,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "socketcan" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3101efc6ef5af6f1c1a488241b469757b7a183baca63af958cd90e4696446c80" -dependencies = [ - "hex 0.2.0", - "itertools 0.4.19", - "libc", - "nix 0.5.1", - "try_from", -] - [[package]] name = "socketcan" version = "3.6.2" @@ -7717,8 +7719,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea24328be148eea05f36bc3991b1c174c586853e04d1480b92429b2df6aea865" dependencies = [ "bitflags 2.13.0", - "embedded-can 0.4.1", - "hex 0.4.3", + "embedded-can", + "hex", "itertools 0.13.0", "libc", "log", @@ -7850,7 +7852,7 @@ dependencies = [ "dotenvy", "either", "heck 0.5.0", - "hex 0.4.3", + "hex", "once_cell", "proc-macro2", "quote", @@ -7888,7 +7890,7 @@ dependencies = [ "futures-io", "futures-util", "generic-array", - "hex 0.4.3", + "hex", "hkdf", "hmac", "itoa", @@ -7931,7 +7933,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "hex 0.4.3", + "hex", "hkdf", "hmac", "home", @@ -8321,7 +8323,7 @@ dependencies = [ "filedescriptor", "finl_unicode", "fixedbitset 0.4.2", - "hex 0.4.3", + "hex", "lazy_static", "libc", "log", @@ -8792,12 +8794,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "try_from" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "923a7ee3e97dbfe8685261beb4511cc9620a1252405d02693d43169729570111" - [[package]] name = "ttf-parser" version = "0.20.0" diff --git a/Cargo.toml b/Cargo.toml index 70c1747..0b99ce0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,9 @@ ratchet = { path = "./toolbox/ratchet" } tesseract = { path = "./core/libs/tesseract" } carbon-fiber = { path = "./core/libs/carbon-fiber" } -tokio-util = "0.7.12" +tokio-util = { version = "0.7.12", features = ["io", "io-util"] } serde = { version = "1.0", features = ["derive"] } +serde_bytes = "0.11.19" serde_json_lenient = "0.2.4" tracing = "0.1.44" tracing-subscriber = "0.3.23" @@ -35,6 +36,7 @@ opentelemetry_sdk = "0.32.0" opentelemetry-appender-tracing = "0.32.0" zenoh = "1.9.0" zenoh-ext = { version = "1.9.0", features = ["unstable"] } +can-iso-tp = "0.2.0" [workspace.dependencies.tokio] version = "1.42.0" diff --git a/clover-hub/Cargo.toml b/clover-hub/Cargo.toml index 42a400b..9ec9733 100644 --- a/clover-hub/Cargo.toml +++ b/clover-hub/Cargo.toml @@ -4,7 +4,7 @@ version = "1.0.0" edition = "2021" [features] -default = ["core", "compositor", "all_busses"] +default = ["core", "compositor", "can"] # Usage specific features core = [] @@ -15,20 +15,18 @@ compositor = ["bevy/bevy_winit"] # HW busses all_busses = ["can", "bt", "spi", "i2c", "uart"] -can = ["can_fd", "can_2"] - -can_fd = ["dep:can", "socketcan"] +can = ["can_2"] # Includes support for CAN 2.0A and 2.0B -can_2 = ["dep:can", "socketcan"] +can_2 = ["dep:can-iso-tp", "dep:linux-socketcan-iso-tp"] bt = ["bt_classic", "bt_le"] -bt_classic = ["bluer"] +bt_classic = ["dep:bluer"] -bt_le = ["bluer"] +bt_le = ["dep:bluer"] -spi = ["spidev"] +spi = ["dep:spidev"] i2c = ["dep:i2c", "i2cdev"] @@ -61,6 +59,7 @@ overflow-proof = "0.1.0" decorum = "0.4.0" queues = "1.1.0" anyhow = "1.0.97" +nix = { version = "0.31.3", features = ["net"] } # HTTP/WS zenoh = { workspace = true } @@ -92,8 +91,8 @@ sea-orm = { workspace = true } bevy = "0.15.0" # Busses -can = { version = "0.2.0", optional = true } -socketcan = { version = "3.3.1", optional = true } +can-iso-tp = { workspace = true, optional = true } +linux-socketcan-iso-tp = { version = "0.1.3", optional = true } bluer = { version = "0.17.3", optional = true } spidev = { version = "0.6.0", optional = true } i2c = { version = "0.1.0", optional = true } @@ -105,3 +104,5 @@ strum = { version = "0.27.1", features = ["derive"] } base64 = "0.22.1" rmp-serde = "1.3.0" rodio = "0.20.1" +serde_bytes = { workspace = true } +strum_macros = "0.28.0" diff --git a/clover-hub/src/logging.rs b/clover-hub/src/logging.rs index 0b10792..082c4b1 100644 --- a/clover-hub/src/logging.rs +++ b/clover-hub/src/logging.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use opentelemetry_appender_tracing::layer; use opentelemetry_sdk::logs::SdkLoggerProvider; use opentelemetry_sdk::Resource; diff --git a/clover-hub/src/server/inference_engine/mod.rs b/clover-hub/src/server/inference_engine/mod.rs index fc62bd8..92676c5 100644 --- a/clover-hub/src/server/inference_engine/mod.rs +++ b/clover-hub/src/server/inference_engine/mod.rs @@ -14,7 +14,6 @@ use tracing::{ error, info, instrument, - span, }; use zenoh_ext::{ AdvancedPublisherBuilderExt, @@ -32,7 +31,7 @@ pub const MODULE_EVT_ID: &str = "com/reboot-codes/clover/hub/inference_engine"; #[derive(Debug, Clone)] pub struct InferenceEngineStore { - config: Arc>, + pub config: Arc>, } impl InferenceEngineStore { diff --git a/clover-hub/src/server/mod.rs b/clover-hub/src/server/mod.rs index 37ff6ca..a679ff3 100644 --- a/clover-hub/src/server/mod.rs +++ b/clover-hub/src/server/mod.rs @@ -28,7 +28,7 @@ use inference_engine::{ InferenceEngineStore, }; use modman::{ - models::ModManStore, + models::store::ModManStore, modman_main, }; use renderer::{ diff --git a/clover-hub/src/server/modman/busses/mod.rs b/clover-hub/src/server/modman/busses/mod.rs index 4c6fb27..6ba1bb3 100644 --- a/clover-hub/src/server/modman/busses/mod.rs +++ b/clover-hub/src/server/modman/busses/mod.rs @@ -6,9 +6,8 @@ pub mod models; pub mod proxies; -use super::models::ModManStore; +use super::models::store::ModManStore; use log::info; -use models::Bus; use std::sync::Arc; pub async fn start_busses( @@ -23,39 +22,34 @@ pub async fn start_busses( info!("Starting App Bus..."); })); - #[cfg(feature = "can_fd")] - handles.push(tokio::task::spawn(async move { - info!("Starting CANFD Bus..."); - })); - #[cfg(feature = "can_2")] handles.push(tokio::task::spawn(async move { info!("Starting CAN 2 A/B Bus..."); })); - #[cfg(feature = "bt_classic")] - handles.push(tokio::task::spawn(async move { - info!("Starting Bluetooth Classic Bus..."); - })); + // #[cfg(feature = "bt_classic")] + // handles.push(tokio::task::spawn(async move { + // info!("Starting Bluetooth Classic Bus..."); + // })); #[cfg(feature = "bt_le")] handles.push(tokio::task::spawn(async move { info!("Starting Bluetooth LE Bus..."); })); - #[cfg(feature = "spi")] - handles.push(tokio::task::spawn(async move { - info!("Starting SPI Bus..."); - })); + // #[cfg(feature = "spi")] + // handles.push(tokio::task::spawn(async move { + // info!("Starting SPI Bus..."); + // })); - #[cfg(feature = "i2c")] - handles.push(tokio::task::spawn(async move { - info!("Starting I2C Bus..."); - })); + // #[cfg(feature = "i2c")] + // handles.push(tokio::task::spawn(async move { + // info!("Starting I2C Bus..."); + // })); #[cfg(feature = "uart")] { - use proxies::uart::UARTBus; + use proxies::individual::uart::UARTBus; let uart_session = session.clone(); info!("Starting UART Bus..."); diff --git a/clover-hub/src/server/modman/busses/models.rs b/clover-hub/src/server/modman/busses/models.rs index e71efda..8e7212a 100644 --- a/clover-hub/src/server/modman/busses/models.rs +++ b/clover-hub/src/server/modman/busses/models.rs @@ -33,3 +33,30 @@ pub trait Bus { ) -> impl std::future::Future, anyhow::Error>> + Send; fn get_type() -> BusTypes; } + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(tag = "t")] +pub enum BusMessage { + #[serde(rename = "p")] + Probe(AdoptionProbe), + #[serde(rename = "h")] + Hello(AdoptionHello), + #[serde(rename = "c")] + Content(ContentMessage), +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct AdoptionProbe {} // TODO: this. + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct AdoptionHello {} // TODO: this. + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ContentMessage { + #[serde(rename = "n", with = "serde_bytes")] + pub nonce: Vec, + #[serde(rename = "d", with = "serde_bytes")] + pub data: Vec, + #[serde(rename = "h", with = "serde_bytes")] + pub hmac: Vec, +} diff --git a/clover-hub/src/server/modman/busses/proxies/can_2.rs b/clover-hub/src/server/modman/busses/proxies/can_2.rs deleted file mode 100644 index 494acc6..0000000 --- a/clover-hub/src/server/modman/busses/proxies/can_2.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::sync::Arc; - -use crate::server::modman::busses::models::{ - Bus, - BusTypes, -}; -use can; -use socketcan; - -#[derive(Debug, Clone)] -pub struct CAN2Bus {} - -impl Bus for CAN2Bus { - async fn subscribe_to_bus( - mut self, - session: Arc, - ) -> Result, anyhow::Error> { - todo!() - } - - fn get_type() -> BusTypes { - BusTypes::CAN2 - } -} diff --git a/clover-hub/src/server/modman/busses/proxies/can_fd.rs b/clover-hub/src/server/modman/busses/proxies/can_fd.rs deleted file mode 100644 index 22bd318..0000000 --- a/clover-hub/src/server/modman/busses/proxies/can_fd.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::sync::Arc; - -use crate::server::modman::busses::models::{ - Bus, - BusTypes, -}; -use can; -use socketcan; - -#[derive(Debug, Clone)] -pub struct CANFDBus {} - -impl Bus for CANFDBus { - async fn subscribe_to_bus( - mut self, - session: Arc, - ) -> Result, anyhow::Error> { - todo!() - } - - fn get_type() -> BusTypes { - BusTypes::CANFD - } -} diff --git a/clover-hub/src/server/modman/busses/proxies/group/can_2/mod.rs b/clover-hub/src/server/modman/busses/proxies/group/can_2/mod.rs new file mode 100644 index 0000000..662b3ec --- /dev/null +++ b/clover-hub/src/server/modman/busses/proxies/group/can_2/mod.rs @@ -0,0 +1,42 @@ +//! # CAN 2.0 Bus Proxy +//! +//! +//! + +use std::sync::Arc; + +use crate::server::modman::{ + busses::models::{ + Bus, + BusTypes, + }, + models::store::ModManStore, +}; + +use can_iso_tp; +use linux_socketcan_iso_tp; +use nix::net::if_::if_nameindex; + +#[derive(Debug, Clone)] +pub struct CAN2Bus { + pub store: Arc, +} + +#[derive(Debug, Clone)] +pub struct InterfaceToBind { + pub module_id: String, + pub path: String, +} + +impl Bus for CAN2Bus { + async fn subscribe_to_bus( + mut self, + session: Arc, + ) -> Result, anyhow::Error> { + todo!() + } + + fn get_type() -> BusTypes { + BusTypes::CAN2 + } +} diff --git a/clover-hub/src/server/modman/busses/proxies/i2c.rs b/clover-hub/src/server/modman/busses/proxies/group/i2c.rs similarity index 100% rename from clover-hub/src/server/modman/busses/proxies/i2c.rs rename to clover-hub/src/server/modman/busses/proxies/group/i2c.rs diff --git a/clover-hub/src/server/modman/busses/proxies/group/mod.rs b/clover-hub/src/server/modman/busses/proxies/group/mod.rs new file mode 100644 index 0000000..9c4d848 --- /dev/null +++ b/clover-hub/src/server/modman/busses/proxies/group/mod.rs @@ -0,0 +1,27 @@ +//! # Group Proxies +//! +//! Group proxies handle proxying Zenoh traffic to a wire-level *bus* protocol. Think CAN, SPI, I2C, etc. These handle multiple modules on one interface, and use configuration paradigms that are useful when configuring interfaces. +//! +//! They maintain 3 types of threads: +//! +//! ## Bus Lookout Threads +//! +//! Lookouts run custom code that is used to check for the appearance of the specified interface for that bus. For example, the CAN 2 lookout thread will check all network interfaces for configured interfaces to check if they should be bound using the SocketCAN paradigm using a Bus Manager thread. +//! +//! ## Bus Manager Threads +//! +//! Bus manager threads handle the creation and deletion of module threads. These handle the inital handshake between a module and ModMan, and then creat a module manager to handle Zenoh proxying. +//! +//! ## Module Manager Threads +//! +//! Module manager threads handle the actual zenoh endpoints for modules (like how drivers expose device paths in linux). +//! + +#[cfg(feature = "can_2")] +pub mod can_2; +#[cfg(feature = "can_fd")] +pub mod can_fd; +#[cfg(feature = "i2c")] +pub mod i2c; +#[cfg(feature = "spi")] +pub mod spi; diff --git a/clover-hub/src/server/modman/busses/proxies/spi.rs b/clover-hub/src/server/modman/busses/proxies/group/spi.rs similarity index 100% rename from clover-hub/src/server/modman/busses/proxies/spi.rs rename to clover-hub/src/server/modman/busses/proxies/group/spi.rs diff --git a/clover-hub/src/server/modman/busses/proxies/app.rs b/clover-hub/src/server/modman/busses/proxies/individual/app.rs similarity index 68% rename from clover-hub/src/server/modman/busses/proxies/app.rs rename to clover-hub/src/server/modman/busses/proxies/individual/app.rs index b2d2e7d..dfa3e28 100644 --- a/clover-hub/src/server/modman/busses/proxies/app.rs +++ b/clover-hub/src/server/modman/busses/proxies/individual/app.rs @@ -1,3 +1,12 @@ +//! # UART Bus Proxy +//! +//!
+//! +//! To meet a deadline of 2027-01-01, UART support is currently left as broken. +//! +//!
+//! + use std::sync::Arc; use crate::server::modman::busses::models::{ diff --git a/clover-hub/src/server/modman/busses/proxies/bt_classic.rs b/clover-hub/src/server/modman/busses/proxies/individual/bt_classic.rs similarity index 100% rename from clover-hub/src/server/modman/busses/proxies/bt_classic.rs rename to clover-hub/src/server/modman/busses/proxies/individual/bt_classic.rs diff --git a/clover-hub/src/server/modman/busses/proxies/bt_le.rs b/clover-hub/src/server/modman/busses/proxies/individual/bt_le.rs similarity index 100% rename from clover-hub/src/server/modman/busses/proxies/bt_le.rs rename to clover-hub/src/server/modman/busses/proxies/individual/bt_le.rs diff --git a/clover-hub/src/server/modman/busses/proxies/individual/mod.rs b/clover-hub/src/server/modman/busses/proxies/individual/mod.rs new file mode 100644 index 0000000..7ac924d --- /dev/null +++ b/clover-hub/src/server/modman/busses/proxies/individual/mod.rs @@ -0,0 +1,7 @@ +pub mod app; +#[cfg(feature = "bt_classic")] +pub mod bt_classic; +#[cfg(feature = "bt_le")] +pub mod bt_le; +#[cfg(feature = "uart")] +pub mod uart; diff --git a/clover-hub/src/server/modman/busses/proxies/uart.rs b/clover-hub/src/server/modman/busses/proxies/individual/uart/mod.rs similarity index 75% rename from clover-hub/src/server/modman/busses/proxies/uart.rs rename to clover-hub/src/server/modman/busses/proxies/individual/uart/mod.rs index 7601031..d635120 100644 --- a/clover-hub/src/server/modman/busses/proxies/uart.rs +++ b/clover-hub/src/server/modman/busses/proxies/individual/uart/mod.rs @@ -3,19 +3,30 @@ //! The UART proxy bus is designed bind one serial port per module, then expose I/O over Zenoh. //! +pub mod reader; +pub mod rx; +pub mod tx; + use std::sync::Arc; use crate::server::modman::{ - busses::models::{ - Bus, - BusTypes, + busses::{ + models::{ + Bus, + BusMessage, + BusTypes, + }, + proxies::uart::{ + reader::uart_reader, + rx::uart_rx_thread, + tx::uart_tx_thread, + }, }, connections::ModuleConnection, models::{ ModManStore, PortStatus, }, - MODULE_EVT_ID, }; use anyhow::anyhow; use serialport::{ @@ -23,26 +34,19 @@ use serialport::{ SerialPortInfo, }; use tokio::{ - io::{ - split, - AsyncReadExt, - AsyncWriteExt, - ReadHalf, - WriteHalf, - }, + io::split, task::JoinHandle, }; use tokio_serial::{ self, SerialStream, }; +use tokio_util::io::SyncIoBridge; use tracing::{ debug, error, instrument, - warn, }; - #[derive(Debug, Clone)] pub struct UARTBus { pub store: Arc, @@ -208,10 +212,17 @@ pub async fn bind_uart_port( uart_tx_thread(tx_bind_info, tx_session, tx_port_ctx, port_write).await; })); + let (read_channel, rx_channel) = tokio::sync::mpsc::unbounded_channel::(); + let rx_port_ctx = (bind_info.module_id.clone(), port.port_name.clone()); let rx_session = port_session.clone(); sub_handles.push(tokio::task::spawn(async move { - uart_rx_thread(rx_port_ctx, port_read, rx_session).await; + uart_rx_thread(rx_port_ctx, rx_channel, rx_session).await; + })); + + let bridge = SyncIoBridge::new(port_read); + sub_handles.push(tokio::task::spawn_blocking(move || { + uart_reader(bridge, read_channel); })); Ok(sub_handles) @@ -227,72 +238,3 @@ pub async fn bind_uart_port( } } } - -#[instrument(skip(port_session, port_write))] -pub async fn uart_tx_thread( - tx_bind_info: PortToBind, - port_session: Arc, - tx_port_ctx: (String, String), - mut port_write: WriteHalf, -) { - let key_expr = format!( - "{MODULE_EVT_ID}/modules/by-id/{}/send", - tx_bind_info.module_id.clone() - ); - let (module_id, port_name) = tx_port_ctx; - - match port_session.declare_queryable(&key_expr).await { - Ok(queryable) => { - match queryable.recv_async().await { - Ok(query) => { - match query.payload() { - Some(payload) => { - match payload.try_to_string() { - Ok(payload_str) => { - debug!("Sending message: {payload_str}..."); - - // TODO: Encrypt - match rmp_serde::to_vec(&payload_str) { - Ok(msg_vec) => match port_write.write(msg_vec.as_slice()).await { - Ok(size) => match query.reply(key_expr, format!("{size}")).await { - Ok(_) => {} - Err(err) => todo!(), - }, - Err(err) => todo!(), - }, - Err(err) => todo!(), - } - } - Err(err) => todo!(), - } - } - None => todo!(), - } - } - Err(err) => todo!(), - } - } - Err(err) => todo!(), - } -} - -#[instrument(skip(port_read, port_session))] -pub async fn uart_rx_thread( - rx_port_ctx: (String, String), - mut port_read: ReadHalf, - port_session: Arc, -) { - let mut buffer = Vec::new(); - - loop { - match port_read.read(buffer.as_mut_slice()).await { - Ok(0) => { - warn!("Got EOF, was the module disconnected?"); - // TODO: Send tokio oneshot to reconnect to serial port. - break; - } - Ok(bytes) => {} - Err(err) => todo!(), - } - } -} diff --git a/clover-hub/src/server/modman/busses/proxies/individual/uart/reader.rs b/clover-hub/src/server/modman/busses/proxies/individual/uart/reader.rs new file mode 100644 index 0000000..969a764 --- /dev/null +++ b/clover-hub/src/server/modman/busses/proxies/individual/uart/reader.rs @@ -0,0 +1,23 @@ +use serde::de::DeserializeOwned; +use tokio::{ + io::ReadHalf, + sync::mpsc::UnboundedSender, +}; +use tokio_serial::SerialStream; +use tokio_util::io::SyncIoBridge; + +pub fn uart_reader( + mut bridge: SyncIoBridge>, + channel: UnboundedSender, +) where + Msg: DeserializeOwned, +{ + let mut should_read = true; + + while should_read { + match rmp_serde::from_read::<_, Msg>(&mut bridge) { + Ok(msg) => {} + Err(err) => todo!(), + } + } +} diff --git a/clover-hub/src/server/modman/busses/proxies/individual/uart/rx.rs b/clover-hub/src/server/modman/busses/proxies/individual/uart/rx.rs new file mode 100644 index 0000000..9a0b96a --- /dev/null +++ b/clover-hub/src/server/modman/busses/proxies/individual/uart/rx.rs @@ -0,0 +1,63 @@ +use std::sync::Arc; + +use crate::server::modman::{ + busses::models::BusMessage, + MODULE_EVT_ID, +}; +use tokio::sync::mpsc::UnboundedReceiver; +use tracing::{ + debug, + error, + instrument, +}; +use zenoh_ext::{ + AdvancedPublisherBuilderExt, + CacheConfig, +}; + +#[instrument(skip(port_session))] +pub async fn uart_rx_thread( + rx_port_ctx: (String, String), + mut rx_channel: UnboundedReceiver, + port_session: Arc, +) { + let (module_id, _port_name) = rx_port_ctx; + + let key_expr = format!("{MODULE_EVT_ID}/modules/by-id/{}/recv", &module_id); + + match port_session + .declare_publisher(&key_expr) + .cache(CacheConfig::default().max_samples(1)) + .await + { + Ok(publisher) => { + // If we don't fail out, this block should be the one that gets put in that new thread. + + while let Some(msg) = rx_channel.recv().await { + match serde_json::to_string(&msg) { + Ok(msg_str) => match publisher.put(&msg_str).await { + Ok(_) => { + debug!( + "Sent message as {}, with len: {}", + &module_id, + msg_str.len() + ) + } + Err(err) => { + error!("Failed to broadcast message contents, due to:\n:{err}"); + } + }, + Err(err) => { + error!("Failed to parse data from module into a BusMessage. Is the module connected properly? Due to:\n{err}"); + } + } + } + } + Err(err) => { + // TODO: implement retries, otherwise fail out the UART bridge creation. + error!( + "Failed to create queryable; this is a bug and should be reported! This was due to:\n{err}" + ); + } // see: rustc E0597 for why the semicolon is there?????? + }; +} diff --git a/clover-hub/src/server/modman/busses/proxies/individual/uart/tx.rs b/clover-hub/src/server/modman/busses/proxies/individual/uart/tx.rs new file mode 100644 index 0000000..5ad30fe --- /dev/null +++ b/clover-hub/src/server/modman/busses/proxies/individual/uart/tx.rs @@ -0,0 +1,127 @@ +use std::sync::Arc; + +use serde::{ + Deserialize, + Serialize, +}; +use tokio::io::{ + AsyncWriteExt, + WriteHalf, +}; +use tokio_serial::SerialStream; +use tracing::{ + debug, + error, + instrument, +}; + +use crate::server::modman::busses::models::{ + BusMessage, + ContentMessage, +}; +use crate::server::modman::busses::proxies::uart::PortToBind; +use crate::server::modman::MODULE_EVT_ID; + +#[derive(Serialize, Deserialize, Debug, Clone, strum_macros::Display)] +pub enum UARTTXError { + #[strum(to_string = "missing-payload")] + MissingPayload, + #[strum(to_string = "payload-is-not-string")] + PayloadIsNotString, + #[strum(to_string = "malformed-payload")] + MalformedPayload, + #[strum(to_string = "malformed-payload-wrapper")] + MalformedPayloadWrapper, + #[strum(to_string = "tx-failed")] + TXFailed, +} + +#[instrument] +async fn report_error(error: UARTTXError, query: zenoh::query::Query, key_expr: &str) { + match query.reply(key_expr, format!("error:{error}")).await { + Ok(_) => {} + Err(err) => { + error!("Failed to reply to query with error message due to:\n{err}"); + } + } +} + +#[instrument(skip(port_session, port_write))] +pub async fn uart_tx_thread( + tx_bind_info: PortToBind, + port_session: Arc, + tx_port_ctx: (String, String), + mut port_write: WriteHalf, +) { + let (module_id, _port_name) = tx_port_ctx; + + let key_expr = format!("{MODULE_EVT_ID}/modules/by-id/{module_id}/send"); + + match port_session.declare_queryable(&key_expr).await { + Ok(queryable) => { + // If we don't fail out, this block is what should get put into that new thread. + + while let Ok(query) = queryable.recv_async().await { + match query.payload() { + Some(payload) => { + match payload.try_to_string() { + Ok(payload_str) => { + debug!("Sending message: {payload_str}..."); + + // TODO: Encrypt + match rmp_serde::to_vec(&payload_str) { + Ok(msg_vec) => { + let wrapped_message = BusMessage::Content(ContentMessage { + nonce: vec![], + data: msg_vec, + hmac: vec![], + }); + + match rmp_serde::to_vec(&wrapped_message) { + Ok(wrapped_vec) => match port_write.write(wrapped_vec.as_slice()).await { + Ok(size) => match query.reply(&key_expr, format!("{size}")).await { + Ok(_) => {} + Err(err) => { + error!( + "Failed to reply to client that we were able to send the message, due to:\n{err}" + ); + } + }, + Err(err) => { + error!("Failed to write to UART port due to:\n{err}"); + report_error(UARTTXError::TXFailed, query, &key_expr).await; + } + }, + Err(err) => { + error!("Failed to wrap payload as a BusMessage; this is a bug and should be reported! This happened due to:\n{err}"); + report_error(UARTTXError::MalformedPayloadWrapper, query, &key_expr).await; + } + } + } + Err(err) => { + error!("Failed to parse payload into msgpack, due to:\n{err}"); + report_error(UARTTXError::MalformedPayload, query, &key_expr).await; + } + } + } + Err(err) => { + error!("Query's payload could not be decoded into a string, due to:\n{err}"); + report_error(UARTTXError::PayloadIsNotString, query, &key_expr).await; + } + } + } + None => { + error!("Query was sent to the module endpoint without a payload!"); + report_error(UARTTXError::MissingPayload, query, &key_expr).await; + } + } + } + } + Err(err) => { + // TODO: implement retries, otherwise fail out the UART bridge creation. + error!( + "Failed to create queryable; this is a bug and should be reported! This was due to:\n{err}" + ); + } + } +} diff --git a/clover-hub/src/server/modman/busses/proxies/mod.rs b/clover-hub/src/server/modman/busses/proxies/mod.rs index b844567..8ee608e 100644 --- a/clover-hub/src/server/modman/busses/proxies/mod.rs +++ b/clover-hub/src/server/modman/busses/proxies/mod.rs @@ -1,20 +1,15 @@ //! # 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) +//! All busses here are officially supported, and are designed to work with the CarbonFiber and CarbonSteel reference APIs to provide standardized communication according to the [Module's configuration](crate::server::modman::modules) +//! +//! # Group Proxies +//! +//! Group proxies handle proxying Zenoh traffic to a wire-level *bus* protocol. Think CAN, SPI, I2C, etc. These handle multiple modules on one interface, and use configuration paradigms that are useful when configuring interfaces. +//! +//! # Individual Proxies +//! +//! Individual proxies handle wire-level protocols which rely upon individual ports being connected to each module. Think bluetooth and UART. //! -pub mod app; -#[cfg(feature = "bt_classic")] -pub mod bt_classic; -#[cfg(feature = "bt_le")] -pub mod bt_le; -#[cfg(feature = "can_2")] -pub mod can_2; -#[cfg(feature = "can_fd")] -pub mod can_fd; -#[cfg(feature = "i2c")] -pub mod i2c; -#[cfg(feature = "spi")] -pub mod spi; -#[cfg(feature = "uart")] -pub mod uart; +pub mod group; +pub mod individual; diff --git a/clover-hub/src/server/modman/components/audio/impls.rs b/clover-hub/src/server/modman/components/audio/impls.rs index 86f9cb3..721ec65 100644 --- a/clover-hub/src/server/modman/components/audio/impls.rs +++ b/clover-hub/src/server/modman/components/audio/impls.rs @@ -4,7 +4,10 @@ use super::models::{ ConnectionType, DirectConnection, }; -use crate::server::modman::components::models::CloverComponentTrait; +use crate::server::modman::{ + components::models::CloverComponentTrait, + models::store::ModManStore, +}; use anyhow::anyhow; use log::{ debug, @@ -20,10 +23,7 @@ use rodio::{ use std::sync::Arc; impl CloverComponentTrait for AudioInputComponent { - async fn init( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn init(&mut self, store: Arc) -> Result<(), anyhow::Error> { let mut ret = Ok(()); match self.connection.clone() { @@ -106,19 +106,13 @@ impl CloverComponentTrait for AudioInputComponent { return ret; } - async fn deinit( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn deinit(&mut self, store: Arc) -> Result<(), anyhow::Error> { todo!() } } impl CloverComponentTrait for AudioOutputComponent { - async fn init( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn init(&mut self, store: Arc) -> Result<(), anyhow::Error> { let mut ret = Ok(()); match self.connection.clone() { @@ -202,10 +196,7 @@ impl CloverComponentTrait for AudioOutputComponent { return ret; } - async fn deinit( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn deinit(&mut self, store: Arc) -> Result<(), anyhow::Error> { todo!() } } diff --git a/clover-hub/src/server/modman/components/audio/models.rs b/clover-hub/src/server/modman/components/audio/models.rs index f6201e4..ea0d7c8 100644 --- a/clover-hub/src/server/modman/components/audio/models.rs +++ b/clover-hub/src/server/modman/components/audio/models.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::server::modman::{ components::models::StreamingConnection, - models::GestureConfig, + models::gestures::GestureConfig, }; use serde::{ Deserialize, diff --git a/clover-hub/src/server/modman/components/models.rs b/clover-hub/src/server/modman/components/models.rs index bb044e0..f66b837 100644 --- a/clover-hub/src/server/modman/components/models.rs +++ b/clover-hub/src/server/modman/components/models.rs @@ -1,4 +1,4 @@ -use crate::server::modman::models::ModManStore; +use crate::server::modman::models::store::ModManStore; use serde::{ Deserialize, Serialize, diff --git a/clover-hub/src/server/modman/components/movement/impls.rs b/clover-hub/src/server/modman/components/movement/impls.rs index f055445..1ec7853 100644 --- a/clover-hub/src/server/modman/components/movement/impls.rs +++ b/clover-hub/src/server/modman/components/movement/impls.rs @@ -1,19 +1,16 @@ use super::models::MovementComponent; -use crate::server::modman::components::models::CloverComponentTrait; +use crate::server::modman::{ + components::models::CloverComponentTrait, + models::store::ModManStore, +}; use std::sync::Arc; impl CloverComponentTrait for MovementComponent { - async fn init( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn init(&mut self, store: Arc) -> Result<(), anyhow::Error> { todo!() } - async fn deinit( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn deinit(&mut self, store: Arc) -> Result<(), anyhow::Error> { todo!() } } diff --git a/clover-hub/src/server/modman/components/movement/models.rs b/clover-hub/src/server/modman/components/movement/models.rs index 87d1253..31fbb39 100644 --- a/clover-hub/src/server/modman/components/movement/models.rs +++ b/clover-hub/src/server/modman/components/movement/models.rs @@ -1,4 +1,4 @@ -use crate::server::modman::models::{ +use crate::server::modman::models::gestures::{ GestureConfig, GestureParameters, }; diff --git a/clover-hub/src/server/modman/components/sensors/impls.rs b/clover-hub/src/server/modman/components/sensors/impls.rs index dbf6bc3..aba5c7a 100644 --- a/clover-hub/src/server/modman/components/sensors/impls.rs +++ b/clover-hub/src/server/modman/components/sensors/impls.rs @@ -2,37 +2,28 @@ use super::models::{ IndicatorComponent, SensorComponent, }; -use crate::server::modman::components::models::CloverComponentTrait; +use crate::server::modman::{ + components::models::CloverComponentTrait, + models::store::ModManStore, +}; use std::sync::Arc; impl CloverComponentTrait for SensorComponent { - async fn init( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn init(&mut self, store: Arc) -> Result<(), anyhow::Error> { todo!() } - async fn deinit( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn deinit(&mut self, store: Arc) -> Result<(), anyhow::Error> { todo!() } } impl CloverComponentTrait for IndicatorComponent { - async fn init( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn init(&mut self, store: Arc) -> Result<(), anyhow::Error> { todo!() } - async fn deinit( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn deinit(&mut self, store: Arc) -> Result<(), anyhow::Error> { todo!() } } diff --git a/clover-hub/src/server/modman/components/sensors/models.rs b/clover-hub/src/server/modman/components/sensors/models.rs index 053a89c..923d81b 100644 --- a/clover-hub/src/server/modman/components/sensors/models.rs +++ b/clover-hub/src/server/modman/components/sensors/models.rs @@ -1,4 +1,4 @@ -use crate::server::modman::models::GestureConfig; +use crate::server::modman::models::gestures::GestureConfig; use serde::{ Deserialize, Serialize, diff --git a/clover-hub/src/server/modman/components/video/cameras/impls.rs b/clover-hub/src/server/modman/components/video/cameras/impls.rs index 1f2b43e..cb9bbad 100644 --- a/clover-hub/src/server/modman/components/video/cameras/impls.rs +++ b/clover-hub/src/server/modman/components/video/cameras/impls.rs @@ -1,19 +1,16 @@ use super::models::CameraComponent; -use crate::server::modman::components::models::CloverComponentTrait; +use crate::server::modman::{ + components::models::CloverComponentTrait, + models::store::ModManStore, +}; use std::sync::Arc; impl CloverComponentTrait for CameraComponent { - async fn init( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn init(&mut self, store: Arc) -> Result<(), anyhow::Error> { todo!() } - async fn deinit( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn deinit(&mut self, store: Arc) -> Result<(), anyhow::Error> { todo!() } } diff --git a/clover-hub/src/server/modman/components/video/displays/impls.rs b/clover-hub/src/server/modman/components/video/displays/impls.rs index b444d19..36309fb 100644 --- a/clover-hub/src/server/modman/components/video/displays/impls.rs +++ b/clover-hub/src/server/modman/components/video/displays/impls.rs @@ -2,37 +2,28 @@ use super::models::{ PhysicalDisplayComponent, VirtualDisplayComponent, }; -use crate::server::modman::components::models::CloverComponentTrait; +use crate::server::modman::{ + components::models::CloverComponentTrait, + models::store::ModManStore, +}; use std::sync::Arc; impl CloverComponentTrait for PhysicalDisplayComponent { - async fn init( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn init(&mut self, store: Arc) -> Result<(), anyhow::Error> { Ok(()) } - async fn deinit( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn deinit(&mut self, store: Arc) -> Result<(), anyhow::Error> { Ok(()) } } impl CloverComponentTrait for VirtualDisplayComponent { - async fn init( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn init(&mut self, store: Arc) -> Result<(), anyhow::Error> { Ok(()) } - async fn deinit( - &mut self, - store: Arc, - ) -> Result<(), anyhow::Error> { + async fn deinit(&mut self, store: Arc) -> Result<(), anyhow::Error> { Ok(()) } } diff --git a/clover-hub/src/server/modman/components/video/displays/models.rs b/clover-hub/src/server/modman/components/video/displays/models.rs index 715a2a3..e9d8720 100644 --- a/clover-hub/src/server/modman/components/video/displays/models.rs +++ b/clover-hub/src/server/modman/components/video/displays/models.rs @@ -3,10 +3,9 @@ use crate::server::modman::{ models::StreamingConnection, video::VideoResolution, }, - models::GestureConfig, + models::gestures::GestureConfig, }; use bevy::prelude::Component; -use decorum::Real; use serde::{ Deserialize, Serialize, diff --git a/clover-hub/src/server/modman/gestures/command_generator.rs b/clover-hub/src/server/modman/gestures/command_generator.rs index 2fb07ef..e097dd4 100644 --- a/clover-hub/src/server/modman/gestures/command_generator.rs +++ b/clover-hub/src/server/modman/gestures/command_generator.rs @@ -3,8 +3,7 @@ use tracing::{ instrument, }; -use crate::server::modman::models::{ - GestureParameters, +use crate::server::modman::models::gestures::{ GestureState, GestureStates, }; diff --git a/clover-hub/src/server/modman/gestures/mod.rs b/clover-hub/src/server/modman/gestures/mod.rs index 1e08aad..913a649 100644 --- a/clover-hub/src/server/modman/gestures/mod.rs +++ b/clover-hub/src/server/modman/gestures/mod.rs @@ -77,7 +77,7 @@ use tracing::{ use crate::server::modman::{ gestures::command_generator::gesture_command_generator, - models::ModManStore, + models::store::ModManStore, }; /// Thread to calculate diff --git a/clover-hub/src/server/modman/ipc/displays.rs b/clover-hub/src/server/modman/ipc/displays.rs index ec118bd..f9f9cc2 100644 --- a/clover-hub/src/server/modman/ipc/displays.rs +++ b/clover-hub/src/server/modman/ipc/displays.rs @@ -10,8 +10,8 @@ use tracing::{ use crate::server::{ modman::{ models::{ - CloverComponent, - ModManStore, + components::CloverComponent, + store::ModManStore, }, MODULE_EVT_ID, }, diff --git a/clover-hub/src/server/modman/ipc/gestures.rs b/clover-hub/src/server/modman/ipc/gestures.rs index ea3023b..f766172 100644 --- a/clover-hub/src/server/modman/ipc/gestures.rs +++ b/clover-hub/src/server/modman/ipc/gestures.rs @@ -11,10 +11,12 @@ use tracing::instrument; use crate::server::modman::{ models::{ - GestureCommand, - GestureState, - GestureStates, - ModManStore, + gestures::{ + GestureCommand, + GestureState, + GestureStates, + }, + store::ModManStore, }, MODULE_EVT_ID, }; diff --git a/clover-hub/src/server/modman/ipc/mod.rs b/clover-hub/src/server/modman/ipc/mod.rs index 491c5b0..ba19a68 100644 --- a/clover-hub/src/server/modman/ipc/mod.rs +++ b/clover-hub/src/server/modman/ipc/mod.rs @@ -9,7 +9,7 @@ use crate::server::modman::{ displays::display_queryable, gestures::gesture_queryable, }, - models::ModManStore, + models::store::ModManStore, }; use std::sync::Arc; diff --git a/clover-hub/src/server/modman/mod.rs b/clover-hub/src/server/modman/mod.rs index e0543c2..91996fa 100644 --- a/clover-hub/src/server/modman/mod.rs +++ b/clover-hub/src/server/modman/mod.rs @@ -15,7 +15,7 @@ pub mod models; pub mod modules; use busses::start_busses; -use models::ModManStore; +use models::store::ModManStore; use modules::{ deinit_module, init_module, @@ -40,7 +40,7 @@ use zenoh_ext::{ use crate::{ server::modman::{ ipc::handle_ipc, - models::Module, + models::modules::Module, }, utils::configure_zenoh, }; diff --git a/clover-hub/src/server/modman/models.rs b/clover-hub/src/server/modman/models.rs deleted file mode 100644 index 96b07da..0000000 --- a/clover-hub/src/server/modman/models.rs +++ /dev/null @@ -1,410 +0,0 @@ -//! # Clover ModMan Data Structures -//! -//! [Modules](Module) are comprised of [Components](CloverComponent) and their [Metadata](CloverComponentMeta). -//! - -use crate::server::{ - modman::{ - components::{ - audio::models::{ - AudioInputComponent, - AudioOutputComponent, - }, - movement::models::MovementComponent, - sensors::models::{ - IndicatorComponent, - SensorComponent, - }, - video::{ - cameras::models::CameraComponent, - displays::models::{ - PhysicalDisplayComponent, - VirtualDisplayComponent, - }, - }, - }, - connections::ModuleConnection, - }, - warehouse::config::models::Config, -}; -use serde::{ - Deserialize, - Serialize, -}; -use std::collections::HashMap; -use std::sync::Arc; -use strum::VariantNames; -use tokio::sync::Mutex; - -use super::components::models::CloverComponentTrait; - -// TODO: Define defaults via `Default` trait impl. - -/// Modules are comprised of [Components](CloverComponent) and their [Metadata](CloverComponentMeta). -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Module { - /// RFQDN of the module definition from the manifest database. - pub module_type: String, - /// Manifest-defined name for this module (e.g. recognizable model number). - pub module_name: String, - /// User defined pretty name with manifest-defined default. - pub custom_name: Option, - /// Has communication been established and self-test run? - pub initialized: bool, - /// Vec of Component IDs and if they're critical. - pub components: Vec<(String, bool)>, - /// Either `com.reboot-codes.clover.hub` or the RFQDN of the app that manages this module. - pub registered_by: String, - /// How is this module connected to modman? - pub connection: ModuleConnection, -} - -impl Module { - pub fn get_name(self: &Self) -> String { - match self.custom_name.clone() { - Some(name) => name.clone(), - Option::None => self.module_name.clone(), - } - } -} - -/// Metadata for components, mostly useful for gesture configurations and security. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CloverComponentMeta { - /// Friendly name for this component to be shown to the User in any UI. - pub name: String, - /// Is this component required for the module to work? Default: yes. - /// If any critical component fails to initalize, the module will fail to initalize entirely. - pub critical: bool, - /// Where this component is on/in the user. RFQDN formatted, e.g. `com.reboot-codes.CORE.head.eyes.internal` for a HUD display - pub location: String, - /// Parameters used for gesture events to synthesize commands to send to this component if it supports RX from Nexus. - /// This is also used to determine if a gesture is supported by this component. - /// Ignored if the component does not support recv. - pub base_gesture_parameters: HashMap, - /// If the component is internal, usually inferenced from the `location` parameter. Used by the permissions/privacy rules model. - pub internal: bool, -} - -/// Enum with all known clover component types, technically a valid "component" ([see the Component Trait](CloverComponentTrait)) itself. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum CloverComponent { - AudioInputComponent(AudioInputComponent), - AudioOutputComponent(AudioOutputComponent), - MovementComponent(MovementComponent), - SensorComponent(SensorComponent), - IndicatorComponent(IndicatorComponent), - CameraComponent(CameraComponent), - PhysicalDisplayComponent(PhysicalDisplayComponent), - VirtualDisplayComponent(VirtualDisplayComponent), -} - -impl CloverComponentTrait for CloverComponent { - /// Passes the context to the inner-component function implementation. - async fn init(&mut self, store: Arc) -> Result<(), anyhow::Error> { - match self { - CloverComponent::AudioInputComponent(component) => component.init(store.clone()).await, - CloverComponent::AudioOutputComponent(component) => component.init(store.clone()).await, - CloverComponent::MovementComponent(component) => component.init(store.clone()).await, - CloverComponent::SensorComponent(component) => component.init(store.clone()).await, - CloverComponent::IndicatorComponent(component) => component.init(store.clone()).await, - CloverComponent::CameraComponent(component) => component.init(store.clone()).await, - CloverComponent::PhysicalDisplayComponent(component) => component.init(store.clone()).await, - CloverComponent::VirtualDisplayComponent(component) => component.init(store.clone()).await, - } - } - - /// Passes the context to the inner-component function implementation. - async fn deinit(&mut self, store: Arc) -> Result<(), anyhow::Error> { - match self { - CloverComponent::AudioInputComponent(component) => component.deinit(store.clone()).await, - CloverComponent::AudioOutputComponent(component) => component.deinit(store.clone()).await, - CloverComponent::MovementComponent(component) => component.deinit(store.clone()).await, - CloverComponent::SensorComponent(component) => component.deinit(store.clone()).await, - CloverComponent::IndicatorComponent(component) => component.deinit(store.clone()).await, - CloverComponent::CameraComponent(component) => component.deinit(store.clone()).await, - CloverComponent::PhysicalDisplayComponent(component) => component.deinit(store.clone()).await, - CloverComponent::VirtualDisplayComponent(component) => component.deinit(store.clone()).await, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -/// Turns a gesture intensity (-1.0 to 1.0) into a value that the component can understand in accordance with the following formula -/// ```rust -/// let value = ( -/// ( -/// ( -/// (intensity + 1) * (max - min) -/// ) + min -/// ) * multiplier -/// ) + offset -/// ``` -pub struct GestureParameters { - pub min: f64, - pub max: f64, - pub multiplier: f64, - pub offset: f64, -} - -impl GestureParameters { - pub fn calculate_intensity(&self, intensity: f64) -> f64 { - return ((((intensity + 1.0) * (self.max - self.min)) + self.min) * self.multiplier) - + self.offset; - } -} - -/// In memory data-store for components, modules, and any needed configuration. -#[derive(Debug, Clone)] -pub struct ModManStore { - pub modules: Arc>>, - pub components: Arc>>>, - pub config: Arc>, - pub gesture_states: Arc>>, - pub foreground_gesture_priority: Arc>>, - pub background_gesture_priority: Arc>>, - /// Used for [Bus](super::busses::models::Bus) statuses, etc - pub port_statuses: PortStatuses, -} - -impl ModManStore { - pub fn new(optional_config: Option>>) -> Self { - let config = match optional_config { - Some(cfg) => cfg, - Option::None => Arc::new(Mutex::new(Config::default())), - }; - - ModManStore { - modules: Arc::new(Mutex::new(HashMap::new())), - components: Arc::new(Mutex::new(HashMap::new())), - gesture_states: Arc::new(Mutex::new(HashMap::new())), - foreground_gesture_priority: Arc::new(Mutex::new(Vec::new())), - background_gesture_priority: Arc::new(Mutex::new(Vec::new())), - port_statuses: PortStatuses { - uart: Arc::new(Mutex::new(HashMap::new())), - }, - config, - } - } -} - -/// Used for [Bus](super::busses::models::Bus) statuses, etc -#[derive(Debug, Clone)] -pub struct PortStatuses { - /// Used by the [UART Bus](super::busses::proxies::uart::UARTBus) - pub uart: Arc>>, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum PortStatus { - /// Available but unused. - #[serde(rename = "available")] - Available, - /// Requested by $MODULE_ID, but the UART bus isn't initalized yet - #[serde(rename = "requested")] - Requested(String), - /// Currently being used by $MODULE_ID - #[serde(rename = "bound")] - Bound(String), - /// Unavailable, but still requested by $MODULE_ID - #[serde(rename = "unavailable")] - Unavailable(String), -} - -#[derive(Serialize, Deserialize, Clone, Debug, VariantNames, PartialEq)] -#[serde(tag = "command")] -pub enum GestureState { - #[serde(rename = "begin")] - #[strum(serialize = "begin")] - Begin { - /// Multiplier over the value (y) axis of the gesture's smoothing curve. - intensity: f64, - /// Multiplier over the time (x) axis of the gesture's smoothing curve. - speed: f64, - /// Background gestures will be calculated before foreground gestures. Foreground gestures will always override background gestures. - /// - /// For example, you can have a background gesture that displays intermittent blinking, but you can start a temporary foreground gesture to show a different shape of the eyes to display an emotion. - background: Option, - }, - /// Resume calculation of module commands. - /// - /// This state is not saved at all. - #[serde(rename = "unpause")] - #[strum(serialize = "unpause")] - UnPause, - /// Temporarilly freeze the values that were already calculated for module commands as a result of this gesture. - /// - /// This state is not saved. Upon shutdown, if a foreground gesture is paused, the gesture will be removed from the stack upon shutdown; otherwise, background gestures will be saved in the `Begin` state to be automatically resumed upon startup. - #[serde(rename = "pause")] - #[strum(serialize = "end")] - Pause, - /// Removes the gesture from module command calculations entierly. - #[serde(rename = "end")] - #[strum(serialize = "end")] - End, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct GestureCommand { - pub state: GestureState, - pub auto_switch: Option, - pub is_from_system: bool, - pub areas: Option>, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GestureConfig { - /// The primary gesture pack to use for this component - pub primary_gesture_pack: Option, - /// The default gesture for the component to use when idle. - pub default_gesture: Option, - pub gesture_parameters: Option, - /// Override gestures (use different pack, adjust gesture parameters, etc) - pub gesture_overrides: HashMap, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GestureOverride { - /// Gesture id in `gesture_RFQDN@gesture_pack_RFQDN` format, where `@` and everything after can be ommitted to use the default gesture pack. - pub gesture_preset_id: String, - pub gesture_parameters: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ModManConfig { - /// All ports available for modman to use to connect to modules. - pub uart_ports: Vec, - /// Whether to restart paused gestures automatically on startup. - pub restart_gestures: bool, - pub gesture_states: HashMap, - pub gestures_bg_by_default: bool, - pub static_modules: HashMap, - pub static_components: HashMap, -} - -impl Default for ModManConfig { - /// Ensure that there is a display if the compositor was compiled in - /// and there wasn't a display defined in the config/disabled explicitly. - fn default() -> Self { - let mut static_modules = HashMap::new(); - let mut static_components = HashMap::new(); - - #[cfg(feature = "compositor")] - { - use std::num::NonZero; - - use crate::server::modman::components::video::{ - displays::{ - self, - models::{ - DirectConnection, - DisplaySize, - }, - }, - VideoResolution, - }; - - let internal_display_id = uuid::Uuid::new_v4().to_string(); - let external_display_id = uuid::Uuid::new_v4().to_string(); - - static_modules.insert( - uuid::Uuid::new_v4().to_string(), - Module { - module_type: "com.reboot-codes.clover.debug-display".to_string(), - module_name: "Debug Displays".to_string(), - custom_name: Default::default(), - initialized: false, - components: vec![ - (internal_display_id.clone(), true), - (external_display_id.clone(), true), - ], - registered_by: "com.reboot-codes.clover.modman.default".to_string(), - connection: ModuleConnection::Simulated( - "com.reboot-codes.clover.debug-display:0".to_string(), - ), - }, - ); - - static_components.insert( - internal_display_id.clone(), - ( - CloverComponentMeta { - name: "Debug Internal Display".to_string(), - critical: true, - location: "none".to_string(), - base_gesture_parameters: HashMap::new(), - internal: true, - }, - CloverComponent::PhysicalDisplayComponent(PhysicalDisplayComponent { - resolution: VideoResolution { - width: NonZero::new(500).unwrap(), - height: NonZero::new(200).unwrap(), - }, - size: DisplaySize { - height: Some(2.0), - width: None, - }, - connection: displays::models::ConnectionType::Direct(DirectConnection { - display_id: "@primary".to_string(), - windowed: true, - }), - virtual_display: None, - gesture_config: None, - internal: true, - }), - ), - ); - - static_components.insert( - external_display_id.clone(), - ( - CloverComponentMeta { - name: "Debug External Display".to_string(), - critical: true, - location: "none".to_string(), - base_gesture_parameters: HashMap::new(), - internal: false, - }, - CloverComponent::PhysicalDisplayComponent(PhysicalDisplayComponent { - resolution: VideoResolution { - width: NonZero::new(500).unwrap(), - height: NonZero::new(200).unwrap(), - }, - size: DisplaySize { - height: Some(2.0), - width: None, - }, - connection: displays::models::ConnectionType::Direct(DirectConnection { - display_id: "@primary".to_string(), - windowed: true, - }), - virtual_display: None, - gesture_config: None, - internal: false, - }), - ), - ); - } - - Self { - static_components, - static_modules, - uart_ports: Default::default(), - restart_gestures: Default::default(), - gesture_states: Default::default(), - gestures_bg_by_default: Default::default(), - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GestureStates { - /// Previous state of the gesture if we'd like to temporarily move back. - pub prev_state: Option, - /// Is this gesture paused? - pub paused: bool, - /// The current state of the gesture. - pub current_state: GestureState, - /// The next state to switch to (pre-loaded into Renderer and Modules if it loads resources), with a delay if we want to switch automatically. - pub next_state: Option<(f64, GestureState)>, -} diff --git a/clover-hub/src/server/modman/models/components.rs b/clover-hub/src/server/modman/models/components.rs new file mode 100644 index 0000000..9584cd9 --- /dev/null +++ b/clover-hub/src/server/modman/models/components.rs @@ -0,0 +1,96 @@ +use std::{ + collections::HashMap, + sync::Arc, +}; + +use serde::{ + Deserialize, + Serialize, +}; + +use crate::server::modman::{ + components::{ + audio::models::{ + AudioInputComponent, + AudioOutputComponent, + }, + models::CloverComponentTrait, + movement::models::MovementComponent, + sensors::models::{ + IndicatorComponent, + SensorComponent, + }, + video::{ + cameras::models::CameraComponent, + displays::models::{ + PhysicalDisplayComponent, + VirtualDisplayComponent, + }, + }, + }, + models::{ + gestures::GestureParameters, + store::ModManStore, + }, +}; + +/// Metadata for components, mostly useful for gesture configurations and security. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CloverComponentMeta { + /// Friendly name for this component to be shown to the User in any UI. + pub name: String, + /// Is this component required for the module to work? Default: yes. + /// If any critical component fails to initalize, the module will fail to initalize entirely. + pub critical: bool, + /// Where this component is on/in the user. RFQDN formatted, e.g. `com.reboot-codes.CORE.head.eyes.internal` for a HUD display + pub location: String, + /// Parameters used for gesture events to synthesize commands to send to this component if it supports RX from Nexus. + /// This is also used to determine if a gesture is supported by this component. + /// Ignored if the component does not support recv. + pub base_gesture_parameters: HashMap, + /// If the component is internal, usually inferenced from the `location` parameter. Used by the permissions/privacy rules model. + pub internal: bool, +} + +/// Enum with all known clover component types, technically a valid "component" ([see the Component Trait](CloverComponentTrait)) itself. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum CloverComponent { + AudioInputComponent(AudioInputComponent), + AudioOutputComponent(AudioOutputComponent), + MovementComponent(MovementComponent), + SensorComponent(SensorComponent), + IndicatorComponent(IndicatorComponent), + CameraComponent(CameraComponent), + PhysicalDisplayComponent(PhysicalDisplayComponent), + VirtualDisplayComponent(VirtualDisplayComponent), +} + +impl CloverComponentTrait for CloverComponent { + /// Passes the context to the inner-component function implementation. + async fn init(&mut self, store: Arc) -> Result<(), anyhow::Error> { + match self { + CloverComponent::AudioInputComponent(component) => component.init(store.clone()).await, + CloverComponent::AudioOutputComponent(component) => component.init(store.clone()).await, + CloverComponent::MovementComponent(component) => component.init(store.clone()).await, + CloverComponent::SensorComponent(component) => component.init(store.clone()).await, + CloverComponent::IndicatorComponent(component) => component.init(store.clone()).await, + CloverComponent::CameraComponent(component) => component.init(store.clone()).await, + CloverComponent::PhysicalDisplayComponent(component) => component.init(store.clone()).await, + CloverComponent::VirtualDisplayComponent(component) => component.init(store.clone()).await, + } + } + + /// Passes the context to the inner-component function implementation. + async fn deinit(&mut self, store: Arc) -> Result<(), anyhow::Error> { + match self { + CloverComponent::AudioInputComponent(component) => component.deinit(store.clone()).await, + CloverComponent::AudioOutputComponent(component) => component.deinit(store.clone()).await, + CloverComponent::MovementComponent(component) => component.deinit(store.clone()).await, + CloverComponent::SensorComponent(component) => component.deinit(store.clone()).await, + CloverComponent::IndicatorComponent(component) => component.deinit(store.clone()).await, + CloverComponent::CameraComponent(component) => component.deinit(store.clone()).await, + CloverComponent::PhysicalDisplayComponent(component) => component.deinit(store.clone()).await, + CloverComponent::VirtualDisplayComponent(component) => component.deinit(store.clone()).await, + } + } +} diff --git a/clover-hub/src/server/modman/models/config.rs b/clover-hub/src/server/modman/models/config.rs new file mode 100644 index 0000000..3a33c6e --- /dev/null +++ b/clover-hub/src/server/modman/models/config.rs @@ -0,0 +1,149 @@ +use std::collections::HashMap; + +use serde::{ + Deserialize, + Serialize, +}; + +use crate::server::modman::models::{ + components::{ + CloverComponent, + CloverComponentMeta, + }, + gestures::GestureStates, + modules::Module, +}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ModManConfig { + /// All ports available for modman to use to connect to modules. + pub uart_ports: Vec, + /// All (CAN 2) network interfaces available for modman to use to connect to modules. + pub can_2_interfaces: Vec, + /// Whether to restart paused gestures automatically on startup. + pub restart_gestures: bool, + pub gesture_states: HashMap, + pub gestures_bg_by_default: bool, + pub static_modules: HashMap, + pub static_components: HashMap, +} + +impl Default for ModManConfig { + /// Ensure that there is a display if the compositor was compiled in + /// and there wasn't a display defined in the config/disabled explicitly. + fn default() -> Self { + let mut static_modules = HashMap::new(); + let mut static_components = HashMap::new(); + + #[cfg(feature = "compositor")] + { + use std::num::NonZero; + + use crate::server::modman::{ + components::video::{ + displays::{ + self, + models::{ + DirectConnection, + DisplaySize, + PhysicalDisplayComponent, + }, + }, + VideoResolution, + }, + connections::ModuleConnection, + }; + + let internal_display_id = uuid::Uuid::new_v4().to_string(); + let external_display_id = uuid::Uuid::new_v4().to_string(); + + static_modules.insert( + uuid::Uuid::new_v4().to_string(), + Module { + module_type: "com.reboot-codes.clover.debug-display".to_string(), + module_name: "Debug Displays".to_string(), + custom_name: Default::default(), + initialized: false, + components: vec![ + (internal_display_id.clone(), true), + (external_display_id.clone(), true), + ], + registered_by: "com.reboot-codes.clover.modman.default".to_string(), + connection: ModuleConnection::Simulated( + "com.reboot-codes.clover.debug-display:0".to_string(), + ), + }, + ); + + static_components.insert( + internal_display_id.clone(), + ( + CloverComponentMeta { + name: "Debug Internal Display".to_string(), + critical: true, + location: "none".to_string(), + base_gesture_parameters: HashMap::new(), + internal: true, + }, + CloverComponent::PhysicalDisplayComponent(PhysicalDisplayComponent { + resolution: VideoResolution { + width: NonZero::new(500).unwrap(), + height: NonZero::new(200).unwrap(), + }, + size: DisplaySize { + height: Some(2.0), + width: None, + }, + connection: displays::models::ConnectionType::Direct(DirectConnection { + display_id: "@primary".to_string(), + windowed: true, + }), + virtual_display: None, + gesture_config: None, + internal: true, + }), + ), + ); + + static_components.insert( + external_display_id.clone(), + ( + CloverComponentMeta { + name: "Debug External Display".to_string(), + critical: true, + location: "none".to_string(), + base_gesture_parameters: HashMap::new(), + internal: false, + }, + CloverComponent::PhysicalDisplayComponent(PhysicalDisplayComponent { + resolution: VideoResolution { + width: NonZero::new(500).unwrap(), + height: NonZero::new(200).unwrap(), + }, + size: DisplaySize { + height: Some(2.0), + width: None, + }, + connection: displays::models::ConnectionType::Direct(DirectConnection { + display_id: "@primary".to_string(), + windowed: true, + }), + virtual_display: None, + gesture_config: None, + internal: false, + }), + ), + ); + } + + Self { + static_components, + static_modules, + uart_ports: Default::default(), + can_2_interfaces: Default::default(), + restart_gestures: Default::default(), + gesture_states: Default::default(), + gestures_bg_by_default: Default::default(), + } + } +} diff --git a/clover-hub/src/server/modman/models/gestures.rs b/clover-hub/src/server/modman/models/gestures.rs new file mode 100644 index 0000000..d791b44 --- /dev/null +++ b/clover-hub/src/server/modman/models/gestures.rs @@ -0,0 +1,103 @@ +use std::collections::HashMap; + +use serde::{ + Deserialize, + Serialize, +}; +use strum::VariantNames; + +#[derive(Serialize, Deserialize, Clone, Debug, VariantNames, PartialEq)] +#[serde(tag = "command")] +pub enum GestureState { + #[serde(rename = "begin")] + #[strum(serialize = "begin")] + Begin { + /// Multiplier over the value (y) axis of the gesture's smoothing curve. + intensity: f64, + /// Multiplier over the time (x) axis of the gesture's smoothing curve. + speed: f64, + /// Background gestures will be calculated before foreground gestures. Foreground gestures will always override background gestures. + /// + /// For example, you can have a background gesture that displays intermittent blinking, but you can start a temporary foreground gesture to show a different shape of the eyes to display an emotion. + background: Option, + }, + /// Resume calculation of module commands. + /// + /// This state is not saved at all. + #[serde(rename = "unpause")] + #[strum(serialize = "unpause")] + UnPause, + /// Temporarilly freeze the values that were already calculated for module commands as a result of this gesture. + /// + /// This state is not saved. Upon shutdown, if a foreground gesture is paused, the gesture will be removed from the stack upon shutdown; otherwise, background gestures will be saved in the `Begin` state to be automatically resumed upon startup. + #[serde(rename = "pause")] + #[strum(serialize = "end")] + Pause, + /// Removes the gesture from module command calculations entierly. + #[serde(rename = "end")] + #[strum(serialize = "end")] + End, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct GestureCommand { + pub state: GestureState, + pub auto_switch: Option, + pub is_from_system: bool, + pub areas: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GestureConfig { + /// The primary gesture pack to use for this component + pub primary_gesture_pack: Option, + /// The default gesture for the component to use when idle. + pub default_gesture: Option, + pub gesture_parameters: Option, + /// Override gestures (use different pack, adjust gesture parameters, etc) + pub gesture_overrides: HashMap, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GestureOverride { + /// Gesture id in `gesture_RFQDN@gesture_pack_RFQDN` format, where `@` and everything after can be ommitted to use the default gesture pack. + pub gesture_preset_id: String, + pub gesture_parameters: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GestureStates { + /// Previous state of the gesture if we'd like to temporarily move back. + pub prev_state: Option, + /// Is this gesture paused? + pub paused: bool, + /// The current state of the gesture. + pub current_state: GestureState, + /// The next state to switch to (pre-loaded into Renderer and Modules if it loads resources), with a delay if we want to switch automatically. + pub next_state: Option<(f64, GestureState)>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +/// Turns a gesture intensity (-1.0 to 1.0) into a value that the component can understand in accordance with the following formula +/// ```rust +/// let value = ( +/// ( +/// ( +/// (intensity + 1) * (max - min) +/// ) + min +/// ) * multiplier +/// ) + offset +/// ``` +pub struct GestureParameters { + pub min: f64, + pub max: f64, + pub multiplier: f64, + pub offset: f64, +} + +impl GestureParameters { + pub fn calculate_intensity(&self, intensity: f64) -> f64 { + return ((((intensity + 1.0) * (self.max - self.min)) + self.min) * self.multiplier) + + self.offset; + } +} diff --git a/clover-hub/src/server/modman/models/mod.rs b/clover-hub/src/server/modman/models/mod.rs new file mode 100644 index 0000000..51c2771 --- /dev/null +++ b/clover-hub/src/server/modman/models/mod.rs @@ -0,0 +1,33 @@ +//! # Clover ModMan Data Structures +//! +//! [Modules](Module) are comprised of [Components](components::CloverComponent) and their [Metadata](components::CloverComponentMeta). +//! + +use serde::{ + Deserialize, + Serialize, +}; + +pub mod components; +pub mod config; +pub mod gestures; +pub mod modules; +pub mod store; + +// TODO: Define defaults via `Default` trait impl. + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum PortStatus { + /// Available but unused. + #[serde(rename = "available")] + Available, + /// Requested by $MODULE_ID, but the UART bus isn't initalized yet + #[serde(rename = "requested")] + Requested(String), + /// Currently being used by $MODULE_ID + #[serde(rename = "bound")] + Bound(String), + /// Unavailable, but still requested by $MODULE_ID + #[serde(rename = "unavailable")] + Unavailable(String), +} diff --git a/clover-hub/src/server/modman/models/modules.rs b/clover-hub/src/server/modman/models/modules.rs new file mode 100644 index 0000000..8258926 --- /dev/null +++ b/clover-hub/src/server/modman/models/modules.rs @@ -0,0 +1,34 @@ +use serde::{ + Deserialize, + Serialize, +}; + +use crate::server::modman::connections::ModuleConnection; + +/// Modules are comprised of [Components](CloverComponent) and their [Metadata](CloverComponentMeta). +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Module { + /// RFQDN of the module definition from the manifest database. + pub module_type: String, + /// Manifest-defined name for this module (e.g. recognizable model number). + pub module_name: String, + /// User defined pretty name with manifest-defined default. + pub custom_name: Option, + /// Has communication been established and self-test run? + pub initialized: bool, + /// Vec of Component IDs and if they're critical. + pub components: Vec<(String, bool)>, + /// Either `com.reboot-codes.clover.hub` or the RFQDN of the app that manages this module. + pub registered_by: String, + /// How is this module connected to modman? + pub connection: ModuleConnection, +} + +impl Module { + pub fn get_name(self: &Self) -> String { + match self.custom_name.clone() { + Some(name) => name.clone(), + Option::None => self.module_name.clone(), + } + } +} diff --git a/clover-hub/src/server/modman/models/store.rs b/clover-hub/src/server/modman/models/store.rs new file mode 100644 index 0000000..8ce3d2e --- /dev/null +++ b/clover-hub/src/server/modman/models/store.rs @@ -0,0 +1,56 @@ +use std::collections::HashMap; +use std::sync::Arc; +use tokio::sync::Mutex; + +use crate::server::modman::models::components::{ + CloverComponent, + CloverComponentMeta, +}; +use crate::server::modman::models::gestures::GestureStates; +use crate::server::modman::models::modules::Module; +use crate::server::modman::models::PortStatus; +use crate::server::warehouse::config::models::Config; + +/// Used for [Bus](super::busses::models::Bus) statuses, etc +#[derive(Debug, Clone)] +pub struct PortStatuses { + /// Used by the [UART Bus](super::busses::proxies::uart::UARTBus). + pub uart: Arc>>, + /// Used by the [CAN2 Bus](super::busses::proxies::can_2::CAN2Bus). + pub can_2: Arc>>, +} + +/// In memory data-store for components, modules, and any needed configuration. +#[derive(Debug, Clone)] +pub struct ModManStore { + pub modules: Arc>>, + pub components: Arc>>>, + pub config: Arc>, + pub gesture_states: Arc>>, + pub foreground_gesture_priority: Arc>>, + pub background_gesture_priority: Arc>>, + /// Used for [Bus](super::busses::models::Bus) statuses, etc + pub port_statuses: PortStatuses, +} + +impl ModManStore { + pub fn new(optional_config: Option>>) -> Self { + let config = match optional_config { + Some(cfg) => cfg, + Option::None => Arc::new(Mutex::new(Config::default())), + }; + + ModManStore { + modules: Arc::new(Mutex::new(HashMap::new())), + components: Arc::new(Mutex::new(HashMap::new())), + gesture_states: Arc::new(Mutex::new(HashMap::new())), + foreground_gesture_priority: Arc::new(Mutex::new(Vec::new())), + background_gesture_priority: Arc::new(Mutex::new(Vec::new())), + port_statuses: PortStatuses { + uart: Arc::new(Mutex::new(HashMap::new())), + can_2: Arc::new(Mutex::new(HashMap::new())), + }, + config, + } + } +} diff --git a/clover-hub/src/server/modman/modules.rs b/clover-hub/src/server/modman/modules.rs index 9519739..b38037f 100644 --- a/clover-hub/src/server/modman/modules.rs +++ b/clover-hub/src/server/modman/modules.rs @@ -43,8 +43,8 @@ use super::{ components::models::CloverComponentTrait, models::{ - ModManStore, - Module, + modules::Module, + store::ModManStore, }, }; use std::sync::Arc; diff --git a/clover-hub/src/server/renderer/system_ui/systems/simulated_controls/mod.rs b/clover-hub/src/server/renderer/system_ui/systems/simulated_controls/mod.rs index 0369172..d36a8a2 100644 --- a/clover-hub/src/server/renderer/system_ui/systems/simulated_controls/mod.rs +++ b/clover-hub/src/server/renderer/system_ui/systems/simulated_controls/mod.rs @@ -15,7 +15,6 @@ use bevy::{ render::camera::RenderTarget, window::{ PresentMode, - PrimaryWindow, WindowRef, WindowTheme, }, diff --git a/clover-hub/src/server/renderer/system_ui/systems/view_management/mod.rs b/clover-hub/src/server/renderer/system_ui/systems/view_management/mod.rs index d35894f..c294079 100644 --- a/clover-hub/src/server/renderer/system_ui/systems/view_management/mod.rs +++ b/clover-hub/src/server/renderer/system_ui/systems/view_management/mod.rs @@ -22,31 +22,31 @@ pub enum View { #[derive(Component)] pub struct ViewPosition { - x: f64, - y: f64, - z: f64, + pub x: f64, + pub y: f64, + pub z: f64, } #[derive(Component)] pub struct ViewDimensions { - x: i64, - y: i64, + pub x: i64, + pub y: i64, /// A 0 here will be determined as a 2D view. - z: i64, + pub z: i64, } #[derive(Component)] pub struct ViewMixingProps { - visible: bool, + pub visible: bool, } /// Each (virtual) display must have a composition attached to it to have frames ready to send. #[derive(Component)] pub struct Composition { /// Holds all Entity Ids for the Views within this Compositon. - views: HashMap, + pub views: HashMap, /// Should this composition be trusted with user-focused content? This has nothing to do with being exclusive to CloverHub usage. - internal: bool, + pub internal: bool, } /// Themes are set for each composition. A ThemeSet can be loaded for each type of composition. diff --git a/clover-hub/src/server/warehouse/config/models.rs b/clover-hub/src/server/warehouse/config/models.rs index 7e1f544..84e3d09 100644 --- a/clover-hub/src/server/warehouse/config/models.rs +++ b/clover-hub/src/server/warehouse/config/models.rs @@ -9,7 +9,7 @@ use std::{ sync::Arc, }; -use crate::server::modman::models::ModManConfig; +use crate::server::modman::models::config::ModManConfig; use crate::server::renderer::models::RendererConfig; /// Clover Base Configuration, generally pulled from `/opt/clover/config.jsonc`. diff --git a/clover-hub/src/server/warehouse/repos/models.rs b/clover-hub/src/server/warehouse/repos/models.rs index 25c68ca..67188c9 100644 --- a/clover-hub/src/server/warehouse/repos/models.rs +++ b/clover-hub/src/server/warehouse/repos/models.rs @@ -330,7 +330,6 @@ pub struct StaticGestureSpec { } /// 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. fn compile( diff --git a/clover-hub/src/tui/mod.rs b/clover-hub/src/tui/mod.rs index 921e6c1..c7f4fec 100644 --- a/clover-hub/src/tui/mod.rs +++ b/clover-hub/src/tui/mod.rs @@ -7,22 +7,13 @@ use ratatui::{ KeyEventKind, }, prelude::*, - widgets::{ - Block, - Borders, - Tabs, - }, }; use std::{ io, time::Duration, }; use tokio_util::sync::CancellationToken; -use tracing::{ - debug, - info, - instrument, -}; +use tracing::instrument; use tui_logger::{ TuiLoggerLevelOutput, TuiLoggerSmartWidget, diff --git a/clover-hub/src/utils.rs b/clover-hub/src/utils.rs index 9a7f7de..84a506c 100644 --- a/clover-hub/src/utils.rs +++ b/clover-hub/src/utils.rs @@ -10,21 +10,15 @@ use log::{ use os_path::OsPath; use serde::Deserialize; use simple_error::SimpleError; -use std::{ - hash::{ - DefaultHasher, - Hash, - Hasher, - }, - sync::Arc, +use std::hash::{ + DefaultHasher, + Hash, + Hasher, }; use tokio::{ fs, io::AsyncReadExt, }; -use tokio_util::sync::CancellationToken; -use tracing::instrument; -use zenoh_ext::AdvancedPublisher; pub struct RecvSync(pub std::sync::mpsc::Receiver); diff --git a/nix/default.nix b/nix/default.nix new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nix/default.nix @@ -0,0 +1 @@ +{} diff --git a/nix/nixos/clover-hub.nix b/nix/nixos/clover-hub.nix new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nix/nixos/clover-hub.nix @@ -0,0 +1 @@ +{} diff --git a/nix/nixos/default.nix b/nix/nixos/default.nix new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nix/nixos/default.nix @@ -0,0 +1 @@ +{} diff --git a/nix/packages/applications/clover-hub.nix b/nix/packages/applications/clover-hub.nix new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nix/packages/applications/clover-hub.nix @@ -0,0 +1 @@ +{} diff --git a/nix/packages/applications/default.nix b/nix/packages/applications/default.nix new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nix/packages/applications/default.nix @@ -0,0 +1 @@ +{} diff --git a/nix/packages/applications/spanner.nix b/nix/packages/applications/spanner.nix new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nix/packages/applications/spanner.nix @@ -0,0 +1 @@ +{} diff --git a/nix/packages/default.nix b/nix/packages/default.nix new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nix/packages/default.nix @@ -0,0 +1 @@ +{} diff --git a/nix/packages/libraries/carbon-fiber/default.nix b/nix/packages/libraries/carbon-fiber/default.nix new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nix/packages/libraries/carbon-fiber/default.nix @@ -0,0 +1 @@ +{} diff --git a/nix/packages/libraries/carbon-fiber/linux.nix b/nix/packages/libraries/carbon-fiber/linux.nix new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nix/packages/libraries/carbon-fiber/linux.nix @@ -0,0 +1 @@ +{} diff --git a/nix/packages/libraries/carbon-fiber/micropython.nix b/nix/packages/libraries/carbon-fiber/micropython.nix new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nix/packages/libraries/carbon-fiber/micropython.nix @@ -0,0 +1 @@ +{} diff --git a/nix/packages/libraries/carbon-fiber/zephyr.nix b/nix/packages/libraries/carbon-fiber/zephyr.nix new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nix/packages/libraries/carbon-fiber/zephyr.nix @@ -0,0 +1 @@ +{} diff --git a/nix/packages/libraries/carbon-steel.nix b/nix/packages/libraries/carbon-steel.nix new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nix/packages/libraries/carbon-steel.nix @@ -0,0 +1 @@ +{} diff --git a/nix/packages/libraries/default.nix b/nix/packages/libraries/default.nix new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/nix/packages/libraries/default.nix @@ -0,0 +1 @@ +{} diff --git a/todo.md b/todo.md index 2519179..c38cf70 100644 --- a/todo.md +++ b/todo.md @@ -29,6 +29,10 @@ - [ ] Finish module init and de-init methods - [ ] Finish gesture event schema - [ ] Build gesture message generator. + - [ ] Reorganize module security levels to match: + - [ ] L1: Static symmetric key. + - [ ] L2: Asymmetric-powered session key exchange. + - [ ] L3: Asymmetric-powered message key exchange. - [ ] i18n - [ ] Warehouse - [ ] Finish models -- 2.51.2