From 4cabb2786c9bffe6b70b059f49d8b25e763d970f Mon Sep 17 00:00:00 2001 From: Ramarko Bhattacharya Date: Sun, 6 Jul 2025 20:07:43 -0500 Subject: [PATCH 1/4] Updated to Use New toio_rust --- .gitignore | 2 +- rust_osc/Cargo.lock | 981 ----------- rust_osc/Cargo.toml | 17 - rust_osc/src/main.rs | 1255 -------------- .../{toio_processing.pde => main.pde} | 20 +- toio_processing/osc.pde | 98 +- toio_processing/sketch.properties | 2 +- toio_rust/Cargo.lock | 1532 +++++++++++++++++ toio_rust/Cargo.toml | 26 + toio_rust/src/main.rs | 273 +++ toio_rust/src/osc.rs | 227 +++ toio_rust/src/toio.rs | 1125 ++++++++++++ toio_rust/src/ui.rs | 140 ++ 13 files changed, 3375 insertions(+), 2323 deletions(-) delete mode 100644 rust_osc/Cargo.lock delete mode 100644 rust_osc/Cargo.toml delete mode 100644 rust_osc/src/main.rs rename toio_processing/{toio_processing.pde => main.pde} (90%) create mode 100644 toio_rust/Cargo.lock create mode 100644 toio_rust/Cargo.toml create mode 100644 toio_rust/src/main.rs create mode 100644 toio_rust/src/osc.rs create mode 100644 toio_rust/src/toio.rs create mode 100644 toio_rust/src/ui.rs diff --git a/.gitignore b/.gitignore index 38964e15..b4f92b28 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .DS_Store -rust_osc/target/ +toio_rust/target/ rust_bridge/target/ env/ \ No newline at end of file diff --git a/rust_osc/Cargo.lock b/rust_osc/Cargo.lock deleted file mode 100644 index c9a7a8c6..00000000 --- a/rust_osc/Cargo.lock +++ /dev/null @@ -1,981 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - -[[package]] -name = "async-trait" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "bluez-async" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e81db626818b179ab5fc3393b7ac77462335f716b302ef1ff1866473ab6ddb25" -dependencies = [ - "async-trait", - "bitflags", - "bluez-generated", - "dbus", - "dbus-tokio", - "futures", - "itertools", - "log", - "serde", - "serde-xml-rs", - "thiserror", - "tokio", - "uuid", -] - -[[package]] -name = "bluez-generated" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ad8f302dd79411881cd0181f3bd9fc91a959b71c3a13159bd8a07d2c40dc7f" -dependencies = [ - "dbus", -] - -[[package]] -name = "btleplug" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4a6513650aae4fbaeab7ed4e3ef14f8e095d156ecc687f2391f75741e788fa" -dependencies = [ - "async-trait", - "bitflags", - "bluez-async", - "cocoa", - "dashmap", - "dbus", - "futures", - "jni", - "jni-utils", - "libc", - "log", - "objc", - "once_cell", - "static_assertions", - "thiserror", - "tokio", - "tokio-stream", - "uuid", - "windows", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e" - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cocoa" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" -dependencies = [ - "bitflags", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" -dependencies = [ - "bitflags", - "block", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "combine" -version = "4.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" -dependencies = [ - "bitflags", - "core-foundation", - "foreign-types", - "libc", -] - -[[package]] -name = "dashmap" -version = "5.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3495912c9c1ccf2e18976439f4443f3fee0fd61f424ff99fde6a66b15ecb448f" -dependencies = [ - "cfg-if", - "hashbrown", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "dbus" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8bcdd56d2e5c4ed26a529c5a9029f5db8290d433497506f958eae3be148eb6" -dependencies = [ - "futures-channel", - "futures-util", - "libc", - "libdbus-sys", - "winapi", -] - -[[package]] -name = "dbus-tokio" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12a1a74a0c53b22d7d994256cf3ecbeefa5eedce3cf9d362945ac523c4132180" -dependencies = [ - "dbus", - "libc", - "tokio", -] - -[[package]] -name = "either" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "futures" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" - -[[package]] -name = "futures-executor" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" - -[[package]] -name = "futures-macro" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" - -[[package]] -name = "futures-task" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" - -[[package]] -name = "futures-util" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "itertools" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" -dependencies = [ - "either", -] - -[[package]] -name = "jni" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" -dependencies = [ - "cesu8", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jni-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d3a00638470bedd9bcbea85292bef2207e6db984079db0bd70148a449cdb457" -dependencies = [ - "dashmap", - "futures", - "jni", - "log", - "once_cell", - "static_assertions", - "uuid", -] - -[[package]] -name = "libc" -version = "0.2.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" - -[[package]] -name = "libdbus-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c185b5b7ad900923ef3a8ff594083d4d9b5aea80bb4f32b8342363138c0d456b" -dependencies = [ - "pkg-config", -] - -[[package]] -name = "lock_api" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "mio" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys", -] - -[[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", -] - -[[package]] -name = "once_cell" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" - -[[package]] -name = "parking_lot_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" - -[[package]] -name = "pretty_env_logger" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" -dependencies = [ - "env_logger", - "log", -] - -[[package]] -name = "proc-macro2" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "redox_syscall" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" - -[[package]] -name = "rosc" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ca359b640ca8ef191ad8a56dd897fc46a7c733ea7b360085891cc7a70effdc" -dependencies = [ - "byteorder", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "serde" -version = "1.0.140" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-xml-rs" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65162e9059be2f6a3421ebbb4fef3e74b7d9e7c60c50a0e292c6239f19f1edfa" -dependencies = [ - "log", - "serde", - "thiserror", - "xml-rs", -] - -[[package]] -name = "serde_derive" -version = "1.0.140" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" - -[[package]] -name = "socket2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "syn" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "toio-osc" -version = "0.1.1" -dependencies = [ - "btleplug", - "env_logger", - "futures", - "getopts", - "log", - "pretty_env_logger", - "rosc", - "tokio", - "uuid", -] - -[[package]] -name = "tokio" -version = "1.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89797afd69d206ccd11fb0ea560a44bbb87731d020670e79416d442919257d42" -dependencies = [ - "autocfg", - "libc", - "mio", - "num_cpus", - "once_cell", - "pin-project-lite", - "socket2", - "tokio-macros", - "winapi", -] - -[[package]] -name = "tokio-macros" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-stream" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-util" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "unicode-ident" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" - -[[package]] -name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - -[[package]] -name = "uuid" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" - -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" -dependencies = [ - "windows_aarch64_msvc 0.39.0", - "windows_i686_gnu 0.39.0", - "windows_i686_msvc 0.39.0", - "windows_x86_64_gnu 0.39.0", - "windows_x86_64_msvc 0.39.0", -] - -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" - -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_i686_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" - -[[package]] -name = "xml-rs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" diff --git a/rust_osc/Cargo.toml b/rust_osc/Cargo.toml deleted file mode 100644 index 95d28212..00000000 --- a/rust_osc/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "toio-osc" -version = "0.1.1" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -log = "0.4" -pretty_env_logger = "0.4.0" -env_logger = "0.7" -btleplug = "0.10" -uuid = "1.1.2" -futures = "0.3.21" -tokio = { version = "1.20.0", features = ["net","rt","macros","rt-multi-thread"] } -rosc = "0.5" -getopts = "0.2" \ No newline at end of file diff --git a/rust_osc/src/main.rs b/rust_osc/src/main.rs deleted file mode 100644 index c533de2f..00000000 --- a/rust_osc/src/main.rs +++ /dev/null @@ -1,1255 +0,0 @@ -extern crate getopts; -use btleplug::api::CharPropFlags; -use btleplug::api::{ - Central, CentralEvent, Characteristic, Manager as _, Peripheral, ScanFilter, WriteType, -}; -use getopts::Options; - -use btleplug::platform::Manager; -use futures::stream::StreamExt; -use rosc::encoder; -use rosc::{OscMessage, OscPacket, OscType}; -use std::collections::HashMap; -use std::error::Error; -use std::sync::{Arc, Mutex}; -use std::time::Duration; -use std::{env, net::SocketAddr}; -use tokio::time; -use tokio::{net::UdpSocket, sync::mpsc}; -use uuid::Uuid; - -//#[macro_use] -extern crate log; - -//characteristic of interest -const TOIO_SERVICE_UUID: Uuid = Uuid::from_u128(0x10B20100_5B3B_4571_9508_CF3EFCD7BBAE); -const POSITION_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(0x10B20101_5B3B_4571_9508_CF3EFCD7BBAE); -const MOTOR_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(0x10B20102_5B3B_4571_9508_CF3EFCD7BBAE); -const LIGHT_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(0x10B20103_5B3B_4571_9508_CF3EFCD7BBAE); -const SOUND_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(0x10B20104_5B3B_4571_9508_CF3EFCD7BBAE); -const MOTION_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(0x10B20106_5B3B_4571_9508_CF3EFCD7BBAE); -const BUTTON_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(0x10B20107_5B3B_4571_9508_CF3EFCD7BBAE); -const BATTERY_CHARACTERISTIC_UUID: Uuid = Uuid::from_u128(0x10B20108_5B3B_4571_9508_CF3EFCD7BBAE); - -fn print_usage(program: &str, opts: Options) { - let brief = format!("Usage: {} FILE [options]", program); - print!("{}", opts.usage(&brief)); -} - -fn print_toio_connected(toio_connected: i32){ - if toio_connected == 1 {println!("1 peripheral connected");} - else {println!("{} peripherals connected", toio_connected);} -} - -fn return_toio_id(name: &str) -> &str{ - //list of toio ids - const IDARR : [&str; 193] = [ - "Individual ID", //TOIO Num - "0", // #1 - "j1c", // #2 - "r81", // #3 - "26E", // #4 - "76t", // #5 - "broken", // #6 - "k5k", // #7 - "h41", // #8 - "0", // #9 - "0", // #10 - "0", // #11 - "Q3A", // #12 - "03a", // #13 - "0", // #14 - "K0m", // #15 - "0", // #16 - "0", // #17 - "p8B", // #18 - "91B", // #19 - "p75", // #20 - "G1E", // #21 - "k2L", // #22 - "b5p", // #23 - "J6C", // #24 - "a1K", // #25 - "b8T", // #26 - "b6A", // #27 - "01c", // #28 - "0", // #29 - "0", // #30 - "E2N", // #31 - "G7t", // #32 - "L6T", // #33 - "C0E", // #34 - "t79", // #35 - "J6k", // #36 - "d6f", // #37 - "0", // #38 - "M75", // #39 - "310", // #40 - "M5p", // #41 - "A4a", // #42 - "M9J", // #43 - "i01", // #44 - "T5m", // #45 - "j1G", // #46 - "40G", // #47 - "L6n", // #48 - "a3F", // #49 - "J8d", // #50 - "227", // #51 - "k4i", // #52 - "J68", // #53 - "90J", // #54 - "k96", // #55 - "0", // #56 - "0", // #57 - "0", // #58 - "0", // #59 - "0", // #60 - "0", // #61 - "0", // #62 - "0", // #63 - "0", // #64 - "0", // #65 - "0", // #66 - "0", // #67 - "0", // #68 - "0", // #69 - "0", // #70 - "0", // #71 - "0", // #72 - "0", // #73 - "0", // #74 - "0", // #75 - "0", // #76 - "0", // #77 - "0", // #78 - "E7c", // #79 - "P1B", // #80 - "F2B", // #81 - "L1H", // #82 - "D5i", // #83 - "m4Q", // #84 - "m1k", // #85 - "r52", // #86 - "k89", // #87 - "D2K", // #88 - "65r", // #89 - "f3K", // #90 - "13c", // #91 - "e1a", // #92 - "0", // #93 - "e6e", // #94 - "07F", // #95 - "m8k", // #96 - "79H", // #97 - "0", // #98 - "i1M", // #99 - "R3C", // #100 - "D98", // #101 - "m86", // #102 - "a66", // #103 - "0", // #104 - "E8T", // #105 - "J8n", // #106 - "N0b", // #107 - "586", // #108 - "p50", // #109 - "c9k", // #110 - "N0N", // #111 - "0", // #112 - "B1m", // #113 - "h7E", // #114 - "c05", // #115 - "K20", // #116 - "32D", // #117 - "F19", // #118 - "r4d", // #119 - "D2F", // #120 - "D0m", // #121 - "m6B", // #122 - "M0j", // #123 - "Q8G", // #124 - "A1t", // #125 - "p7J", // #126 - "t0H", // #127 - "M5i", // #128 - "j1L", // #129 - "e7i", // #130 - "T1E", // #131 - "85i", // #132 - "71H", // #133 - "20H", // #134 - "T9n", // #135 - "58B", // #136 - "J4R", // #137 - "93N", // #138 - "t0F", // #139 - "M7G", // #140 - "r4P", // #141 - "i1d", // #142 - "a22", // #143 - "M39", // #144 - "C23", // #145 - "816", // #146 - "E0M", // #147 - "T4b", // #148 - "L1L", // #149 - "i5m", // #150 - "P2R", // #151 - "t77", // #152 - "A5E", // #153 - "88e", // #154 - "k1b", // #155 - "m04", // #156 - "41b", // #157 - "B4k", // #158 - "J1M", // #159 - "H4M", // #160 - "C1D", // #161 - "12K", // #162 - "822", // #163 - "E1T", // #164 - "Q4H", // #165 - "k4d", // #166 - "k4J", // #167 - "L70", // #168 - "31f", // #169 - "G1P", // #170 - "34e", // #171 - "939", // #172 - "24F", // #173 - "43r", // #174 - "M81", // #175 - "01E", // #176 - "A0N", // #177 - "65f", // #178 - "Q6p", // #179 - "93R", // #180 - "r0i", // #181 - "A35", // #182 - "P40", // #183 - "G9R", // #184 - "c7C", // #185 - "P17", // #186 - "76f", // #187 - "99p", // #188 - "96E", // #189 - "p3E", // #190 - "h6t", // #191 - "n2L", // #192 - ]; - match name.parse::() { - Ok(n) => { - let name = IDARR[n as usize]; - return name; - }, - Err(_e) => { - panic!("Error while reading the names"); - } - } -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - env_logger::init(); - - let mut toio_connected = 0; - let mut verbose = false; - let mut search = false; - let mut ordered = false; - let mut ordernum = 0; - - //read command line arguments - let args: Vec = env::args().collect(); - let program = args[0].clone(); - - let mut opts = Options::new(); - opts.optopt("p", "port", "set receiving port", "PORT_NUMBER"); - opts.optopt("r", "remote", "set remote port", "IP:PORT_NUMBER"); - opts.optopt("i", "host_id", "set host id number", "ID_NUMBER"); - opts.optopt("n", "names", "connect to those cubes only", "AAA,BBB,CCC"); - opts.optopt("a", "axlab_id", "connect to those cubes only (#1, #2, #3)", "1,2,3"); - opts.optflag("h", "help", "print this help menu"); - opts.optflag("v", "verbose", "print more connection details"); - opts.optflag("s", "search", "search for toios without specifying IDs"); - opts.optflag("o", "ordered", "search for toios in order of IDs"); - - let matches = match opts.parse(&args[1..]) { - Ok(m) => { m } - Err(f) => { panic!("{}",f.to_string())} - }; - if matches.opt_present("h") { - print_usage(&program, opts); - return Ok(()); - } - if matches.opt_present("v") { - verbose = true; - } - if matches.opt_present("s") { - search = true; - } - - let port_number = matches.opt_str("p").unwrap_or("3334".to_string()); - let listening_address = format!("0.0.0.0:{}", port_number); - - //this will be filled with the names to connect to *if needed* - let possible_names = { - let mut res = Vec::new(); - match matches.opt_str("n") { - Some(names) => { - for part in names.split(",") { - if part.len() == 3 { - res.push(format!("toio Core Cube-{}", part)); - } else { - panic!("Error while reading the names"); - } - } - }, - None => {} - } - match matches.opt_str("a") { - Some(names) => { - for part in names.split(",") { - res.push(format!("toio Core Cube-{}", return_toio_id(part))); - } - }, - None => {} - } - - if !search && res.len() == 0 { - panic!("Toio IDs must be specified unless in search mode"); - } - - if res.len() > 0 { - ordernum = res.len(); - Some(res) - } else { - None - } - }; - - if matches.opt_present("o") { - ordered = true; - ordernum = 1; - } - - //the ID/addresses of the cubes - //let addresses: Arc>> = Arc::new(Mutex::new(Vec::new())); - let uuids: Arc>> = Arc::new(Mutex::new(Vec::new())); - let senders: Arc>>> = - Arc::new(Mutex::new(HashMap::new())); - - //OSC listening on port 3334 - let sock = UdpSocket::bind((listening_address).parse::().unwrap()).await?; - println!("OSC listening on port {}", port_number); - - let r = Arc::new(sock); - let s = r.clone(); - let (tx, mut rx) = mpsc::channel::<(Vec, SocketAddr)>(1_000); - - //Where to send packets - let remote = matches.opt_str("r").unwrap_or("127.0.0.1:3333".to_string()); - let remote_read = remote.parse::(); - let remote_addr = if remote_read.is_ok() { - remote_read.unwrap() - } else { - eprintln!( - "Remote address {} is wrongly formatted, use IP:PORT (127.0.0.1:3333)", - remote - ); - return Ok(()); - }; - - let host_id = matches - .opt_str("i") - .unwrap_or("0".to_string()) - .parse::() - .unwrap_or(0); - println!( - "Sending messages to {} prefixed by {}", - remote_addr, host_id - ); - - //Send OSC - tokio::spawn(async move { - //just one channel - while let Some((bytes, addr)) = rx.recv().await { - s.send_to(&bytes, &addr).await.unwrap(); - } - }); - - //Receive OSC - let mut buf = [0; 1024]; - //let addresses2 = addresses.clone(); - let uuids2 = uuids.clone(); - let senders2 = senders.clone(); - - tokio::spawn(async move { - while let Ok((len, addr)) = r.recv_from(&mut buf).await { - println!("{:?} bytes received from {:?}", len, addr); - let packet = rosc::decoder::decode(&buf[..len]).unwrap(); - match packet { - OscPacket::Message(msg) => { - if msg.args.len() > 0 { - let mut marg = 0; - if let OscType::Int(i) = msg.args[0] { - marg = i; - } - println!("Got a message for {}", marg); - - // find the address - let maybe_uuid = { - let storage_uuid = uuids2.lock().unwrap(); - if storage_uuid.len() > marg as usize { - Some(storage_uuid[marg as usize].clone()) - } else { - None - } - }; - if let Some(uuid) = maybe_uuid { - //try to get the channel and not breaking everything - let sender = { - let sends = senders2.lock().unwrap(); - sends.get(&uuid).map(|p| p.clone()) - //we drop the lock here because we *clone* - }; - if let Some(channel) = sender { - println!("Sending to..."); - channel.send(msg).await.unwrap(); - } - } - } - } - OscPacket::Bundle(bundle) => { - println!("OSC Bundle: {:?}", bundle); - } - } - } - }); - - let manager = Manager::new().await?; - - println!("Found Manager: {:?}", manager); - // get the first bluetooth adapter - // connect to the adapter - let adapters = manager.adapters().await.unwrap(); - println!("Found Adapter: "); - for adapter in adapters.iter() { - println!("Found Adapter: {}", adapter.adapter_info().await?); - } - - let central = adapters.into_iter().nth(0).unwrap(); - - //get the events from the central - let mut events = central.events().await?; - - // start scanning for devices - central - .start_scan(ScanFilter { - services: vec![TOIO_SERVICE_UUID], - }) - .await?; - - if verbose { - println!("Scanning for BTLE events on {:?}...",central); - } - else { - println!("Scanning for BTLE events..."); - } - - //Scan all the time - while let Some(event) = events.next().await { - //println!("event... {:?}", event); - let mut device_candidate: Option = None; - //Sometimes we miss the discovered event, so react on the DeviceUpdated as well - match event { - CentralEvent::DeviceUpdated(bd_addr) => { - device_candidate = Some(bd_addr); - } - CentralEvent::DeviceDiscovered(bd_addr) => { - device_candidate = Some(bd_addr); - } - CentralEvent::DeviceDisconnected(bd_addr) => { - println!("DeviceDisconnected: {:?}", bd_addr); - toio_connected -= 1; - print_toio_connected(toio_connected); - } - _ => {} - } - - - // - if let Some(bd_addr) = device_candidate { - let peripheral = central.peripheral(&bd_addr).await.unwrap(); - - let properties = peripheral.properties().await?.unwrap(); - let local_name = properties.local_name.unwrap_or("".to_string()); - - let services = properties.services; - let should_connect = if let Some(names) = &possible_names { - if ordernum > names.len() { - ordernum -= 1; - } - names[0 ..ordernum].contains(&local_name) - } else { - services.contains(&TOIO_SERVICE_UUID) - }; - - - //if (services.contains(&TOIO_SERVICE_UUID)) || possible_names.contains(&local_name) { - if should_connect { - if ordered { - ordernum += 1; - } - //we kave a toio cube! - let tx3 = tx.clone(); - if !(peripheral.is_connected().await?) { - println!("Device Connected: {}", local_name); - toio_connected += 1; - print_toio_connected(toio_connected); - - // Connect if we aren't already connected. - if let Err(err) = peripheral.connect().await { - eprintln!("Error connecting to peripheral, skipping: {}", err); - continue; - } - time::sleep(Duration::from_millis(200)).await; - // We should be connected now - let peripheral_id = peripheral.id(); - if verbose { - println!("connecting with Peripheral ID: {:?}", peripheral_id); - } - - //find the id for this cube - //let mut addr = addresses.lock().unwrap(); - let mut storage_uuid = uuids.lock().unwrap(); - - //do we already know that address? - let id = if let Some(index) = - storage_uuid.iter().position(|a| a == &peripheral_id) - { - index - } else { - //no, add it to the list - storage_uuid.push(peripheral_id.clone()); - storage_uuid.len() - 1 - }; - //get rid of the mutex lock - drop(storage_uuid); - //println!("Connecting to Cube {} address is {}", id, address); - - //creating the channels - let (tx, mut rx) = mpsc::channel::(1_000); - //saving one end to allow to receive OSC - let mut sends = senders.lock().unwrap(); - sends.insert(peripheral_id, tx); - drop(sends); - - let id2 = id; - let p2 = peripheral.clone(); - tokio::spawn(async move { - while let Some(message) = rx.recv().await { - println!("Received {:?} for cube {}", message, id2); - match message.addr.as_ref() { - "/motorbasic" => { - if message.args.len() == 5 { - //we should have 5 args - println!("Message received"); - let mut marg = [0; 5]; - for k in 0..5 { - if let OscType::Int(i) = message.args[k] { - marg[k] = i; - } - } - - let characteristic = Characteristic { - uuid: MOTOR_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE_WITHOUT_RESPONSE, - }; - let cmd = vec![ - 0x01, //motor - 0x01, //left - marg[1].abs() as u8, //forwards or backwards - marg[2].abs() as u8, //speed - 0x02, //right - marg[3].abs() as u8, //forwards or backwards - marg[4].abs() as u8, //speed - ]; - p2.write(&characteristic, &cmd, WriteType::WithoutResponse) - .await - .unwrap(); - } else { - //error - } - } - "/motorduration" => { - if message.args.len() == 6 { - //we should have 5 args - println!("Message received"); - let mut marg = [0; 6]; - for k in 0..6 { - if let OscType::Int(i) = message.args[k] { - marg[k] = i; - } - } - - let characteristic = Characteristic { - uuid: MOTOR_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE_WITHOUT_RESPONSE, - }; - let cmd = vec![ - 0x02, //motor - 0x01, //left - marg[1].abs() as u8, //forwards or backwards - marg[2].abs() as u8, //speed - 0x02, //right - marg[3].abs() as u8, //forwards or backwards - marg[4].abs() as u8, //speed - marg[5].abs() as u8, //duration - ]; - p2.write(&characteristic, &cmd, WriteType::WithoutResponse) - .await - .unwrap(); - } else { - //error - } - } - "/motortarget" => { - if message.args.len() == 5 { - //we should have 5 args - let mut marg = [0; 5]; - for k in 0..5 { - if let OscType::Int(i) = message.args[k] { - marg[k] = i; - } - } - let characteristic = Characteristic { - uuid: MOTOR_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE_WITHOUT_RESPONSE, - }; - let cmd = vec![ - 0x03, //motor target - 0x00, //control distinction value - 0x05, //timeout period - marg[1].abs() as u8, //movement type - 0x50, //maximum motor speed - 0x00, //motor speed changes - 0x00, //reserved - (marg[2].abs() & 0x00FF) as u8, //x value of target - ((marg[2].abs() & 0xFF00) >> 8) as u8, - (marg[3].abs() & 0x00FF) as u8, //y value of target - ((marg[3].abs() & 0xFF00) >> 8) as u8, - (marg[4].abs() & 0x00FF) as u8, //θ value of target - ((marg[4].abs() & 0xFF00) >> 8) as u8, - ]; - p2.write(&characteristic, &cmd, WriteType::WithoutResponse) - .await - .unwrap(); - } else if message.args.len() == 9 { - //we should have 9 args - let mut marg = [0; 9]; - for k in 0..9 { - if let OscType::Int(i) = message.args[k] { - marg[k] = i; - } - } - let characteristic = Characteristic { - uuid: MOTOR_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE_WITHOUT_RESPONSE, - }; - let cmd = vec![ - 0x03, //motor target - marg[1].abs() as u8, //control distinction value - marg[2].abs() as u8, //timeout period - marg[3].abs() as u8, //movement type - marg[4].abs() as u8, //maximum motor speed - marg[5].abs() as u8, //motor speed changes - 0x00, //reserved - (marg[6].abs() & 0x00FF) as u8, //x value of target - ((marg[6].abs() & 0xFF00) >> 8) as u8, - (marg[7].abs() & 0x00FF) as u8, //y value of target - ((marg[7].abs() & 0xFF00) >> 8) as u8, - (marg[8].abs() & 0x00FF) as u8, //θ value of target - ((marg[8].abs() & 0xFF00) >> 8) as u8, - ]; - p2.write(&characteristic, &cmd, WriteType::WithoutResponse) - .await - .unwrap(); - } else { - //error - } - } - "/multitargetsimple" => { - let mut marg = [0; 2]; - for k in 0..2 { - if let OscType::Int(i) = message.args[k] { - marg[k] = i; - } - } - let characteristic = Characteristic { - uuid: MOTOR_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE_WITHOUT_RESPONSE, - }; - let mut cmd = vec![ - 0x04, //multi target - 0x00, //control distinction value - 0x05, //timeout period - marg[1].abs() as u8, //movement type - 0x50, //maximum motor speed - 0x00, //motor speed changes - 0x00, //reserved - 0x01, //Write operation addition setting - ]; - - for k in 2..message.args.len() { - if let OscType::Int(i) = message.args[k] { - cmd.push((i.abs() & 0x00FF) as u8); - cmd.push(((i.abs() & 0xFF00) >> 8) as u8); - } - } - - p2.write(&characteristic, &cmd, WriteType::WithoutResponse) - .await - .unwrap(); - } - "/multitarget" => { - let mut marg = [0; 6]; - for k in 0..6 { - if let OscType::Int(i) = message.args[k] { - marg[k] = i; - } - } - let characteristic = Characteristic { - uuid: MOTOR_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE_WITHOUT_RESPONSE, - }; - let mut cmd = vec![ - 0x04, //multi target - marg[1].abs() as u8, //control distinction value - marg[2].abs() as u8, //timeout period - marg[3].abs() as u8, //movement type - marg[4].abs() as u8, //maximum motor speed - marg[5].abs() as u8, //motor speed changes - 0x00, //reserved - 0x01, //Write operation addition setting - ]; - - for k in 6..message.args.len() { - if let OscType::Int(i) = message.args[k] { - cmd.push((i.abs() & 0x00FF) as u8); - cmd.push(((i.abs() & 0xFF00) >> 8) as u8); - } - } - - p2.write(&characteristic, &cmd, WriteType::WithoutResponse) - .await - .unwrap(); - } - "/motoracceleration" => { - if message.args.len() == 8 { - //we should have 4 args - let mut marg = [0; 8]; - for k in 0..8 { - if let OscType::Int(i) = message.args[k] { - marg[k] = i; - } - } - let characteristic = Characteristic { - uuid: MOTOR_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE_WITHOUT_RESPONSE, - }; - let cmd = vec![ - 0x05, //motor control with acceleration - marg[1].abs() as u8, //translational speed - marg[2].abs() as u8, //acceleration - (marg[3].abs() & 0x00FF) as u8, //rotational velocity - ((marg[3].abs() & 0xFF00) >> 8) as u8, - marg[4].abs() as u8, //rotational direction - marg[5].abs() as u8, //direction - marg[6].abs() as u8, //Priority designation - marg[7].abs() as u8, //duration - ]; - p2.write(&characteristic, &cmd, WriteType::WithoutResponse) - .await - .unwrap(); - } - } - "/led" => { - if message.args.len() == 5 { - //we should have 5 args - let mut marg = [0; 5]; - for k in 0..5 { - if let OscType::Int(i) = message.args[k] { - marg[k] = i; - } - } - let characteristic = Characteristic { - uuid: LIGHT_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE_WITHOUT_RESPONSE, - }; - let cmd = vec![ - 0x03, //light - marg[1] as u8, //length - 0x01, //led - 0x01, //reserved - marg[2].abs() as u8, //red - marg[3].abs() as u8, //green - marg[4].abs() as u8, //blue - ]; - println!("{:?}", cmd); - p2.write(&characteristic, &cmd, WriteType::WithResponse) - .await - .unwrap(); - } else { - let mut marg = [0; 3]; - for k in 0..3 { - if let OscType::Int(i) = message.args[k] { - marg[k] = i; - } - } - let characteristic = Characteristic { - uuid: LIGHT_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE_WITHOUT_RESPONSE, - }; - let mut cmd = vec![ - 0x04, //midi - marg[1].abs() as u8, //repetitions - marg[2].abs() as u8, //operations - ]; - - for k in 3..message.args.len() { - if let OscType::Int(i) = message.args[k] { - cmd.push(i.abs() as u8); - } - } - - println!("{:?}", cmd); - p2.write(&characteristic, &cmd, WriteType::WithResponse) - .await - .unwrap(); - } - } - "/sound" => { - if message.args.len() == 3 { - //we should have 3 args - let mut marg = [0; 3]; - for k in 0..3 { - if let OscType::Int(i) = message.args[k] { - marg[k] = i; - } - } - let characteristic = Characteristic { - uuid: SOUND_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE_WITHOUT_RESPONSE, - }; - let cmd = vec![ - 0x02, //sound - marg[1] as u8, //sound effect ID - marg[2] as u8 //volume - ]; - println!("{:?}", cmd); - p2.write(&characteristic, &cmd, WriteType::WithResponse) - .await - .unwrap(); - } else { - //error - } - } - "/midi" => { - if message.args.len() == 4 { - //we should have 5 args - let mut marg = [0; 4]; - for k in 0..4 { - if let OscType::Int(i) = message.args[k] { - marg[k] = i; - } - } - let characteristic = Characteristic { - uuid: SOUND_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE_WITHOUT_RESPONSE, - }; - let cmd = vec![ - 0x03, //midi - 0x01, //repetitions - 0x01, //operations - marg[1].abs() as u8, //duration - marg[2].abs() as u8, //MIDI note number - marg[3].abs() as u8, //volume - ]; - println!("{:?}", cmd); - p2.write(&characteristic, &cmd, WriteType::WithResponse) - .await - .unwrap(); - } else { - let mut marg = [0; 3]; - for k in 0..3 { - if let OscType::Int(i) = message.args[k] { - marg[k] = i; - } - } - let characteristic = Characteristic { - uuid: SOUND_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE_WITHOUT_RESPONSE, - }; - let mut cmd = vec![ - 0x03, //midi - marg[1].abs() as u8, //repetitions - marg[2].abs() as u8, //operations - ]; - - for k in 3..message.args.len() { - if let OscType::Int(i) = message.args[k] { - cmd.push(i.abs() as u8); - } - } - - println!("{:?}", cmd); - p2.write(&characteristic, &cmd, WriteType::WithResponse) - .await - .unwrap(); - } - } - "/motion" => { - let characteristic = Characteristic { - uuid: MOTION_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE, - }; - let cmd = vec![ - 0x81, - ]; - //println!("{:?}", cmd); - p2.write(&characteristic, &cmd, WriteType::WithResponse) - .await - .unwrap(); - } - "/magnetic" => { - let characteristic = Characteristic { - uuid: MOTION_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE, - }; - let cmd = vec![ - 0x82 - ]; - //println!("{:?}", cmd); - p2.write(&characteristic, &cmd, WriteType::WithResponse) - .await - .unwrap(); - } - "/postureeuler" => { - let characteristic = Characteristic { - uuid: MOTION_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE, - }; - let cmd = vec![ - 0x83, - 0x01 - ]; - //println!("{:?}", cmd); - p2.write(&characteristic, &cmd, WriteType::WithResponse) - .await - .unwrap(); - } - "/posturequaternion" => { - let characteristic = Characteristic { - uuid: MOTION_CHARACTERISTIC_UUID, - service_uuid: TOIO_SERVICE_UUID, - properties: CharPropFlags::WRITE, - }; - let cmd = vec![ - 0x83, - 0x02 - ]; - //println!("{:?}", cmd); - p2.write(&characteristic, &cmd, WriteType::WithResponse) - .await - .unwrap(); - } - _ => {} - } - } - }); - - //let is_connected = peripheral.is_connected().await?; - //println!("Peripheral {} connected: {}", address, is_connected); - - //println!("Discovering peripheral characteristics..."); - peripheral.discover_services().await.unwrap(); - let chars = peripheral.characteristics(); - for characteristic in chars.into_iter() { - if verbose { - println!("Checking {:?}", characteristic); - } - if characteristic.uuid == POSITION_CHARACTERISTIC_UUID - && characteristic.properties.contains(CharPropFlags::NOTIFY) - { - if verbose { - println!( - "Subscribing to position characteristic {:?}", - characteristic.uuid - ); - } - peripheral.subscribe(&characteristic).await?; - } - if characteristic.uuid == BUTTON_CHARACTERISTIC_UUID - && characteristic.properties.contains(CharPropFlags::NOTIFY) - { - if verbose { - println!( - "Subscribing to button characteristic {:?}", - characteristic.uuid - ); - } - peripheral.subscribe(&characteristic).await?; - } - if characteristic.uuid == MOTOR_CHARACTERISTIC_UUID - && characteristic.properties.contains(CharPropFlags::NOTIFY) - { - if verbose { - println!( - "Subscribing to motor characteristic {:?}", - characteristic.uuid - ); - } - peripheral.subscribe(&characteristic).await?; - } - if characteristic.uuid == MOTION_CHARACTERISTIC_UUID - && characteristic.properties.contains(CharPropFlags::NOTIFY) - { - if verbose { - println!( - "Subscribing to motion characteristic {:?}", - characteristic.uuid - ); - } - peripheral.subscribe(&characteristic).await?; - } - if characteristic.uuid == BATTERY_CHARACTERISTIC_UUID - && characteristic.properties.contains(CharPropFlags::NOTIFY) - { - if verbose { - println!( - "Subscribing to battery characteristic {:?}", - characteristic.uuid - ); - } - peripheral.subscribe(&characteristic).await?; - } - } - //after scanning all chars and subscribing - //we can expect to get notifications as a stream - //TODO figure a way to end the task on disconnect - let mut notification_stream = peripheral.notifications().await.unwrap(); - tokio::spawn(async move { - while let Some(data) = notification_stream.next().await { - match data.uuid { - POSITION_CHARACTERISTIC_UUID => { - //data is - // data[0] is 1 for read, 3 for off - // data[1] data[2] is x - // data[3] data[4] is y - // data[5] data[6] is angle - if data.value[0] == 1 { - let x = (data.value[2] as u32) << 8 | data.value[1] as u32; - let y = (data.value[4] as u32) << 8 | data.value[3] as u32; - let angle = - (data.value[6] as u32) << 8 | data.value[5] as u32; - let realx = - (data.value[8] as u32) << 8 | data.value[7] as u32; - let realy = - (data.value[10] as u32) << 8 | data.value[9] as u32; - //println!( "Received data from cube {}: {},{} {}", id, x, y, angle ); - let msg = - encoder::encode(&OscPacket::Message(OscMessage { - addr: "/position".to_string(), - args: vec![ - OscType::Int(host_id), - OscType::Int(id as i32), - OscType::Int(x as i32), - OscType::Int(y as i32), - OscType::Int(angle as i32), - OscType::Int(realx as i32), - OscType::Int(realy as i32), - ], - })) - .unwrap(); - - tx3.send((msg, remote_addr)).await.unwrap(); - } - } - BUTTON_CHARACTERISTIC_UUID => { - let button = data.value[1]; - let msg = encoder::encode(&OscPacket::Message(OscMessage { - addr: "/button".to_string(), - args: vec![ - OscType::Int(host_id), - OscType::Int(id as i32), - OscType::Int(button as i32), - ], - })) - .unwrap(); - - tx3.send((msg, remote_addr)).await.unwrap(); - } - MOTOR_CHARACTERISTIC_UUID => { - if data.value[0] == 0x83 || data.value[0] == 0x84 { - let control = data.value[1]; - let response = data.value[2]; - let msg = encoder::encode(&OscPacket::Message(OscMessage { - addr: "/motorresponse".to_string(), - args: vec![ - OscType::Int(host_id), - OscType::Int(id as i32), - OscType::Int(control as i32), - OscType::Int(response as i32), - ], - })) - .unwrap(); - - tx3.send((msg, remote_addr)).await.unwrap(); - } else if data.value[0] == 0xe0 { - let speedleft = data.value[1]; - let speedright = data.value[2]; - let msg = encoder::encode(&OscPacket::Message(OscMessage { - addr: "/speedresponse".to_string(), - args: vec![ - OscType::Int(host_id), - OscType::Int(id as i32), - OscType::Int(speedleft as i32), - OscType::Int(speedright as i32), - ], - })) - .unwrap(); - - tx3.send((msg, remote_addr)).await.unwrap(); - - } - } - MOTION_CHARACTERISTIC_UUID => { - if data.value[0] == 0x01 { - let flatness = data.value[1]; - let hit = data.value[2]; - let double_tap = data.value[3]; - let face_up = data.value[4]; - let shake_level = data.value[5]; - - let msg = encoder::encode(&OscPacket::Message(OscMessage { - addr: "/motion".to_string(), - args: vec![ - OscType::Int(host_id), - OscType::Int(id as i32), - OscType::Int(flatness as i32), - OscType::Int(hit as i32), - OscType::Int(double_tap as i32), - OscType::Int(face_up as i32), - OscType::Int(shake_level as i32), - ], - })) - .unwrap(); - - tx3.send((msg, remote_addr)).await.unwrap(); - } else if data.value[0] == 0x02 { - let state = data.value[1]; - let strength = data.value[2]; - let forcex = data.value[3]; - let forcey = data.value[4]; - let forcez = data.value[5]; - - let msg = encoder::encode(&OscPacket::Message(OscMessage { - addr: "/magnetic".to_string(), - args: vec![ - OscType::Int(host_id), - OscType::Int(id as i32), - OscType::Int(state as i32), - OscType::Int(strength as i32), - OscType::Int(forcex as i32), - OscType::Int(forcey as i32), - OscType::Int(forcez as i32) - ], - })) - .unwrap(); - - tx3.send((msg, remote_addr)).await.unwrap(); - } else if data.value[0] == 0x03 { - if data.value[1] == 0x01 { - let roll = data.value[2]; - let pitch = data.value[2]; - let yaw = data.value[3]; - - let msg = encoder::encode(&OscPacket::Message(OscMessage { - addr: "/postureeuler".to_string(), - args: vec![ - OscType::Int(host_id), - OscType::Int(id as i32), - OscType::Int(roll as i32), - OscType::Int(pitch as i32), - OscType::Int(yaw as i32) - ], - })) - .unwrap(); - - tx3.send((msg, remote_addr)).await.unwrap(); - } else { - let w = data.value[2]; - let x = data.value[2]; - let y = data.value[3]; - let z = data.value[3]; - - let msg = encoder::encode(&OscPacket::Message(OscMessage { - addr: "/posturequaternion".to_string(), - args: vec![ - OscType::Int(host_id), - OscType::Int(id as i32), - OscType::Int(w as i32), - OscType::Int(x as i32), - OscType::Int(y as i32), - OscType::Int(z as i32) - ], - })) - .unwrap(); - - tx3.send((msg, remote_addr)).await.unwrap(); - } - } - } - BATTERY_CHARACTERISTIC_UUID => { - let battery = data.value[0]; - let msg = encoder::encode(&OscPacket::Message(OscMessage { - addr: "/battery".to_string(), - args: vec![ - OscType::Int(host_id), - OscType::Int(id as i32), - OscType::Int(battery as i32), - ], - })) - .unwrap(); - - tx3.send((msg, remote_addr)).await.unwrap(); - } - _ => {} - } - } - }); - } - } else { - // println!("Device Found: {}", local_name); - } - } - } - - Ok(()) -} diff --git a/toio_processing/toio_processing.pde b/toio_processing/main.pde similarity index 90% rename from toio_processing/toio_processing.pde rename to toio_processing/main.pde index fa0a8d2b..a9852af8 100644 --- a/toio_processing/toio_processing.pde +++ b/toio_processing/main.pde @@ -5,7 +5,7 @@ import netP5.*; //constants //The soft limit on how many toios a laptop can handle is in the 10-12 range //the more toios you connect to, the more difficult it becomes to sustain the connection -int nCubes = 2; +int nCubes = 4; int cubesPerHost = 12; int maxMotorSpeed = 115; int xOffset; @@ -22,6 +22,7 @@ boolean WindowsMode = false; //When you enable this, it will check for connectio int framerate = 30; int[] matDimension = {45, 45, 455, 455}; +String[] hosts = {"127.0.0.1"}; //for OSC @@ -38,11 +39,12 @@ void settings() { void setup() { - //launch OSC sercer + //create OSC servers oscP5 = new OscP5(this, 3333); - server = new NetAddress[1]; - server[0] = new NetAddress("127.0.0.1", 3334); - + server = new NetAddress[hosts.length]; + for (int i = 0; i < hosts.length; i++) { + server[i] = new NetAddress(hosts[i], 3334); + } //create cubes cubes = new Cube[nCubes]; for (int i = 0; i< nCubes; ++i) { @@ -55,13 +57,11 @@ void setup() { //do not send TOO MANY PACKETS //we'll be updating the cubes every frame, so don't try to go too high frameRate(framerate); - if(WindowsMode){ - check_connection(); + if (WindowsMode){ + check_connection(); } } - - void draw() { //START TEMPLATE/DEBUG VIEW background(255); @@ -96,4 +96,4 @@ void draw() { //END TEMPLATE/DEBUG VIEW //INSERT YOUR CODE HERE! -} +} \ No newline at end of file diff --git a/toio_processing/osc.pde b/toio_processing/osc.pde index e0eccf93..9dfb0462 100644 --- a/toio_processing/osc.pde +++ b/toio_processing/osc.pde @@ -256,96 +256,81 @@ void postureRequest(boolean euler, int[] cubeId) { } } +int getHostId(String addr) { + for (int i = 0; i < hosts.length; i++) { + if (addr == hosts[i]) { + return i; + } + } + + return 0; +} + void oscEvent(OscMessage msg) { + int id = (int) msg.arguments()[0]; + int hostId = getHostId(msg.address()); + id = cubesPerHost * hostId + id; + if (msg.checkAddrPattern("/position")) { //this collects position information - int hostId = msg.get(0).intValue(); - int id = msg.get(1).intValue(); - int posx = msg.get(2).intValue(); - int posy = msg.get(3).intValue(); - int postheta = msg.get(4).intValue(); - - id = cubesPerHost*hostId + id; + int posx = (int) msg.arguments()[1]; + int posy = (int) msg.arguments()[2]; + int postheta = (int) msg.arguments()[3]; cubes[id].onPositionUpdate(posx, posy, postheta); } else if (msg.checkAddrPattern("/battery")) { //this collects battery value information - int hostId = msg.get(0).intValue(); - int id = msg.get(1).intValue(); - int battery = msg.get(2).intValue(); - - id = cubesPerHost * hostId + id; + int battery = (int) msg.arguments()[1]; cubes[id].onBatteryUpdate(battery); } else if (msg.checkAddrPattern("/motion")) { //this collects motion sensor information - int hostId = msg.get(0).intValue(); - int id = msg.get(1).intValue(); - int flatness = msg.get(2).intValue(); - int hit = msg.get(3).intValue(); - int double_tap = msg.get(4).intValue(); - int face_up = msg.get(5).intValue(); - int shake_level = msg.get(6).intValue(); - - id = cubesPerHost*hostId + id; + int flatness = (int) msg.arguments()[1]; + int hit = (int) msg.arguments()[2]; + int double_tap = (int) msg.arguments()[3]; + int face_up = (int) msg.arguments()[4]; + int shake_level = (int) msg.arguments()[5]; cubes[id].onMotionUpdate(flatness, hit, double_tap, face_up, shake_level); } else if (msg.checkAddrPattern("/magnetic")) { //this collects magnetic sensor information - int hostId = msg.get(0).intValue(); - int relid = msg.get(1).intValue(); - int id = cubesPerHost*hostId + relid; - int state = msg.get(2).intValue(); - int strength = msg.get(3).intValue(); - int forcex = msg.get(4).intValue(); - int forcey = msg.get(5).intValue(); - int forcez = msg.get(6).intValue(); - - id = cubesPerHost*hostId + id; + int state = (int) msg.arguments()[1]; + int strength = (int) msg.arguments()[2]; + int forcex = (int) msg.arguments()[3]; + int forcey = (int) msg.arguments()[4]; + int forcez = (int) msg.arguments()[5]; cubes[id].onMagneticUpdate(state, strength, forcex, forcey, forcez); } else if (msg.checkAddrPattern("/postureeuler")) { //this collects posture sensor information (in eulers) - int hostId = msg.get(0).intValue(); - int id = msg.get(1).intValue(); - int roll = msg.get(2).intValue(); - int pitch = msg.get(3).intValue(); - int yaw = msg.get(4).intValue(); - - id = cubesPerHost*hostId + id; + int roll = (int) msg.arguments()[1]; + int pitch = (int) msg.arguments()[2]; + int yaw = (int) msg.arguments()[3]; cubes[id].onPostureUpdate(roll, pitch, yaw); } else if (msg.checkAddrPattern("/posturequaternion")) { //this collects posture sensor information (in quaternion) - int hostId = msg.get(0).intValue(); - int id = msg.get(1).intValue(); - int w = msg.get(2).intValue(); - int x = msg.get(3).intValue(); - int y = msg.get(4).intValue(); - int z = msg.get(5).intValue(); - - id = cubesPerHost*hostId + id; + int w = (int) msg.arguments()[1]; + int x = (int) msg.arguments()[2]; + int y = (int) msg.arguments()[3]; + int z = (int) msg.arguments()[4]; cubes[id].onPostureUpdate(w, x, y, z); } else if (msg.checkAddrPattern("/button")) { //this collects button information - int hostId = msg.get(0).intValue(); - int relid = msg.get(1).intValue(); - int pressValue = msg.get(2).intValue(); - - int id = cubesPerHost*hostId + relid; + int pressValue = (int) msg.arguments()[1]; if (pressValue == 0) { cubes[id].onButtonUp(); @@ -355,17 +340,14 @@ void oscEvent(OscMessage msg) { } else if (msg.checkAddrPattern("/motorresponse")) { - //this collects button information - int hostId = msg.get(0).intValue(); - int relid = msg.get(1).intValue(); - int control = msg.get(2).intValue(); - int response = msg.get(3).intValue(); - - int id = cubesPerHost*hostId + relid; + //this collects motor response information + int control = (int) msg.arguments()[1]; + int response = (int) msg.arguments()[2]; cubes[id].onMotorResponse(control, response); } } + void check_connection(){ long now = System.currentTimeMillis(); for (int i = 0; i < nCubes; i++) { diff --git a/toio_processing/sketch.properties b/toio_processing/sketch.properties index 654fcd1b..e0fa0cf8 100644 --- a/toio_processing/sketch.properties +++ b/toio_processing/sketch.properties @@ -1 +1 @@ -main=toio_processing.pde +main=main.pde diff --git a/toio_rust/Cargo.lock b/toio_rust/Cargo.lock new file mode 100644 index 00000000..da7782ba --- /dev/null +++ b/toio_rust/Cargo.lock @@ -0,0 +1,1532 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-trait" +version = "0.1.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "bluez-async" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce7d4413c940e8e3cb6afc122d3f4a07096aca259d286781128683fc9f39d9b" +dependencies = [ + "async-trait", + "bitflags 2.5.0", + "bluez-generated", + "dbus", + "dbus-tokio", + "futures", + "itertools 0.10.5", + "log", + "serde", + "serde-xml-rs", + "thiserror", + "tokio", + "uuid", +] + +[[package]] +name = "bluez-generated" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d1c659dbc82f0b8ca75606c91a371e763589b7f6acf36858eeed0c705afe367" +dependencies = [ + "dbus", +] + +[[package]] +name = "btleplug" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790e133b9d27f6dbd1289e046e735cef97fb0b4c840f18db52c959521dfb8145" +dependencies = [ + "async-trait", + "bitflags 1.3.2", + "bluez-async", + "cocoa", + "dashmap", + "dbus", + "futures", + "jni", + "jni-utils", + "libc", + "log", + "objc", + "once_cell", + "static_assertions", + "thiserror", + "tokio", + "tokio-stream", + "uuid", + "windows", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "cocoa" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "color-eyre" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.5.0", + "crossterm_winapi", + "futures-core", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "futures-channel", + "futures-util", + "libc", + "libdbus-sys", + "winapi", +] + +[[package]] +name = "dbus-tokio" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007688d459bc677131c063a3a77fb899526e17b7980f390b69644bdbc41fad13" +dependencies = [ + "dbus", + "libc", + "tokio", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "hashbrown" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fa0ae458eb99874f54c09f4f9174f8b45fb87e854536a4e608696247f0c23" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jni-utils" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "259e9f2c3ead61de911f147000660511f07ab00adeed1d84f5ac4d0386e7a6c4" +dependencies = [ + "dashmap", + "futures", + "jni", + "log", + "once_cell", + "static_assertions", + "uuid", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ratatui" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8" +dependencies = [ + "bitflags 2.5.0", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "itertools 0.12.1", + "lru", + "paste", + "stability", + "strum", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "rosc" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e63d9e6b0d090be1485cf159b1e04c3973d2d3e1614963544ea2ff47a4a981" +dependencies = [ + "byteorder", + "nom", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustversion" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-xml-rs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782" +dependencies = [ + "log", + "serde", + "thiserror", + "xml-rs", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stability" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.53", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "toio" +version = "0.1.0" +dependencies = [ + "btleplug", + "clap", + "color-eyre", + "crossterm", + "futures", + "ratatui", + "rosc", + "tokio", + "tokio-util", + "uuid", +] + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "xml-rs" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] diff --git a/toio_rust/Cargo.toml b/toio_rust/Cargo.toml new file mode 100644 index 00000000..65b06a12 --- /dev/null +++ b/toio_rust/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "toio" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +btleplug = "0.10" +uuid = "1.8" +tokio = { version = "1.36", features = ["full"] } +futures = "0.3.30" +rosc = "0.10.0" +color-eyre = "0.6.3" +crossterm = { version = "0.27.0", features = ["event-stream"] } +ratatui = "0.26.1" +tokio-util = "0.7.10" +clap = { version = "4.4", features = ["derive"] } + +[profile.dev] +opt-level = 3 +debug = 1 + + +[profile.release] +debug = 1 \ No newline at end of file diff --git a/toio_rust/src/main.rs b/toio_rust/src/main.rs new file mode 100644 index 00000000..317fdb88 --- /dev/null +++ b/toio_rust/src/main.rs @@ -0,0 +1,273 @@ +mod osc; +mod toio; +mod ui; + +use osc::*; +use toio::*; +use ui::*; + +use std::error::Error; +use std::net::UdpSocket; +use std::process; +use std::sync::Arc; +use std::time::SystemTime; +use std::vec; + +use clap::Parser; +use futures::future::join_all; +use futures::future::Either::{Left, Right}; +use tokio::sync::RwLock; + +#[derive(Parser)] +#[command(name = "toio")] +struct Args { + /// Set receiving port + #[arg(short, long)] + port: Option, + + /// Set remote port + #[arg(short, long)] + remote: Option, + + /// Show terminal UI + #[arg(short, long)] + terminal: bool, + + /// Use unfiltered search + #[arg(short, long)] + search: bool, + + /// Use ordered search + #[arg(short, long)] + ordered: bool, + + /// Filter toios by IDs (comma-separated list e.g. 1,2,3) + #[arg(short, long, value_delimiter = ',')] + axlab_id: Option>, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let args = Args::parse(); + + // create scanner and array of toios + let scanner = match args.axlab_id.clone() { + Some(filter) => { + if args.terminal { + if args.ordered { + println!("Running Ordered Search for Toios: {:?}", filter.clone()); + } else { + println!("Running Unordered Search for Toios: {:?}", filter.clone()); + } + } + ToioScanner::new_with_filter(args.ordered, filter.clone()).await? + } + None => { + if !args.search { + println!("You must provide IDs when not in search mode"); + process::exit(0); + } + + if args.terminal { + println!("Running Unfiltered Search for Toios"); + } + ToioScanner::new().await? + } + }; + // let scanner = ToioScanner::new_with_filter(true, vec![3, 100]).await?; + let mut toios = scanner.search().await?; + let connected: Arc>>>> = Arc::new(RwLock::new(vec![])); + + // server and client address + let host_addr = format!("0.0.0.0:{}", args.port.unwrap_or(3334)); + let to_addr = format!("0.0.0.0:{}", args.remote.unwrap_or(3333)); + if args.terminal { + println!( + "Listening on port {} and sending to port {}", + host_addr, to_addr + ) + } + + // open socket and create buffer + let socket = Arc::new(UdpSocket::bind(&host_addr)?); + let mut buf = [0u8; rosc::decoder::MTU]; + + // whenever a message is recieved through OSC, forward to toio + let sock = socket.clone(); + let connected_clone = connected.clone(); + tokio::spawn(async move { + // let mut now = SystemTime::now(); + while let Ok(size) = sock.recv(&mut buf) { + if let Ok((_, packet)) = rosc::decoder::decode_udp(&buf[..size]) { + if let Some((toionum, cmd)) = handle_packet(packet) { + let connected_read = connected_clone.read().await; + if toionum < connected_read.len() { + let toio = connected_read[toionum].read().await; + + let last_command = toio.get_last_command(); + let mut last_command_write = last_command.write().await; + *last_command_write = Some(SystemTime::now()); + + toio.toio.send_command(cmd).await; + } + } + } + } + }); + + // whenever we connect to a toio, add it to the list + let connected_clone = connected.clone(); + tokio::spawn(async move { + while let Some(peripheral_update) = toios.next().await { + match peripheral_update { + Left(toio_peripheral) => { + // clone socket + let sock = socket.clone(); + + // listen for updates from toio + let mut updates = toio_peripheral.updates().await.unwrap(); + + // create instance of Toio to record toio info + let mut toio = Toio::new(toio_peripheral); + if args.terminal { + println!("Toio Connected: {}", toio.id); + } + + let battery = toio.get_battery(); + let last_update = toio.get_last_update(); + + // request permission to write to list of connected toios + let mut connected_write = connected_clone.write().await; + let id = connected_write.len(); + + // start process to listen for messages from toio + let toio_channel = tokio::spawn({ + let to_addr = to_addr.clone(); + async move { + while let Some(update) = updates.next().await { + // if it is a battery update, record it in the Toio + if let Update::Battery { level } = update { + let mut battery = battery.write().await; + *battery = Some(level); + } + + // record time of update + let mut last_update = last_update.write().await; + *last_update = Some(SystemTime::now()); + + send_packet(&sock, &to_addr, id, update, args.terminal); + } + } + }); + + toio.add_channel(toio_channel); + connected_write.push(Arc::new(RwLock::new(toio))); + } + Right(peripheral_id) => { + // request permission to write to list of connected toios + let connected_write = connected_clone.write().await; + + // Collect all peripheral_ids with their indices + let ids = join_all(connected_write.iter().map(|x| async { + let toio = x.read().await; + toio.toio.peripheral_id.clone() + })) + .await; + + let toio_id = ids.iter().position(|id| *id == peripheral_id); + + // Find the index of the matching peripheral_id + if let Some(idx) = toio_id { + let mut toio = connected_write[idx].write().await; + toio.disconnect(); + if args.terminal { + println!("Toio Disconnected: {}", toio.id); + } + }; + } + } + } + }); + + // // start TUI process + let mut terminal: ToioUI = None; + if !args.terminal { + terminal = setup_terminal()?; + } + + // update UI from all of the toios + let connected_clone = connected.clone(); + loop { + let connected_read = connected_clone.read().await; + + // get info from all of the toios + let toio_info = join_all(connected_read.iter().map(|toio_guard| async { + let toio = toio_guard.read().await; + let name = toio.name.clone(); + let id = toio.id.clone(); + let connected = toio.is_connected().await; + + // get battery level + let battery_string = if let Some(level) = *toio.battery.read().await { + format!("{}", level) + } else { + "N/A".to_string() + }; + + // get time of last update + let last_update_string = if let Some(last) = *toio.last_update.read().await { + if let Ok(time) = last.elapsed() { + if time.as_millis() < 50 { + "<50ms".to_string() + } else if time.as_secs() < 1 { + format!(">{}ms", time.as_millis() - (time.as_millis() % 100)) + } else { + format!("{}s", time.as_secs()) + } + } else { + "N/A".to_string() + } + } else { + "N/A".to_string() + }; + + // get time of last command + let last_command_string = if let Some(last) = *toio.last_command.read().await { + if let Ok(time) = last.elapsed() { + if time.as_millis() < 50 { + "<50ms".to_string() + } else if time.as_secs() < 1 { + format!(">{}ms", time.as_millis() - (time.as_millis() % 100)) + } else { + format!("{}s", time.as_secs()) + } + } else { + "N/A".to_string() + } + } else { + "N/A".to_string() + }; + + ( + name, + id, + battery_string, + last_update_string, + last_command_string, + connected, + ) + })) + .await; + + // update UI + if let Some(ref mut toio_ui) = terminal { + toio_ui.draw(ui(toio_info, args.axlab_id.clone()))?; + } + + // // exit terminal if "Q" key is pressed + if handle_events()? { + exit_terminal()?; + process::exit(0); + } + } +} diff --git a/toio_rust/src/osc.rs b/toio_rust/src/osc.rs new file mode 100644 index 00000000..0b90f0b5 --- /dev/null +++ b/toio_rust/src/osc.rs @@ -0,0 +1,227 @@ +use std::io::{self}; +use std::net::UdpSocket; + +use std::vec; + +use rosc::encoder; +use rosc::{OscMessage, OscPacket, OscType}; + +use crossterm::event::{self, Event, KeyCode}; + +use crate::toio::*; + +pub fn handle_events() -> io::Result { + if event::poll(std::time::Duration::from_millis(50))? { + if let Event::Key(key) = event::read()? { + if key.kind == event::KeyEventKind::Press && key.code == KeyCode::Char('q') { + return Ok(true); + } + } + } + Ok(false) +} + +pub fn handle_packet(packet: OscPacket) -> Option<(usize, Command)> { + match packet { + OscPacket::Message(msg) => { + let mut vals: Vec = msg + .args + .iter() + .flat_map(|val| match val { + OscType::Int(i) => Some(*i), + _ => None, + }) + .collect(); + + // extract command + let cmd: Option = match msg.addr.as_ref() { + "/motorbasic" => Some(Command::MotorControl { + left_direction: vals[1] as u8, + left_speed: vals[2] as u8, + right_direction: vals[3] as u8, + right_speed: vals[4] as u8, + }), + "/motorduration" => Some(Command::MotorDuration { + left_direction: vals[1] as u8, + left_speed: vals[2] as u8, + right_direction: vals[3] as u8, + right_speed: vals[4] as u8, + duration: vals[5] as u8, + }), + "/motortarget" => Some(Command::MotorTarget { + control: vals[1] as u8, + timeout: vals[2] as u8, + move_type: vals[3] as u8, + max_speed: vals[4] as u8, + speed_change: vals[5] as u8, + x_target: vals[6] as u16, + y_target: vals[7] as u16, + theta_target: vals[8] as u16, + }), + "/motoracceleration" => Some(Command::MotorAcceleration { + velocity: vals[1] as u8, + acceleration: vals[2] as u8, + rotational_velocity: vals[3] as u16, + rotational_direction: vals[4] as u8, + direction: vals[5] as u8, + priority: vals[6] as u8, + duration: vals[7] as u8, + }), + "/multitarget" => Some(Command::MultiTarget { + control: vals[1] as u8, + timeout: vals[2] as u8, + move_type: vals[3] as u8, + max_speed: vals[4] as u8, + speed_change: vals[5] as u8, + op_add: 1, + targets: vals + .split_off(6) + .chunks(3) + .map(|target| TargetCommand { + x_target: target[0] as u16, + y_target: target[1] as u16, + theta_target: target[2] as u16, + }) + .collect(), + }), + "/led" => Some(Command::Led { + duration: vals[1] as u8, + red: vals[2] as u8, + green: vals[3] as u8, + blue: vals[4] as u8, + }), + "/multiLed" => Some(Command::MultiLed { + repetitions: vals[1] as u8, + lights: vals + .split_off(2) + .chunks(4) + .map(|light| LedCommand { + duration: light[0] as u8, + red: light[1] as u8, + green: light[2] as u8, + blue: light[3] as u8, + }) + .collect(), + }), + "/sound" => Some(Command::Sound { + sound_effect: vals[1] as u8, + volume: vals[2] as u8, + }), + "/midi" => Some(Command::Midi { + repetitions: vals[1] as u8, + notes: vals + .split_off(2) + .chunks(3) + .map(|note| MidiCommand { + duration: note[0] as u8, + note: note[1] as u8, + volume: note[2] as u8, + }) + .collect(), + }), + + _ => None, + }; + + // Return pair of (toioID, pair) + return cmd.map(|cmd| (vals[0] as usize, cmd)); + } + _ => None, + } +} + +pub fn send_packet( + socket: &UdpSocket, + to_addr: &str, + id: usize, + update: Update, + show_terminal: bool, +) { + let vals: Option<(&str, Vec)> = match update { + Update::Position { + x_center, + y_center, + theta, + .. + } => Some(( + "/position", + vec![x_center as i32, y_center as i32, theta as i32], + )), + Update::Battery { level } => Some(("/battery", vec![level as i32])), + Update::Button { pressed } => Some(("/button", vec![if pressed { 0x00 } else { 0x80 }])), + Update::Motion { + horizontal, + collision, + double_tap, + posture, + shake, + } => Some(( + "/motion", + vec![ + horizontal as i32, + collision as i32, + double_tap as i32, + posture as i32, + shake as i32, + ], + )), + Update::MotorTargetResponse { control, response } => { + Some(("/motorresponse", vec![control as i32, response as i32])) + } + Update::MultiTargetResponse { control, response } => { + Some(("/motorresponse", vec![control as i32, response as i32])) + } + Update::Standard { standard, theta } => { + Some(("/standard", vec![standard as i32, theta as i32])) + } + Update::PositionMissed => Some(("/positionMissed", vec![])), + Update::StandardMissed => Some(("/standardMissed", vec![])), + Update::MotorSpeed { + left_speed, + right_speed, + } => Some(("/motorSpeed", vec![left_speed as i32, right_speed as i32])), + Update::PostureEuler { roll, pitch, yaw } => { + Some(("/postureEuler", vec![roll as i32, pitch as i32, yaw as i32])) + } + Update::PostureQuaternion { w, x, y, z } => Some(( + "/PostureQuaternion", + vec![w as i32, x as i32, y as i32, z as i32], + )), + // Update::PostureHighPrecisionEuler { .. } => todo!(), + Update::Magnetic { + state, + strength, + forcex, + forcey, + forcez, + } => Some(( + "/magnetic", + vec![ + state as i32, + strength as i32, + forcex as i32, + forcey as i32, + forcez as i32, + ], + )), + _ => None, + }; + + if let Some((addr, args)) = vals { + let msg = encoder::encode(&OscPacket::Message(OscMessage { + addr: addr.to_string(), + args: vec![id as i32] + .iter() + .chain(args.iter()) + .map(|x| OscType::Int(*x)) + .collect(), + })) + .unwrap(); + + if show_terminal { + println!("{}: {:?}", addr, args) + } + + socket.send_to(&msg, to_addr).unwrap(); + } +} diff --git a/toio_rust/src/toio.rs b/toio_rust/src/toio.rs new file mode 100644 index 00000000..4765bf1a --- /dev/null +++ b/toio_rust/src/toio.rs @@ -0,0 +1,1125 @@ +use std::error::Error; +use std::sync::Arc; +use std::time::SystemTime; +use std::vec; + +use futures::future::Either; +use tokio::sync::mpsc; +use tokio::sync::mpsc::{Receiver, Sender}; +use tokio::sync::RwLock; +use tokio::task::JoinHandle; +use tokio::time::timeout; + +use futures::stream::StreamExt; + +use uuid::Uuid; + +use btleplug::{ + api::{ + Central, CentralEvent, CharPropFlags, Characteristic, Manager as _, Peripheral, ScanFilter, + ValueNotification, WriteType, + }, + platform, + platform::{Adapter, Manager}, +}; + +pub const SERVICE: Uuid = Uuid::from_u128(0x10B20100_5B3B_4571_9508_CF3EFCD7BBAE); +pub const POSITION: Uuid = Uuid::from_u128(0x10B20101_5B3B_4571_9508_CF3EFCD7BBAE); +pub const MOTOR: Uuid = Uuid::from_u128(0x10B20102_5B3B_4571_9508_CF3EFCD7BBAE); +pub const LIGHT: Uuid = Uuid::from_u128(0x10B20103_5B3B_4571_9508_CF3EFCD7BBAE); +pub const SOUND: Uuid = Uuid::from_u128(0x10B20104_5B3B_4571_9508_CF3EFCD7BBAE); +pub const MOTION: Uuid = Uuid::from_u128(0x10B20106_5B3B_4571_9508_CF3EFCD7BBAE); +pub const BUTTON: Uuid = Uuid::from_u128(0x10B20107_5B3B_4571_9508_CF3EFCD7BBAE); +pub const BATTERY: Uuid = Uuid::from_u128(0x10B20108_5B3B_4571_9508_CF3EFCD7BBAE); +pub const CONFIG: Uuid = Uuid::from_u128(0x10B201FF_5B3B_4571_9508_CF3EFCD7BBAE); + +const IDARR : [&str; 193] = [ + "Individual ID", //TOIO Num + "0", // #1 + "j1c", // #2 + "r81", // #3 + "26E", // #4 + "76t", // #5 + "broken", // #6 + "k5k", // #7 + "h41", // #8 + "0", // #9 + "0", // #10 + "0", // #11 + "Q3A", // #12 + "03a", // #13 + "0", // #14 + "K0m", // #15 + "0", // #16 + "0", // #17 + "p8B", // #18 + "91B", // #19 + "p75", // #20 + "G1E", // #21 + "k2L", // #22 + "b5p", // #23 + "J6C", // #24 + "a1K", // #25 + "b8T", // #26 + "b6A", // #27 + "01c", // #28 + "0", // #29 + "0", // #30 + "E2N", // #31 + "G7t", // #32 + "L6T", // #33 + "C0E", // #34 + "t79", // #35 + "J6k", // #36 + "d6f", // #37 + "0", // #38 + "M75", // #39 + "310", // #40 + "M5p", // #41 + "A4a", // #42 + "M9J", // #43 + "i01", // #44 + "T5m", // #45 + "j1G", // #46 + "40G", // #47 + "L6n", // #48 + "a3F", // #49 + "J8d", // #50 + "227", // #51 + "k4i", // #52 + "J68", // #53 + "90J", // #54 + "k96", // #55 + "0", // #56 + "0", // #57 + "0", // #58 + "0", // #59 + "0", // #60 + "0", // #61 + "0", // #62 + "0", // #63 + "0", // #64 + "0", // #65 + "0", // #66 + "0", // #67 + "0", // #68 + "0", // #69 + "0", // #70 + "0", // #71 + "0", // #72 + "0", // #73 + "0", // #74 + "0", // #75 + "0", // #76 + "0", // #77 + "0", // #78 + "E7c", // #79 + "P1B", // #80 + "F2B", // #81 + "L1H", // #82 + "D5i", // #83 + "m4Q", // #84 + "m1k", // #85 + "r52", // #86 + "k89", // #87 + "D2K", // #88 + "65r", // #89 + "f3K", // #90 + "13c", // #91 + "e1a", // #92 + "0", // #93 + "e6e", // #94 + "07F", // #95 + "m8k", // #96 + "79H", // #97 + "0", // #98 + "i1M", // #99 + "R3C", // #100 + "D98", // #101 + "m86", // #102 + "a66", // #103 + "0", // #104 + "E8T", // #105 + "J8n", // #106 + "N0b", // #107 + "586", // #108 + "p50", // #109 + "c9k", // #110 + "N0N", // #111 + "0", // #112 + "B1m", // #113 + "h7E", // #114 + "c05", // #115 + "K20", // #116 + "32D", // #117 + "F19", // #118 + "r4d", // #119 + "D2F", // #120 + "D0m", // #121 + "m6B", // #122 + "M0j", // #123 + "Q8G", // #124 + "A1t", // #125 + "p7J", // #126 + "t0H", // #127 + "M5i", // #128 + "j1L", // #129 + "e7i", // #130 + "T1E", // #131 + "85i", // #132 + "71H", // #133 + "20H", // #134 + "T9n", // #135 + "58B", // #136 + "J4R", // #137 + "93N", // #138 + "t0F", // #139 + "M7G", // #140 + "r4P", // #141 + "i1d", // #142 + "a22", // #143 + "M39", // #144 + "C23", // #145 + "816", // #146 + "E0M", // #147 + "T4b", // #148 + "L1L", // #149 + "i5m", // #150 + "P2R", // #151 + "t77", // #152 + "A5E", // #153 + "88e", // #154 + "k1b", // #155 + "m04", // #156 + "41b", // #157 + "B4k", // #158 + "J1M", // #159 + "H4M", // #160 + "C1D", // #161 + "12K", // #162 + "822", // #163 + "E1T", // #164 + "Q4H", // #165 + "k4d", // #166 + "k4J", // #167 + "L70", // #168 + "31f", // #169 + "G1P", // #170 + "34e", // #171 + "939", // #172 + "24F", // #173 + "43r", // #174 + "M81", // #175 + "01E", // #176 + "A0N", // #177 + "65f", // #178 + "Q6p", // #179 + "93R", // #180 + "r0i", // #181 + "A35", // #182 + "P40", // #183 + "G9R", // #184 + "c7C", // #185 + "P17", // #186 + "76f", // #187 + "99p", // #188 + "96E", // #189 + "p3E", // #190 + "h6t", // #191 + "n2L", // #192 +]; + +/// Format for a target to plug into the MotorTarget varient +/// of the Command enum. By putting multiple of these into a vector, +/// you can send a a series of targets for a toio to travel to in +/// a sequence. +#[derive(Clone, Debug)] +pub struct TargetCommand { + pub x_target: u16, + pub y_target: u16, + pub theta_target: u16, +} + +/// Format for a RGB color to plug into the MultiLed varient +/// of the Command enum. By putting multiple of these into a vector, +/// you can send a a series of colors for a toio to flash on its led +/// in a sequence. +#[derive(Clone, Debug)] +pub struct LedCommand { + pub duration: u8, + pub red: u8, + pub blue: u8, + pub green: u8, +} + +/// Format for a MIDI note to plug into the Midi varient +/// of the Command enum. By putting multiple of these into a vector, +/// you can send a a series of notes for a toio to play in +/// a sequence. +#[derive(Clone, Debug)] +pub struct MidiCommand { + pub duration: u8, + pub note: u8, + pub volume: u8, +} + +/// An enum to list out all possible commands to send to a toio +#[allow(dead_code)] +#[derive(Clone, Debug)] +pub enum Command { + //Request Commands + MotionRequest, + MagneticRequest, + PostureRequest { + format: u8, + }, + + //Motor Commands + MotorControl { + left_direction: u8, + left_speed: u8, + right_direction: u8, + right_speed: u8, + }, + MotorDuration { + left_direction: u8, + left_speed: u8, + right_direction: u8, + right_speed: u8, + duration: u8, + }, + MotorTarget { + control: u8, + timeout: u8, + move_type: u8, + max_speed: u8, + speed_change: u8, + x_target: u16, + y_target: u16, + theta_target: u16, + }, + MultiTarget { + control: u8, + timeout: u8, + move_type: u8, + max_speed: u8, + speed_change: u8, + op_add: u8, + targets: Vec, + }, + MotorAcceleration { + velocity: u8, + acceleration: u8, + rotational_velocity: u16, + rotational_direction: u8, + direction: u8, + priority: u8, + duration: u8, + }, + + //Light Commands + LedOff, + Led { + duration: u8, + red: u8, + green: u8, + blue: u8, + }, + MultiLed { + repetitions: u8, + lights: Vec, + }, + + //Sound Commands + SoundOff, + Sound { + sound_effect: u8, + volume: u8, + }, + Midi { + repetitions: u8, + notes: Vec, + }, +} + +/// An enum to list out all possible updates to recieve from a toio +#[allow(dead_code)] +#[derive(Debug)] +pub enum Update { + Position { + x_center: u16, + y_center: u16, + theta: u16, + x_sensor: u16, + y_sensor: u16, + }, + Standard { + standard: u32, + theta: u16, + }, + PositionMissed, + StandardMissed, + MotorTargetResponse { + control: u8, + response: u8, + }, + MultiTargetResponse { + control: u8, + response: u8, + }, + MotorSpeed { + left_speed: u8, + right_speed: u8, + }, + Motion { + horizontal: u8, + collision: u8, + double_tap: u8, + posture: u8, + shake: u8, + }, + PostureEuler { + roll: u16, + pitch: u16, + yaw: u16, + }, + PostureQuaternion { + w: f32, + x: f32, + y: f32, + z: f32, + }, + PostureHighPrecisionEuler { + roll: f32, + pitch: f32, + yaw: f32, + }, + Magnetic { + state: u8, + strength: u8, + forcex: i8, + forcey: i8, + forcez: i8, + }, + Button { + pressed: bool, + }, + Battery { + level: u8, + }, +} + +pub struct Updates { + receiver: Receiver, +} + +pub struct ToioScanner { + central: Adapter, + ordered: bool, + filter: Option>, +} + +pub struct ToioReceiver { + receiver: Receiver>, +} + +pub struct ToioPeripheral { + pub name: String, + peripheral: platform::Peripheral, + pub peripheral_id: platform::PeripheralId, +} + +pub struct Toio { + pub toio: ToioPeripheral, + pub name: String, + pub id: String, + pub connected: bool, + pub channel: Option>, + pub battery: Arc>>, + pub last_update: Arc>>, + pub last_command: Arc>>, +} + +impl Updates { + fn new(receiver: Receiver) -> Updates { + return Updates { receiver }; + } + + pub async fn next(&mut self) -> Option { + return self.receiver.recv().await; + } +} + +impl ToioScanner { + pub async fn new() -> Result> { + let manager = Manager::new().await?; + + // get the first bluetooth adapter + // connect to the adapter + let central = manager + .adapters() + .await + .unwrap() + .into_iter() + .nth(0) + .unwrap(); + + Ok(ToioScanner { + central, + filter: None, + ordered: false, + }) + } + + pub async fn new_with_filter( + ordered: bool, + filter: Vec, + ) -> Result> { + let manager = Manager::new().await?; + + // get the first bluetooth adapter + // connect to the adapter + let central = manager + .adapters() + .await + .unwrap() + .into_iter() + .nth(0) + .unwrap(); + + let toio_filter = filter.iter().map(|x| IDARR[*x].to_string()).collect(); + + Ok(ToioScanner { + central, + filter: Some(toio_filter), + ordered, + }) + } + + pub async fn search(&self) -> Result> { + let central = self.central.clone(); + let mut events = central.events().await?; + + // start scanning for devices + central + .start_scan(ScanFilter { + services: vec![SERVICE, CONFIG], + }) + .await?; + + let (tx, rx) = mpsc::channel(32); + + //Discovery Async Task + let mut toio_filter = self.filter.clone(); + let ordered = self.ordered; + tokio::spawn(async move { + while let Some(event) = events.next().await { + match event { + CentralEvent::DeviceDiscovered(id) => { + let peripheral = central.peripheral(&id).await.unwrap(); + + if ordered { + // if succesful connection and the filter is ordered, update filter + if let Some(ref mut new_filter) = toio_filter.clone() { + if !new_filter.is_empty() { + let curr_toio = new_filter.remove(0); + + let connect_success = Self::try_connect( + peripheral, + &tx, + Some(vec![curr_toio.clone()]), + ) + .await; + + if connect_success { + toio_filter = Some(new_filter.to_vec()); + } + } + } + } else { + Self::try_connect(peripheral, &tx, toio_filter.clone()).await; + } + } + CentralEvent::ServicesAdvertisement { id, services: _ } => { + let peripheral = central.peripheral(&id).await.unwrap(); + + if ordered { + // if succesful connection and the filter is ordered, update filter + if let Some(ref mut new_filter) = toio_filter.clone() { + if !new_filter.is_empty() { + let curr_toio = new_filter.remove(0); + + let connect_success = Self::try_connect( + peripheral, + &tx, + Some(vec![curr_toio.clone()]), + ) + .await; + + if connect_success { + toio_filter = Some(new_filter.to_vec()); + } + } + } + } else { + Self::try_connect(peripheral, &tx, toio_filter.clone()).await; + } + } + CentralEvent::DeviceDisconnected(id) => { + Self::set_disconnected(id, &tx).await; + } + _ => {} + } + } + }); + + return Ok(ToioReceiver::new(rx)); + } + + async fn try_connect( + peripheral: platform::Peripheral, + tx: &Sender>, + filter: Option>, + ) -> bool { + if let Some(properties) = peripheral.properties().await.unwrap() { + let fullname = properties.local_name.unwrap_or("".to_string()); + if peripheral.is_connected().await.unwrap() || !fullname.contains(&"toio") { + return false; + } + + let name: Vec<&str> = fullname.split('-').collect(); + let toio_name = name.last().unwrap_or(&&" ").to_string(); + if let Some(filter_list) = filter { + if !filter_list.contains(&toio_name) { + return false; + } + } + + let toio_peripheral = ToioPeripheral::new(toio_name, peripheral); + if !toio_peripheral.connect().await { + return false; + } + + tx.send(Either::Left(toio_peripheral)).await.unwrap(); + + return true; + } + + return false; + } + + async fn set_disconnected( + peripheral_id: platform::PeripheralId, + tx: &Sender>, + ) { + tx.send(Either::Right(peripheral_id)).await.unwrap(); + } +} + +impl ToioReceiver { + fn new(receiver: Receiver>) -> ToioReceiver { + return ToioReceiver { receiver }; + } + + pub async fn next(&mut self) -> Option> { + return self.receiver.recv().await; + } +} + +impl ToioPeripheral { + pub fn new(name: String, peripheral: platform::Peripheral) -> ToioPeripheral { + ToioPeripheral { + name, + peripheral_id: peripheral.id(), + peripheral, + } + } + + pub async fn connect(&self) -> bool { + if let Err(_) = self.peripheral.connect().await { + return false; + } else if let Err(_) = self.peripheral.discover_services().await { + return false; + } + + for characteristic in self.peripheral.characteristics().into_iter() { + if !characteristic.properties.contains(CharPropFlags::NOTIFY) { + continue; + } + + if let Err(err) = self.peripheral.subscribe(&characteristic).await { + eprintln!("Error connecting to characteristic, skipping: {}", err); + continue; + } + } + + return true; + } + + pub async fn updates(&self) -> Result> { + let (tx, rx) = mpsc::channel(32); + + let mut notification_stream = self.peripheral.notifications().await?; + tokio::spawn(async move { + // let notification_steam = notification_stream; + + while let Ok(possible_event) = timeout( + std::time::Duration::from_secs(5), + notification_stream.next(), + ) + .await + { + if let Some(event) = possible_event { + if let Some(update) = ToioPeripheral::get_update(event) { + tx.send(update).await.unwrap(); + } + } + } + }); + + return Ok(Updates::new(rx)); + } + + fn get_update(notification: ValueNotification) -> Option { + let vals = notification.value; + match notification.uuid { + POSITION => match vals[0] { + 0x01 => Some(Update::Position { + x_center: vals[1] as u16 | (vals[2] as u16) << 8, + y_center: vals[3] as u16 | (vals[4] as u16) << 8, + theta: vals[5] as u16 | (vals[6] as u16) << 8, + x_sensor: vals[7] as u16 | (vals[8] as u16) << 8, + y_sensor: vals[9] as u16 | (vals[10] as u16) << 8, + }), + 0x02 => Some(Update::Standard { + standard: vals[1] as u32 + | (vals[2] as u32) << 8 + | (vals[3] as u32) << 16 + | (vals[4] as u32) << 24, + theta: vals[5] as u16 | (vals[6] as u16) << 8, + }), + 0x03 => Some(Update::PositionMissed), + 0x04 => Some(Update::StandardMissed), + _ => { + println!( + "Unkown {} Update: {:?}", + uuid_to_string(notification.uuid), + vals + ); + None + } + }, + MOTOR => match vals[0] { + 0x83 => Some(Update::MotorTargetResponse { + control: vals[1], + response: vals[2], + }), + 0x84 => Some(Update::MultiTargetResponse { + control: vals[1], + response: vals[2], + }), + 0xe0 => Some(Update::MotorSpeed { + left_speed: vals[1], + right_speed: vals[2], + }), + _ => { + println!( + "Unkown {} Update: {:?}", + uuid_to_string(notification.uuid), + vals + ); + None + } + }, + MOTION => match vals[0] { + 0x01 => Some(Update::Motion { + horizontal: vals[1], + collision: vals[2], + double_tap: vals[3], + posture: vals[4], + shake: vals[5], + }), + // 0x02 => Some(Update::Magnetic { + // state: vals[1], + // strength: vals[2], + // forcex: vals[3] as i8, + // forcey: vals[4] as i8, + // forcez: vals[5] as i8, + // }), + // 0x03 => match vals[1] { + // 0x01 => Some(Update::PostureEuler { + // roll: vals[2] as u16 | (vals[3] as u16) << 8, + // pitch: vals[4] as u16 | (vals[5] as u16) << 8, + // yaw: vals[5] as u16 | (vals[6] as u16) << 8, + // }), + // 0x02 => Some(Update::PostureQuaternion { + // w: 0.0, + // // vals[2] as f32 + // // | (vals[3] as f32) << 8 + // // | (vals[4] as f32) << 16 + // // | (vals[5] as f32) << 24, + // x: 0.0, + // // vals[6] as f32 + // // | (vals[7] as f32) << 8 + // // | (vals[8] as f32) << 16 + // // | (vals[9] as f32) << 24, + // y: 0.0, + // // vals[10] as f32 + // // | (vals[11] as f32) << 8 + // // | (vals[12] as f32) << 16 + // // | (vals[13] as f32) << 24, + // z: 0.0, + // // vals[14] as f32 + // // | (vals[15] as f32) << 8 + // // | (vals[16] as f32) << 16 + // // | (vals[17] as f32) << 24, + // }), + // 0x03 => Some(Update::PostureHighPrecisionEuler { + // roll: 0.0, + // // vals[2] as f32 + // // | (vals[3] as f32) << 8 + // // | (vals[4] as f32) << 16 + // // | (vals[5] as f32) << 24, + // pitch: 0.0, + // // vals[6] as f32 + // // | (vals[7] as f32) << 8 + // // | (vals[8] as f32) << 16 + // // | (vals[9] as f32) << 24, + // yaw: 0.0, + // // vals[10] as f32 + // // | (vals[11] as f32) << 8 + // // | (vals[12] as f32) << 16 + // // | (vals[13] as f32) << 24, + // }), + // _ => { + // println!( + // "Unkown {} Update: {:?}", + // uuid_to_string(notification.uuid), + // vals + // ); + // None + // } + // }, + _ => { + println!( + "Unkown {} Update: {:?}", + uuid_to_string(notification.uuid), + vals + ); + None + } + }, + BATTERY => Some(Update::Battery { level: vals[0] }), + BUTTON => Some(Update::Button { + pressed: vals[1] == 0x80, + }), + _ => { + println!( + "Unkown {} Update: {:?}", + uuid_to_string(notification.uuid), + vals + ); + None + } + } + } + + pub async fn send_command(&self, command: Command) { + let uuid = match command { + Command::MotionRequest | Command::MagneticRequest | Command::PostureRequest { .. } => { + MOTION + } + Command::MotorControl { .. } + | Command::MotorDuration { .. } + | Command::MotorTarget { .. } + | Command::MultiTarget { .. } + | Command::MotorAcceleration { .. } => MOTOR, + Command::LedOff | Command::Led { .. } | Command::MultiLed { .. } => LIGHT, + Command::SoundOff | Command::Sound { .. } | Command::Midi { .. } => SOUND, + }; + + let (response_flag, response_type) = match uuid { + LIGHT | SOUND => (CharPropFlags::WRITE, WriteType::WithResponse), + _ => ( + CharPropFlags::WRITE_WITHOUT_RESPONSE, + WriteType::WithoutResponse, + ), + }; + + let cmd: Vec = match command { + Command::MotionRequest => { + vec![0x81] + } + Command::MagneticRequest => { + vec![0x82] + } + Command::PostureRequest { format } => { + vec![0x83, format] + } + Command::MotorControl { + left_direction, + left_speed, + right_direction, + right_speed, + } => { + vec![ + 0x01, + 0x01, + left_direction, + left_speed, + 0x02, + right_direction, + right_speed, + ] + } + Command::MotorDuration { + left_direction, + left_speed, + right_direction, + right_speed, + duration, + } => { + vec![ + 0x02, + 0x01, + left_direction, + left_speed, + 0x02, + right_direction, + right_speed, + duration, + ] + } + Command::MotorTarget { + control, + timeout, + move_type, + max_speed, + speed_change, + x_target, + y_target, + theta_target, + } => { + vec![ + 0x03, + control, + timeout, + move_type, + max_speed, + speed_change, + 0x00, + (x_target & 0x00FF) as u8, + ((x_target & 0xFF00) >> 8) as u8, + (y_target & 0x00FF) as u8, + ((y_target & 0xFF00) >> 8) as u8, + (theta_target & 0x00FF) as u8, + ((theta_target & 0xFF00) >> 8) as u8, + ] + } + Command::MultiTarget { + control, + timeout, + move_type, + max_speed, + speed_change, + op_add, + targets, + } => { + let mut cmd = vec![ + 0x04, + control, + timeout, + move_type, + max_speed, + speed_change, + 0x00, + op_add, + ]; + cmd.append(&mut parse_target_command(targets)); + cmd + } + + Command::MotorAcceleration { + velocity, + acceleration, + rotational_velocity, + rotational_direction, + direction, + priority, + duration, + } => { + vec![ + 0x05, + velocity, + acceleration, + (rotational_velocity & 0x00FF) as u8, + ((rotational_velocity & 0xFF00) >> 8) as u8, + rotational_direction, + direction, + priority, + duration, + ] + } + Command::LedOff => { + vec![0x01] + } + Command::Led { + duration, + red, + green, + blue, + } => { + vec![0x03, duration, 0x01, 0x01, red, green, blue] + } + Command::MultiLed { + repetitions, + lights, + } => parse_led_command(repetitions, lights), + Command::SoundOff => { + vec![0x01] + } + Command::Sound { + sound_effect, + volume, + } => { + vec![ + 0x02, //sound + sound_effect, //sound effect ID + volume, //volume + ] + } + Command::Midi { repetitions, notes } => parse_midi_command(repetitions, notes), + }; + + self.write(uuid, cmd, response_flag, response_type).await; + } + + pub async fn write( + &self, + uuid: Uuid, + cmd: Vec, + response_flag: CharPropFlags, + response_type: WriteType, + ) { + let characteristic = Characteristic { + uuid: uuid, + service_uuid: SERVICE, + properties: response_flag, + }; + + // println!("{} : {:?}", uuid_to_string(uuid), cmd); + self.peripheral + .write(&characteristic, &cmd, response_type) + .await + .unwrap(); + } +} + +impl Toio { + pub fn new(toio: ToioPeripheral) -> Toio { + return Toio { + name: toio.name.clone(), + id: if let Some(id) = return_toio_id(&toio.name) { + format!("{}", id) + } else { + "N/A".to_owned() + }, + connected: true, + channel: None, + battery: Arc::new(RwLock::new(None)), + toio, + last_update: Arc::new(RwLock::new(None)), + last_command: Arc::new(RwLock::new(None)), + }; + } + + pub fn add_channel(&mut self, channel: JoinHandle<()>) { + self.channel = Some(channel); + } + + pub fn disconnect(&mut self) { + if let Some(channel) = &self.channel { + channel.abort(); + } + self.connected = false; + } + + pub fn get_battery(&self) -> Arc>> { + return self.battery.clone(); + } + + pub fn get_last_update(&self) -> Arc>> { + return self.last_update.clone(); + } + + pub fn get_last_command(&self) -> Arc>> { + return self.last_command.clone(); + } + + pub async fn is_connected(&self) -> bool { + return self.connected; + } +} + +/// matches UUIDs of toios a string of their coresponding service +pub fn uuid_to_string(uuid: Uuid) -> String { + return match uuid { + SERVICE => "Service", + POSITION => "Position", + MOTOR => "Motor", + LIGHT => "Light", + SOUND => "Sound", + MOTION => "Motion", + BUTTON => "Button", + BATTERY => "Battery", + CONFIG => "Config", + _ => "", + } + .to_owned(); +} + +fn parse_target_command(vals: Vec) -> Vec { + let mut cmd = vec![]; + + for target in vals.iter() { + cmd.push((target.x_target & 0x00FF) as u8); + cmd.push(((target.x_target & 0xFF00) >> 8) as u8); + cmd.push((target.y_target & 0x00FF) as u8); + cmd.push(((target.y_target & 0xFF00) >> 8) as u8); + cmd.push((target.theta_target & 0x00FF) as u8); + cmd.push(((target.theta_target & 0xFF00) >> 8) as u8); + } + + return cmd; +} + +fn parse_led_command(repetitions: u8, vals: Vec) -> Vec { + let mut cmd = vec![0x04, repetitions, vals.len() as u8]; + + for led in vals.iter() { + cmd.push(led.duration); + cmd.push(0x01); + cmd.push(0x01); + cmd.push(led.red); + cmd.push(led.green); + cmd.push(led.blue); + } + + return cmd; +} + +fn parse_midi_command(repetitions: u8, vals: Vec) -> Vec { + let mut cmd = vec![0x03, repetitions, vals.len() as u8]; + + for note in vals.iter() { + cmd.push(note.duration); + cmd.push(note.note); + cmd.push(note.volume); + } + + return cmd; +} + +fn return_toio_id(name: &str) -> Option { + return IDARR.iter().position(|&r| r == name); +} diff --git a/toio_rust/src/ui.rs b/toio_rust/src/ui.rs new file mode 100644 index 00000000..affc21a8 --- /dev/null +++ b/toio_rust/src/ui.rs @@ -0,0 +1,140 @@ +use std::error::Error; +use std::io::stdout; +use std::vec; + +use crossterm::{ + cursor::Show, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, + ExecutableCommand, +}; + +use ratatui::{ + prelude::*, + widgets::{block::*, *}, +}; + +pub type ToioUI = Option>>; + +pub fn ui( + toio_info: Vec<(String, String, String, String, String, bool)>, + filter: Option>, +) -> impl Fn(&mut Frame) { + return move |frame| { + let area = frame.size(); + + let rows: Vec = toio_info + .iter() + .enumerate() + .map(|(i, val)| { + let connected_color = match val.5 { + true => Style::new().white(), + false => Style::new().red(), + }; + + let battery = val.2.clone(); + let battery_color = if let Ok(level) = battery.parse::() { + if level == 10 || connected_color == Style::new().red() { + Style::new().red() + } else if level < 50 { + Style::new().yellow() + } else { + Style::new().green() + } + } else if connected_color == Style::new().red() { + Style::new().red() + } else { + Style::new().white() + }; + + Row::new(vec![ + Span::raw(format!("{}", i)).style(connected_color), + Span::raw(val.0.clone()).style(connected_color), + Span::raw(val.1.clone()).style(connected_color), + Span::raw(battery).style(battery_color), + Span::raw(val.3.clone()).style(connected_color), + Span::raw(val.4.clone()).style(connected_color), + ]) + }) + .collect(); + + let instructions = Title::from(Line::from(vec![" Quit ".into(), " ".blue().bold()])); + + let widths = [ + Constraint::Length(2), + Constraint::Length(4), + Constraint::Length(3), + Constraint::Length(7), + Constraint::Length(12), + Constraint::Length(12), + ]; + + let table = Table::new(rows, widths) + .column_spacing(7) + .header( + Row::new(vec![ + "", + "Name", + "ID", + "Battery", + "Last Update", + "Last Command", + ]) + .style(Style::new().bold()), + ) + .highlight_style(Style::new().reversed()) + .highlight_symbol(">>"); + + let connected_ids: Vec = toio_info.iter().map(|val| val.1.clone()).collect(); + let filter_list = match &filter { + Some(toio_filter) => { + let spans: Vec = toio_filter + .iter() + .enumerate() + .map(|(i, x)| { + let id_string = x.to_string(); + let formatted_id = match i == 0 { + true => format!(" {} ", id_string), + false => format!("{} ", id_string), + }; + + match connected_ids.contains(&id_string) { + true => Span::from(formatted_id).style(Style::new().white()), + false => Span::from(formatted_id).style(Style::new().green()), + } + }) + .collect(); + Title::from(spans) + } + None => Title::from(Line::from(" Search Mode ").style(Style::new().green())), + }; + + frame.render_widget( + table.block( + Block::default() + .title(" Laptop Toio ") + .title(filter_list) + .title( + instructions + .alignment(Alignment::Center) + .position(Position::Bottom), + ) + .borders(Borders::ALL), + ), + area, + ); + }; +} + +pub fn setup_terminal() -> Result> { + enable_raw_mode()?; + stdout().execute(EnterAlternateScreen)?; + let terminal = Terminal::new(CrosstermBackend::new(stdout()))?; + Ok(Some(terminal)) +} + +pub fn exit_terminal() -> Result<(), Box> { + disable_raw_mode()?; + stdout().execute(Show)?; + stdout().execute(LeaveAlternateScreen)?; + Ok(()) +} From 5cd73da5fd34fbc0e8b6bd0e194299454183087a Mon Sep 17 00:00:00 2001 From: Ramarko Bhattacharya Date: Sun, 6 Jul 2025 20:58:47 -0500 Subject: [PATCH 2/4] Fixed Support for LED and MIDI sequences --- toio_processing/osc.pde | 14 +- toio_rust/src/main.rs | 2 +- toio_rust/src/osc.rs | 18 +- toio_rust/src/toio.rs | 392 ++++++++++++++++++++-------------------- 4 files changed, 211 insertions(+), 215 deletions(-) diff --git a/toio_processing/osc.pde b/toio_processing/osc.pde index 9dfb0462..9713d1e9 100644 --- a/toio_processing/osc.pde +++ b/toio_processing/osc.pde @@ -81,10 +81,6 @@ void motorMultiTarget(int cubeId, int control, int timeout, int mode, int maxspe for (int j = 0; j < targets[i].length; j++) { msg.add(targets[i][j]); } - - if (targets[i].length == 2) { - msg.add(0); - } } oscP5.send(msg, server[hostId]); } @@ -114,6 +110,7 @@ void lightLed(int cubeId, int duration, int red, int green, int blue) { int actualcubeid = cubeId % cubesPerHost; OscMessage msg = new OscMessage("/led"); msg.add(actualcubeid); + msg.add(1); msg.add(duration); msg.add(red); msg.add(green); @@ -130,13 +127,9 @@ void lightLed(int cubeId, int repetitions, int[][] lights) { OscMessage msg = new OscMessage("/led"); msg.add(actualcubeid); msg.add(repetitions); - msg.add(lights.length); for (int i = 0; i < lights.length; i++) { - msg.add(lights[i][0]); - msg.add(1); - msg.add(1); - + msg.add(lights[i][0]); for (int j = 1; j < lights[i].length; j++) { msg.add(lights[i][j]); } @@ -164,6 +157,8 @@ void soundMidi(int cubeId, int duration, int noteID, int volume) { int actualcubeid = cubeId % cubesPerHost; OscMessage msg = new OscMessage("/midi"); msg.add(actualcubeid); + msg.add(1); + msg.add(duration); msg.add(noteID); msg.add(volume); @@ -179,7 +174,6 @@ void soundMidi(int cubeId, int repetitions, int[][] notes) { OscMessage msg = new OscMessage("/midi"); msg.add(actualcubeid); msg.add(repetitions); - msg.add(notes.length); for (int i = 0; i < notes.length; i++) { for (int j = 0; j < notes[i].length; j++) { diff --git a/toio_rust/src/main.rs b/toio_rust/src/main.rs index 317fdb88..d68909ee 100644 --- a/toio_rust/src/main.rs +++ b/toio_rust/src/main.rs @@ -99,7 +99,7 @@ async fn main() -> Result<(), Box> { // let mut now = SystemTime::now(); while let Ok(size) = sock.recv(&mut buf) { if let Ok((_, packet)) = rosc::decoder::decode_udp(&buf[..size]) { - if let Some((toionum, cmd)) = handle_packet(packet) { + if let Some((toionum, cmd)) = handle_packet(packet, args.terminal) { let connected_read = connected_clone.read().await; if toionum < connected_read.len() { let toio = connected_read[toionum].read().await; diff --git a/toio_rust/src/osc.rs b/toio_rust/src/osc.rs index 0b90f0b5..b9fdf185 100644 --- a/toio_rust/src/osc.rs +++ b/toio_rust/src/osc.rs @@ -21,7 +21,7 @@ pub fn handle_events() -> io::Result { Ok(false) } -pub fn handle_packet(packet: OscPacket) -> Option<(usize, Command)> { +pub fn handle_packet(packet: OscPacket, show_terminal: bool) -> Option<(usize, Command)> { match packet { OscPacket::Message(msg) => { let mut vals: Vec = msg @@ -84,13 +84,7 @@ pub fn handle_packet(packet: OscPacket) -> Option<(usize, Command)> { }) .collect(), }), - "/led" => Some(Command::Led { - duration: vals[1] as u8, - red: vals[2] as u8, - green: vals[3] as u8, - blue: vals[4] as u8, - }), - "/multiLed" => Some(Command::MultiLed { + "/led" => Some(Command::MultiLed { repetitions: vals[1] as u8, lights: vals .split_off(2) @@ -123,8 +117,12 @@ pub fn handle_packet(packet: OscPacket) -> Option<(usize, Command)> { _ => None, }; + if show_terminal { + println!("Update Received [{}]: {:?}", msg.addr.as_str(), vals,) + } + // Return pair of (toioID, pair) - return cmd.map(|cmd| (vals[0] as usize, cmd)); + cmd.map(|cmd| (vals[0] as usize, cmd)) } _ => None, } @@ -219,7 +217,7 @@ pub fn send_packet( .unwrap(); if show_terminal { - println!("{}: {:?}", addr, args) + println!("Update Received [{}]: {:?}", addr, args) } socket.send_to(&msg, to_addr).unwrap(); diff --git a/toio_rust/src/toio.rs b/toio_rust/src/toio.rs index 4765bf1a..784f3962 100644 --- a/toio_rust/src/toio.rs +++ b/toio_rust/src/toio.rs @@ -33,200 +33,200 @@ pub const BUTTON: Uuid = Uuid::from_u128(0x10B20107_5B3B_4571_9508_CF3EFCD7BBAE) pub const BATTERY: Uuid = Uuid::from_u128(0x10B20108_5B3B_4571_9508_CF3EFCD7BBAE); pub const CONFIG: Uuid = Uuid::from_u128(0x10B201FF_5B3B_4571_9508_CF3EFCD7BBAE); -const IDARR : [&str; 193] = [ - "Individual ID", //TOIO Num - "0", // #1 - "j1c", // #2 - "r81", // #3 - "26E", // #4 - "76t", // #5 - "broken", // #6 - "k5k", // #7 - "h41", // #8 - "0", // #9 - "0", // #10 - "0", // #11 - "Q3A", // #12 - "03a", // #13 - "0", // #14 - "K0m", // #15 - "0", // #16 - "0", // #17 - "p8B", // #18 - "91B", // #19 - "p75", // #20 - "G1E", // #21 - "k2L", // #22 - "b5p", // #23 - "J6C", // #24 - "a1K", // #25 - "b8T", // #26 - "b6A", // #27 - "01c", // #28 - "0", // #29 - "0", // #30 - "E2N", // #31 - "G7t", // #32 - "L6T", // #33 - "C0E", // #34 - "t79", // #35 - "J6k", // #36 - "d6f", // #37 - "0", // #38 - "M75", // #39 - "310", // #40 - "M5p", // #41 - "A4a", // #42 - "M9J", // #43 - "i01", // #44 - "T5m", // #45 - "j1G", // #46 - "40G", // #47 - "L6n", // #48 - "a3F", // #49 - "J8d", // #50 - "227", // #51 - "k4i", // #52 - "J68", // #53 - "90J", // #54 - "k96", // #55 - "0", // #56 - "0", // #57 - "0", // #58 - "0", // #59 - "0", // #60 - "0", // #61 - "0", // #62 - "0", // #63 - "0", // #64 - "0", // #65 - "0", // #66 - "0", // #67 - "0", // #68 - "0", // #69 - "0", // #70 - "0", // #71 - "0", // #72 - "0", // #73 - "0", // #74 - "0", // #75 - "0", // #76 - "0", // #77 - "0", // #78 - "E7c", // #79 - "P1B", // #80 - "F2B", // #81 - "L1H", // #82 - "D5i", // #83 - "m4Q", // #84 - "m1k", // #85 - "r52", // #86 - "k89", // #87 - "D2K", // #88 - "65r", // #89 - "f3K", // #90 - "13c", // #91 - "e1a", // #92 - "0", // #93 - "e6e", // #94 - "07F", // #95 - "m8k", // #96 - "79H", // #97 - "0", // #98 - "i1M", // #99 - "R3C", // #100 - "D98", // #101 - "m86", // #102 - "a66", // #103 - "0", // #104 - "E8T", // #105 - "J8n", // #106 - "N0b", // #107 - "586", // #108 - "p50", // #109 - "c9k", // #110 - "N0N", // #111 - "0", // #112 - "B1m", // #113 - "h7E", // #114 - "c05", // #115 - "K20", // #116 - "32D", // #117 - "F19", // #118 - "r4d", // #119 - "D2F", // #120 - "D0m", // #121 - "m6B", // #122 - "M0j", // #123 - "Q8G", // #124 - "A1t", // #125 - "p7J", // #126 - "t0H", // #127 - "M5i", // #128 - "j1L", // #129 - "e7i", // #130 - "T1E", // #131 - "85i", // #132 - "71H", // #133 - "20H", // #134 - "T9n", // #135 - "58B", // #136 - "J4R", // #137 - "93N", // #138 - "t0F", // #139 - "M7G", // #140 - "r4P", // #141 - "i1d", // #142 - "a22", // #143 - "M39", // #144 - "C23", // #145 - "816", // #146 - "E0M", // #147 - "T4b", // #148 - "L1L", // #149 - "i5m", // #150 - "P2R", // #151 - "t77", // #152 - "A5E", // #153 - "88e", // #154 - "k1b", // #155 - "m04", // #156 - "41b", // #157 - "B4k", // #158 - "J1M", // #159 - "H4M", // #160 - "C1D", // #161 - "12K", // #162 - "822", // #163 - "E1T", // #164 - "Q4H", // #165 - "k4d", // #166 - "k4J", // #167 - "L70", // #168 - "31f", // #169 - "G1P", // #170 - "34e", // #171 - "939", // #172 - "24F", // #173 - "43r", // #174 - "M81", // #175 - "01E", // #176 - "A0N", // #177 - "65f", // #178 - "Q6p", // #179 - "93R", // #180 - "r0i", // #181 - "A35", // #182 - "P40", // #183 - "G9R", // #184 - "c7C", // #185 - "P17", // #186 - "76f", // #187 - "99p", // #188 - "96E", // #189 - "p3E", // #190 - "h6t", // #191 - "n2L", // #192 +const IDARR: [&str; 193] = [ + "Individual ID", //TOIO Num + "0", // #1 + "j1c", // #2 + "r81", // #3 + "26E", // #4 + "76t", // #5 + "broken", // #6 + "k5k", // #7 + "h41", // #8 + "0", // #9 + "0", // #10 + "0", // #11 + "Q3A", // #12 + "03a", // #13 + "0", // #14 + "K0m", // #15 + "0", // #16 + "0", // #17 + "p8B", // #18 + "91B", // #19 + "p75", // #20 + "G1E", // #21 + "k2L", // #22 + "b5p", // #23 + "J6C", // #24 + "a1K", // #25 + "b8T", // #26 + "b6A", // #27 + "01c", // #28 + "0", // #29 + "0", // #30 + "E2N", // #31 + "G7t", // #32 + "L6T", // #33 + "C0E", // #34 + "t79", // #35 + "J6k", // #36 + "d6f", // #37 + "0", // #38 + "M75", // #39 + "310", // #40 + "M5p", // #41 + "A4a", // #42 + "M9J", // #43 + "i01", // #44 + "T5m", // #45 + "j1G", // #46 + "40G", // #47 + "L6n", // #48 + "a3F", // #49 + "J8d", // #50 + "227", // #51 + "k4i", // #52 + "J68", // #53 + "90J", // #54 + "k96", // #55 + "0", // #56 + "0", // #57 + "0", // #58 + "0", // #59 + "0", // #60 + "0", // #61 + "0", // #62 + "0", // #63 + "0", // #64 + "0", // #65 + "0", // #66 + "0", // #67 + "0", // #68 + "0", // #69 + "0", // #70 + "0", // #71 + "0", // #72 + "0", // #73 + "0", // #74 + "0", // #75 + "0", // #76 + "0", // #77 + "0", // #78 + "E7c", // #79 + "P1B", // #80 + "F2B", // #81 + "L1H", // #82 + "D5i", // #83 + "m4Q", // #84 + "m1k", // #85 + "r52", // #86 + "k89", // #87 + "D2K", // #88 + "65r", // #89 + "f3K", // #90 + "13c", // #91 + "e1a", // #92 + "0", // #93 + "e6e", // #94 + "07F", // #95 + "m8k", // #96 + "79H", // #97 + "0", // #98 + "i1M", // #99 + "R3C", // #100 + "D98", // #101 + "m86", // #102 + "a66", // #103 + "0", // #104 + "E8T", // #105 + "J8n", // #106 + "N0b", // #107 + "586", // #108 + "p50", // #109 + "c9k", // #110 + "N0N", // #111 + "0", // #112 + "B1m", // #113 + "h7E", // #114 + "c05", // #115 + "K20", // #116 + "32D", // #117 + "F19", // #118 + "r4d", // #119 + "D2F", // #120 + "D0m", // #121 + "m6B", // #122 + "M0j", // #123 + "Q8G", // #124 + "A1t", // #125 + "p7J", // #126 + "t0H", // #127 + "M5i", // #128 + "j1L", // #129 + "e7i", // #130 + "T1E", // #131 + "85i", // #132 + "71H", // #133 + "20H", // #134 + "T9n", // #135 + "58B", // #136 + "J4R", // #137 + "93N", // #138 + "t0F", // #139 + "M7G", // #140 + "r4P", // #141 + "i1d", // #142 + "a22", // #143 + "M39", // #144 + "C23", // #145 + "816", // #146 + "E0M", // #147 + "T4b", // #148 + "L1L", // #149 + "i5m", // #150 + "P2R", // #151 + "t77", // #152 + "A5E", // #153 + "88e", // #154 + "k1b", // #155 + "m04", // #156 + "41b", // #157 + "B4k", // #158 + "J1M", // #159 + "H4M", // #160 + "C1D", // #161 + "12K", // #162 + "822", // #163 + "E1T", // #164 + "Q4H", // #165 + "k4d", // #166 + "k4J", // #167 + "L70", // #168 + "31f", // #169 + "G1P", // #170 + "34e", // #171 + "939", // #172 + "24F", // #173 + "43r", // #174 + "M81", // #175 + "01E", // #176 + "A0N", // #177 + "65f", // #178 + "Q6p", // #179 + "93R", // #180 + "r0i", // #181 + "A35", // #182 + "P40", // #183 + "G9R", // #184 + "c7C", // #185 + "P17", // #186 + "76f", // #187 + "99p", // #188 + "96E", // #189 + "p3E", // #190 + "h6t", // #191 + "n2L", // #192 ]; /// Format for a target to plug into the MotorTarget varient @@ -1078,6 +1078,7 @@ pub fn uuid_to_string(uuid: Uuid) -> String { .to_owned(); } +// parse a multi-targeting command fn parse_target_command(vals: Vec) -> Vec { let mut cmd = vec![]; @@ -1093,6 +1094,7 @@ fn parse_target_command(vals: Vec) -> Vec { return cmd; } +// parse a led command fn parse_led_command(repetitions: u8, vals: Vec) -> Vec { let mut cmd = vec![0x04, repetitions, vals.len() as u8]; @@ -1108,6 +1110,7 @@ fn parse_led_command(repetitions: u8, vals: Vec) -> Vec { return cmd; } +// parse a MIDI command fn parse_midi_command(repetitions: u8, vals: Vec) -> Vec { let mut cmd = vec![0x03, repetitions, vals.len() as u8]; @@ -1120,6 +1123,7 @@ fn parse_midi_command(repetitions: u8, vals: Vec) -> Vec { return cmd; } +// return the toio ID that corresponds to a specific toio fn return_toio_id(name: &str) -> Option { return IDARR.iter().position(|&r| r == name); } From 3326390354ff16ffb8565f34cfc4eb72b397b8bc Mon Sep 17 00:00:00 2001 From: Ramarko Bhattacharya <66401951+RamarkoB@users.noreply.github.com> Date: Sun, 6 Jul 2025 21:38:30 -0500 Subject: [PATCH 3/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e832292..0454c3c5 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ cargo run -- -a 48,46 NOTE: Do not uses spaces between the IDs. -Using the `-a` flag allows us to connect using the AxLab ID. When needed, we can also use `-n` to connect directly to their internal name, or even combine the two. For example: `cargo run -- -n P1B` or `cargo run -- -a 46 -n P1B` +Using the `-a` flag allows us to connect using the AxLab ID. ## Special Flags There are special flags for specific cases. From 3fddc9f00c170518c7084eda4ddf797588df12c9 Mon Sep 17 00:00:00 2001 From: Ramarko Bhattacharya Date: Thu, 15 Jan 2026 23:36:25 -0600 Subject: [PATCH 4/4] Updated Processing OSC Code --- toio_processing/events.pde | 6 +- toio_processing/osc.pde | 94 +++++++++++- toio_rust/src/main.rs | 103 +++++++------ toio_rust/src/osc.rs | 39 +++-- toio_rust/src/toio.rs | 290 ++++++++++++------------------------- toio_rust/src/ui.rs | 1 - 6 files changed, 265 insertions(+), 268 deletions(-) diff --git a/toio_processing/events.pde b/toio_processing/events.pde index 9429b662..d0907d92 100644 --- a/toio_processing/events.pde +++ b/toio_processing/events.pde @@ -8,7 +8,7 @@ void keyPressed() { //https://toio.github.io/toio-spec/en/docs/ble_motor/#motor-control-with-specified-duration //can use negative numbers to move toio backwards // void motor(int leftSpeed, int rightSpeed, int duration) - + cubes[0].motor(115, 115, 5); break; @@ -20,6 +20,7 @@ void keyPressed() { //motor control with target specified (simplified), specification found at: //https://toio.github.io/toio-spec/en/docs/ble_motor#motor-control-with-target-specified //control, timeout, maxspeed, and speed change are preset + cubes[0].target(200, 200, 270); break; @@ -30,8 +31,7 @@ void keyPressed() { //https://toio.github.io/toio-spec/en/docs/ble_motor/#motor-control-with-multiple-targets-specified //targets should be formatted as {x, y, theta} or {x, y}. Unless specified, theta = 0 // void multiTarget(int mode, int[][] targets) - - + int[][] targets = {{200, 200}, {200, 300, 90}, {300, 300}, {300, 200, 270}, {200, 200, 180}}; cubes[0].multiTarget(0, targets); break; diff --git a/toio_processing/osc.pde b/toio_processing/osc.pde index 9713d1e9..5020500f 100644 --- a/toio_processing/osc.pde +++ b/toio_processing/osc.pde @@ -21,7 +21,29 @@ void motorBasic(int cubeId, int leftspeed, int rightspeed) { oscP5.send(msg, server[hostId]); } -//basic motor control with duration, specification found at: +//basic motor control (advanced), specification found at: +//https://toio.github.io/toio-spec/en/docs/ble_motor#motor-control +void motorBasic(int cubeId, boolean leftforwards, int leftspeed, boolean rightforwards, int rightspeed) { + int hostId = cubeId/cubesPerHost; + int actualcubeid = cubeId % cubesPerHost; + OscMessage msg = new OscMessage("/motorbasic"); + msg.add(actualcubeid); + if (leftforwards) { + msg.add(0x01); + } else { + msg.add(0x02); + } + msg.add(leftspeed); + if (rightforwards) { + msg.add(0x01); + } else { + msg.add(0x02); + } + msg.add(rightspeed); + oscP5.send(msg, server[hostId]); +} + +//basic motor control (simplified), specification found at: //can use negative numbers to move toio backwards //https://toio.github.io/toio-spec/en/docs/ble_motor#motor-control void motorDuration(int cubeId, int leftspeed, int rightspeed, int duration) { @@ -45,7 +67,45 @@ void motorDuration(int cubeId, int leftspeed, int rightspeed, int duration) { oscP5.send(msg, server[hostId]); } -//motor control with target specified, specification found at: +//basic motor control (advanced), specification found at: +//https://toio.github.io/toio-spec/en/docs/ble_motor#motor-control +void motorDuration(int cubeId, boolean leftforwards, int leftspeed, boolean rightforwards, int rightspeed, int duration) { + int hostId = cubeId/cubesPerHost; + int actualcubeid = cubeId % cubesPerHost; + OscMessage msg = new OscMessage("/motorduration"); + msg.add(actualcubeid); + if (leftforwards) { + msg.add(0x01); + } else { + msg.add(0x02); + } + msg.add(leftspeed); + if (rightforwards) { + msg.add(0x01); + } else { + msg.add(0x02); + } + msg.add(rightspeed); + msg.add(duration); + oscP5.send(msg, server[hostId]); +} + +//motor control with target specified (simplified), specification found at: +//https://toio.github.io/toio-spec/en/docs/ble_motor#motor-control-with-target-specified +//control, timeout, maxspeed, and speed change are preset +void motorTarget(int cubeId, int mode, int x, int y, int theta){ + int hostId = cubeId/cubesPerHost; + int actualcubeid = cubeId % cubesPerHost; + OscMessage msg = new OscMessage("/motortarget"); + msg.add(actualcubeid); + msg.add(mode); + msg.add(x); + msg.add(y); + msg.add(theta); + oscP5.send(msg, server[hostId]); +} + +//motor control with target specified (advanced), specification found at: //https://toio.github.io/toio-spec/en/docs/ble_motor#motor-control-with-target-specified void motorTarget(int cubeId, int control, int timeout, int mode, int maxspeed, int speedchange, int x, int y, int theta){ int hostId = cubeId/cubesPerHost; @@ -63,7 +123,28 @@ void motorTarget(int cubeId, int control, int timeout, int mode, int maxspeed, i oscP5.send(msg, server[hostId]); } -//motor control with multiple targets specified, specification found at: +//motor control with multiple targets specified (simplified), specification found at: +//https://toio.github.io/toio-spec/en/docs/ble_motor#motor-control-with-target-specified +//targets should be formatted as {x, y, theta} or {x, y}. Unless specified, theta = 0 +void motorMultiTarget(int cubeId, int mode, int[][] targets) { + int hostId = cubeId/cubesPerHost; + int actualcubeid = cubeId % cubesPerHost; + OscMessage msg = new OscMessage("/multitargetsimple"); + msg.add(actualcubeid); + msg.add(mode); + for (int i = 0; i < targets.length; i++) { + for (int j = 0; j < targets[i].length; j++) { + msg.add(targets[i][j]); + } + + if (targets[i].length == 2) { + msg.add(0); + } + } + oscP5.send(msg, server[hostId]); +} + +//motor control with multiple targets specified (advanced), specification found at: //https://toio.github.io/toio-spec/en/docs/ble_motor#motor-control-with-target-specified //targets should be formatted as {x, y, theta} or {x, y}. Unless specified, theta = 0 void motorMultiTarget(int cubeId, int control, int timeout, int mode, int maxspeed, int speedchange, int[][] targets){ @@ -81,6 +162,10 @@ void motorMultiTarget(int cubeId, int control, int timeout, int mode, int maxspe for (int j = 0; j < targets[i].length; j++) { msg.add(targets[i][j]); } + + if (targets[i].length == 2) { + msg.add(0); + } } oscP5.send(msg, server[hostId]); } @@ -129,7 +214,7 @@ void lightLed(int cubeId, int repetitions, int[][] lights) { msg.add(repetitions); for (int i = 0; i < lights.length; i++) { - msg.add(lights[i][0]); + msg.add(lights[i][0]); for (int j = 1; j < lights[i].length; j++) { msg.add(lights[i][j]); } @@ -158,7 +243,6 @@ void soundMidi(int cubeId, int duration, int noteID, int volume) { OscMessage msg = new OscMessage("/midi"); msg.add(actualcubeid); msg.add(1); - msg.add(duration); msg.add(noteID); msg.add(volume); diff --git a/toio_rust/src/main.rs b/toio_rust/src/main.rs index d68909ee..3342969c 100644 --- a/toio_rust/src/main.rs +++ b/toio_rust/src/main.rs @@ -10,13 +10,13 @@ use std::error::Error; use std::net::UdpSocket; use std::process; use std::sync::Arc; -use std::time::SystemTime; -use std::vec; +use std::time::{Duration, SystemTime}; use clap::Parser; use futures::future::join_all; use futures::future::Either::{Left, Right}; use tokio::sync::RwLock; +use tokio::time; #[derive(Parser)] #[command(name = "toio")] @@ -74,7 +74,7 @@ async fn main() -> Result<(), Box> { ToioScanner::new().await? } }; - // let scanner = ToioScanner::new_with_filter(true, vec![3, 100]).await?; + let mut toios = scanner.search().await?; let connected: Arc>>>> = Arc::new(RwLock::new(vec![])); @@ -92,11 +92,10 @@ async fn main() -> Result<(), Box> { let socket = Arc::new(UdpSocket::bind(&host_addr)?); let mut buf = [0u8; rosc::decoder::MTU]; - // whenever a message is recieved through OSC, forward to toio + // whenever a message is received through OSC, forward to toio let sock = socket.clone(); let connected_clone = connected.clone(); tokio::spawn(async move { - // let mut now = SystemTime::now(); while let Ok(size) = sock.recv(&mut buf) { if let Ok((_, packet)) = rosc::decoder::decode_udp(&buf[..size]) { if let Some((toionum, cmd)) = handle_packet(packet, args.terminal) { @@ -104,10 +103,10 @@ async fn main() -> Result<(), Box> { if toionum < connected_read.len() { let toio = connected_read[toionum].read().await; - let last_command = toio.get_last_command(); - let mut last_command_write = last_command.write().await; - *last_command_write = Some(SystemTime::now()); + // Update last command timestamp + toio.update_last_command(); + // Forward command to toio toio.toio.send_command(cmd).await; } } @@ -128,13 +127,12 @@ async fn main() -> Result<(), Box> { let mut updates = toio_peripheral.updates().await.unwrap(); // create instance of Toio to record toio info - let mut toio = Toio::new(toio_peripheral); + let toio = Toio::new(toio_peripheral); if args.terminal { println!("Toio Connected: {}", toio.id); } - let battery = toio.get_battery(); - let last_update = toio.get_last_update(); + let state = toio.state.clone(); // request permission to write to list of connected toios let mut connected_write = connected_clone.write().await; @@ -144,24 +142,34 @@ async fn main() -> Result<(), Box> { let toio_channel = tokio::spawn({ let to_addr = to_addr.clone(); async move { - while let Some(update) = updates.next().await { - // if it is a battery update, record it in the Toio - if let Update::Battery { level } = update { - let mut battery = battery.write().await; - *battery = Some(level); + loop { + match updates.next().await { + Some(update) => { + // Update state based on update type + { + let mut state_write = state.write().await; + if let Update::Battery { level } = update { + state_write.battery = Some(level); + } + state_write.last_update = Some(SystemTime::now()); + } + + send_packet(&sock, &to_addr, id, update, args.terminal); + } + None => { + break; // Stream ended, break out + } } - - // record time of update - let mut last_update = last_update.write().await; - *last_update = Some(SystemTime::now()); - - send_packet(&sock, &to_addr, id, update, args.terminal); } } }); - toio.add_channel(toio_channel); - connected_write.push(Arc::new(RwLock::new(toio))); + let toio = Arc::new(RwLock::new(toio)); + { + let mut toio_write = toio.write().await; + toio_write.add_channel(toio_channel); + } + connected_write.push(toio); } Right(peripheral_id) => { // request permission to write to list of connected toios @@ -189,7 +197,7 @@ async fn main() -> Result<(), Box> { } }); - // // start TUI process + // start TUI process let mut terminal: ToioUI = None; if !args.terminal { terminal = setup_terminal()?; @@ -197,7 +205,10 @@ async fn main() -> Result<(), Box> { // update UI from all of the toios let connected_clone = connected.clone(); + let mut interval = time::interval(Duration::from_millis(50)); // 20 FPS loop { + interval.tick().await; + let connected_read = connected_clone.read().await; // get info from all of the toios @@ -205,25 +216,22 @@ async fn main() -> Result<(), Box> { let toio = toio_guard.read().await; let name = toio.name.clone(); let id = toio.id.clone(); - let connected = toio.is_connected().await; + + // Read toio state + let state = toio.state.read().await; + let connected = toio.connected; // get battery level - let battery_string = if let Some(level) = *toio.battery.read().await { + let battery_string = if let Some(level) = state.battery { format!("{}", level) } else { "N/A".to_string() }; // get time of last update - let last_update_string = if let Some(last) = *toio.last_update.read().await { + let last_update_string = if let Some(last) = state.last_update { if let Ok(time) = last.elapsed() { - if time.as_millis() < 50 { - "<50ms".to_string() - } else if time.as_secs() < 1 { - format!(">{}ms", time.as_millis() - (time.as_millis() % 100)) - } else { - format!("{}s", time.as_secs()) - } + format_elapsed_time(time) } else { "N/A".to_string() } @@ -232,15 +240,9 @@ async fn main() -> Result<(), Box> { }; // get time of last command - let last_command_string = if let Some(last) = *toio.last_command.read().await { + let last_command_string = if let Some(last) = state.last_command { if let Ok(time) = last.elapsed() { - if time.as_millis() < 50 { - "<50ms".to_string() - } else if time.as_secs() < 1 { - format!(">{}ms", time.as_millis() - (time.as_millis() % 100)) - } else { - format!("{}s", time.as_secs()) - } + format_elapsed_time(time) } else { "N/A".to_string() } @@ -259,15 +261,26 @@ async fn main() -> Result<(), Box> { })) .await; - // update UI + // update UI if let Some(ref mut toio_ui) = terminal { toio_ui.draw(ui(toio_info, args.axlab_id.clone()))?; } - // // exit terminal if "Q" key is pressed + // exit terminal if "Q" key is pressed if handle_events()? { exit_terminal()?; process::exit(0); } } } + +// Helper function to format elapsed time +fn format_elapsed_time(time: Duration) -> String { + if time.as_millis() < 50 { + "<50ms".to_string() + } else if time.as_secs() < 1 { + format!(">{}ms", time.as_millis() - (time.as_millis() % 100)) + } else { + format!("{}s", time.as_secs()) + } +} diff --git a/toio_rust/src/osc.rs b/toio_rust/src/osc.rs index b9fdf185..0f89a36a 100644 --- a/toio_rust/src/osc.rs +++ b/toio_rust/src/osc.rs @@ -1,7 +1,6 @@ use std::io::{self}; use std::net::UdpSocket; - -use std::vec; +use std::time::SystemTime; use rosc::encoder; use rosc::{OscMessage, OscPacket, OscType}; @@ -24,10 +23,10 @@ pub fn handle_events() -> io::Result { pub fn handle_packet(packet: OscPacket, show_terminal: bool) -> Option<(usize, Command)> { match packet { OscPacket::Message(msg) => { - let mut vals: Vec = msg + let vals: Vec = msg .args .iter() - .flat_map(|val| match val { + .filter_map(|val| match val { OscType::Int(i) => Some(*i), _ => None, }) @@ -74,8 +73,7 @@ pub fn handle_packet(packet: OscPacket, show_terminal: bool) -> Option<(usize, C max_speed: vals[4] as u8, speed_change: vals[5] as u8, op_add: 1, - targets: vals - .split_off(6) + targets: vals[6..] .chunks(3) .map(|target| TargetCommand { x_target: target[0] as u16, @@ -86,8 +84,7 @@ pub fn handle_packet(packet: OscPacket, show_terminal: bool) -> Option<(usize, C }), "/led" => Some(Command::MultiLed { repetitions: vals[1] as u8, - lights: vals - .split_off(2) + lights: vals[2..] .chunks(4) .map(|light| LedCommand { duration: light[0] as u8, @@ -103,8 +100,7 @@ pub fn handle_packet(packet: OscPacket, show_terminal: bool) -> Option<(usize, C }), "/midi" => Some(Command::Midi { repetitions: vals[1] as u8, - notes: vals - .split_off(2) + notes: vals[2..] .chunks(3) .map(|note| MidiCommand { duration: note[0] as u8, @@ -113,15 +109,14 @@ pub fn handle_packet(packet: OscPacket, show_terminal: bool) -> Option<(usize, C }) .collect(), }), - _ => None, }; if show_terminal { - println!("Update Received [{}]: {:?}", msg.addr.as_str(), vals,) + println!("[{:?}] Command Sent: {:?}", SystemTime::now(), cmd); } - // Return pair of (toioID, pair) + // Return pair of (toioID, cmd) cmd.map(|cmd| (vals[0] as usize, cmd)) } _ => None, @@ -146,7 +141,7 @@ pub fn send_packet( vec![x_center as i32, y_center as i32, theta as i32], )), Update::Battery { level } => Some(("/battery", vec![level as i32])), - Update::Button { pressed } => Some(("/button", vec![if pressed { 0x00 } else { 0x80 }])), + Update::Button { pressed } => Some(("/button", vec![if pressed { 0x80 } else { 0x00 }])), Update::Motion { horizontal, collision, @@ -185,7 +180,6 @@ pub fn send_packet( "/PostureQuaternion", vec![w as i32, x as i32, y as i32, z as i32], )), - // Update::PostureHighPrecisionEuler { .. } => todo!(), Update::Magnetic { state, strength, @@ -206,6 +200,15 @@ pub fn send_packet( }; if let Some((addr, args)) = vals { + if show_terminal { + println!( + "[{:?}] Update Received [{}]: {:?}", + SystemTime::now(), + addr, + args + ) + } + let msg = encoder::encode(&OscPacket::Message(OscMessage { addr: addr.to_string(), args: vec![id as i32] @@ -216,10 +219,6 @@ pub fn send_packet( })) .unwrap(); - if show_terminal { - println!("Update Received [{}]: {:?}", addr, args) - } - - socket.send_to(&msg, to_addr).unwrap(); + let _ = socket.send_to(&msg, to_addr); } } diff --git a/toio_rust/src/toio.rs b/toio_rust/src/toio.rs index 784f3962..2ab36865 100644 --- a/toio_rust/src/toio.rs +++ b/toio_rust/src/toio.rs @@ -8,7 +8,6 @@ use tokio::sync::mpsc; use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::RwLock; use tokio::task::JoinHandle; -use tokio::time::timeout; use futures::stream::StreamExt; @@ -229,10 +228,8 @@ const IDARR: [&str; 193] = [ "n2L", // #192 ]; -/// Format for a target to plug into the MotorTarget varient -/// of the Command enum. By putting multiple of these into a vector, -/// you can send a a series of targets for a toio to travel to in -/// a sequence. +/// Format for a target to plug into the MotorTarget variant +/// of the Command enum. #[derive(Clone, Debug)] pub struct TargetCommand { pub x_target: u16, @@ -240,10 +237,8 @@ pub struct TargetCommand { pub theta_target: u16, } -/// Format for a RGB color to plug into the MultiLed varient -/// of the Command enum. By putting multiple of these into a vector, -/// you can send a a series of colors for a toio to flash on its led -/// in a sequence. +/// Format for a RGB color to plug into the MultiLed variant +/// of the Command enum. #[derive(Clone, Debug)] pub struct LedCommand { pub duration: u8, @@ -252,10 +247,8 @@ pub struct LedCommand { pub green: u8, } -/// Format for a MIDI note to plug into the Midi varient -/// of the Command enum. By putting multiple of these into a vector, -/// you can send a a series of notes for a toio to play in -/// a sequence. +/// Format for a MIDI note to plug into the Midi variant +/// of the Command enum. #[derive(Clone, Debug)] pub struct MidiCommand { pub duration: u8, @@ -342,7 +335,7 @@ pub enum Command { }, } -/// An enum to list out all possible updates to recieve from a toio +/// An enum to list out all possible updates to receive from a toio #[allow(dead_code)] #[derive(Debug)] pub enum Update { @@ -429,15 +422,31 @@ pub struct ToioPeripheral { pub peripheral_id: platform::PeripheralId, } +// Consolidated state struct to reduce lock overhead +#[derive(Clone)] +pub struct ToioState { + pub battery: Option, + pub last_update: Option, + pub last_command: Option, +} + +impl ToioState { + fn new() -> Self { + ToioState { + battery: None, + last_update: None, + last_command: None, + } + } +} + pub struct Toio { pub toio: ToioPeripheral, pub name: String, pub id: String, pub connected: bool, pub channel: Option>, - pub battery: Arc>>, - pub last_update: Arc>>, - pub last_command: Arc>>, + pub state: Arc>, } impl Updates { @@ -455,7 +464,6 @@ impl ToioScanner { let manager = Manager::new().await?; // get the first bluetooth adapter - // connect to the adapter let central = manager .adapters() .await @@ -478,7 +486,6 @@ impl ToioScanner { let manager = Manager::new().await?; // get the first bluetooth adapter - // connect to the adapter let central = manager .adapters() .await @@ -519,7 +526,7 @@ impl ToioScanner { let peripheral = central.peripheral(&id).await.unwrap(); if ordered { - // if succesful connection and the filter is ordered, update filter + // if filtered order, update filter on successful connection if let Some(ref mut new_filter) = toio_filter.clone() { if !new_filter.is_empty() { let curr_toio = new_filter.remove(0); @@ -544,7 +551,7 @@ impl ToioScanner { let peripheral = central.peripheral(&id).await.unwrap(); if ordered { - // if succesful connection and the filter is ordered, update filter + // if filtered order, update filter on successful connection if let Some(ref mut new_filter) = toio_filter.clone() { if !new_filter.is_empty() { let curr_toio = new_filter.remove(0); @@ -661,17 +668,11 @@ impl ToioPeripheral { let mut notification_stream = self.peripheral.notifications().await?; tokio::spawn(async move { - // let notification_steam = notification_stream; - - while let Ok(possible_event) = timeout( - std::time::Duration::from_secs(5), - notification_stream.next(), - ) - .await - { - if let Some(event) = possible_event { - if let Some(update) = ToioPeripheral::get_update(event) { - tx.send(update).await.unwrap(); + while let Some(event) = notification_stream.next().await { + if let Some(update) = ToioPeripheral::get_update(event) { + if tx.send(update).await.is_err() { + // Channel closed, exit gracefully + break; } } } @@ -739,67 +740,6 @@ impl ToioPeripheral { posture: vals[4], shake: vals[5], }), - // 0x02 => Some(Update::Magnetic { - // state: vals[1], - // strength: vals[2], - // forcex: vals[3] as i8, - // forcey: vals[4] as i8, - // forcez: vals[5] as i8, - // }), - // 0x03 => match vals[1] { - // 0x01 => Some(Update::PostureEuler { - // roll: vals[2] as u16 | (vals[3] as u16) << 8, - // pitch: vals[4] as u16 | (vals[5] as u16) << 8, - // yaw: vals[5] as u16 | (vals[6] as u16) << 8, - // }), - // 0x02 => Some(Update::PostureQuaternion { - // w: 0.0, - // // vals[2] as f32 - // // | (vals[3] as f32) << 8 - // // | (vals[4] as f32) << 16 - // // | (vals[5] as f32) << 24, - // x: 0.0, - // // vals[6] as f32 - // // | (vals[7] as f32) << 8 - // // | (vals[8] as f32) << 16 - // // | (vals[9] as f32) << 24, - // y: 0.0, - // // vals[10] as f32 - // // | (vals[11] as f32) << 8 - // // | (vals[12] as f32) << 16 - // // | (vals[13] as f32) << 24, - // z: 0.0, - // // vals[14] as f32 - // // | (vals[15] as f32) << 8 - // // | (vals[16] as f32) << 16 - // // | (vals[17] as f32) << 24, - // }), - // 0x03 => Some(Update::PostureHighPrecisionEuler { - // roll: 0.0, - // // vals[2] as f32 - // // | (vals[3] as f32) << 8 - // // | (vals[4] as f32) << 16 - // // | (vals[5] as f32) << 24, - // pitch: 0.0, - // // vals[6] as f32 - // // | (vals[7] as f32) << 8 - // // | (vals[8] as f32) << 16 - // // | (vals[9] as f32) << 24, - // yaw: 0.0, - // // vals[10] as f32 - // // | (vals[11] as f32) << 8 - // // | (vals[12] as f32) << 16 - // // | (vals[13] as f32) << 24, - // }), - // _ => { - // println!( - // "Unkown {} Update: {:?}", - // uuid_to_string(notification.uuid), - // vals - // ); - // None - // } - // }, _ => { println!( "Unkown {} Update: {:?}", @@ -847,49 +787,39 @@ impl ToioPeripheral { }; let cmd: Vec = match command { - Command::MotionRequest => { - vec![0x81] - } - Command::MagneticRequest => { - vec![0x82] - } - Command::PostureRequest { format } => { - vec![0x83, format] - } + Command::MotionRequest => vec![0x81], + Command::MagneticRequest => vec![0x82], + Command::PostureRequest { format } => vec![0x83, format], Command::MotorControl { left_direction, left_speed, right_direction, right_speed, - } => { - vec![ - 0x01, - 0x01, - left_direction, - left_speed, - 0x02, - right_direction, - right_speed, - ] - } + } => vec![ + 0x01, + 0x01, + left_direction, + left_speed, + 0x02, + right_direction, + right_speed, + ], Command::MotorDuration { left_direction, left_speed, right_direction, right_speed, duration, - } => { - vec![ - 0x02, - 0x01, - left_direction, - left_speed, - 0x02, - right_direction, - right_speed, - duration, - ] - } + } => vec![ + 0x02, + 0x01, + left_direction, + left_speed, + 0x02, + right_direction, + right_speed, + duration, + ], Command::MotorTarget { control, timeout, @@ -899,23 +829,21 @@ impl ToioPeripheral { x_target, y_target, theta_target, - } => { - vec![ - 0x03, - control, - timeout, - move_type, - max_speed, - speed_change, - 0x00, - (x_target & 0x00FF) as u8, - ((x_target & 0xFF00) >> 8) as u8, - (y_target & 0x00FF) as u8, - ((y_target & 0xFF00) >> 8) as u8, - (theta_target & 0x00FF) as u8, - ((theta_target & 0xFF00) >> 8) as u8, - ] - } + } => vec![ + 0x03, + control, + timeout, + move_type, + max_speed, + speed_change, + 0x00, + (x_target & 0x00FF) as u8, + ((x_target & 0xFF00) >> 8) as u8, + (y_target & 0x00FF) as u8, + ((y_target & 0xFF00) >> 8) as u8, + (theta_target & 0x00FF) as u8, + ((theta_target & 0xFF00) >> 8) as u8, + ], Command::MultiTarget { control, timeout, @@ -938,7 +866,6 @@ impl ToioPeripheral { cmd.append(&mut parse_target_command(targets)); cmd } - Command::MotorAcceleration { velocity, acceleration, @@ -947,47 +874,33 @@ impl ToioPeripheral { direction, priority, duration, - } => { - vec![ - 0x05, - velocity, - acceleration, - (rotational_velocity & 0x00FF) as u8, - ((rotational_velocity & 0xFF00) >> 8) as u8, - rotational_direction, - direction, - priority, - duration, - ] - } - Command::LedOff => { - vec![0x01] - } + } => vec![ + 0x05, + velocity, + acceleration, + (rotational_velocity & 0x00FF) as u8, + ((rotational_velocity & 0xFF00) >> 8) as u8, + rotational_direction, + direction, + priority, + duration, + ], + Command::LedOff => vec![0x01], Command::Led { duration, red, green, blue, - } => { - vec![0x03, duration, 0x01, 0x01, red, green, blue] - } + } => vec![0x03, duration, 0x01, 0x01, red, green, blue], Command::MultiLed { repetitions, lights, } => parse_led_command(repetitions, lights), - Command::SoundOff => { - vec![0x01] - } + Command::SoundOff => vec![0x01], Command::Sound { sound_effect, volume, - } => { - vec![ - 0x02, //sound - sound_effect, //sound effect ID - volume, //volume - ] - } + } => vec![0x02, sound_effect, volume], Command::Midi { repetitions, notes } => parse_midi_command(repetitions, notes), }; @@ -1007,11 +920,10 @@ impl ToioPeripheral { properties: response_flag, }; - // println!("{} : {:?}", uuid_to_string(uuid), cmd); - self.peripheral + let _ = self + .peripheral .write(&characteristic, &cmd, response_type) - .await - .unwrap(); + .await; } } @@ -1026,10 +938,8 @@ impl Toio { }, connected: true, channel: None, - battery: Arc::new(RwLock::new(None)), + state: Arc::new(RwLock::new(ToioState::new())), toio, - last_update: Arc::new(RwLock::new(None)), - last_command: Arc::new(RwLock::new(None)), }; } @@ -1044,24 +954,16 @@ impl Toio { self.connected = false; } - pub fn get_battery(&self) -> Arc>> { - return self.battery.clone(); - } - - pub fn get_last_update(&self) -> Arc>> { - return self.last_update.clone(); - } - - pub fn get_last_command(&self) -> Arc>> { - return self.last_command.clone(); - } - - pub async fn is_connected(&self) -> bool { - return self.connected; + pub fn update_last_command(&self) { + let state = self.state.clone(); + tokio::spawn(async move { + let mut state_write = state.write().await; + state_write.last_command = Some(SystemTime::now()); + }); } } -/// matches UUIDs of toios a string of their coresponding service +/// matches UUIDs of toios a string of their corresponding service pub fn uuid_to_string(uuid: Uuid) -> String { return match uuid { SERVICE => "Service", diff --git a/toio_rust/src/ui.rs b/toio_rust/src/ui.rs index affc21a8..72faac8e 100644 --- a/toio_rust/src/ui.rs +++ b/toio_rust/src/ui.rs @@ -1,6 +1,5 @@ use std::error::Error; use std::io::stdout; -use std::vec; use crossterm::{ cursor::Show,