From 051c4fd410c3e53526ef3cbe4c453acbfa9f1151 Mon Sep 17 00:00:00 2001 From: Sachymetsu Date: Fri, 9 Jan 2026 17:50:05 +0100 Subject: [PATCH] Strike connection and update loop --- .cargo/config.toml | 9 ++ Cargo.lock | 182 +++++++++++++++++++++++++--------------- Cargo.toml | 9 ++ src/device.rs | 9 +- src/net.rs | 60 ++++++++++++- src/views.rs | 24 ++++-- src/views/home.rs | 16 +++- src/views/monitoring.rs | 150 +++++++++++++++++++++++++++++++-- 8 files changed, 363 insertions(+), 96 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..21dc54f --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.x86_64-unknown-linux-gnu] +linker = "clang" +rustflags = ["-C", "link-arg=-fuse-ld=/usr/bin/mold"] + +[profile.dev.build-override] +opt-level = 1 + +[env] +RUST_LOG = "info" diff --git a/Cargo.lock b/Cargo.lock index 83962cd..56c97ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,7 +106,7 @@ checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043" dependencies = [ "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -354,7 +354,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -407,7 +407,7 @@ checksum = "f9396b256b366a43d7f61d1f230cdab0a512fb4712cbf7d688f3d6fce4c5ea8a" dependencies = [ "bevy_macro_utils", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -464,7 +464,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -582,7 +582,7 @@ dependencies = [ "parking_lot", "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", "toml_edit", ] @@ -735,7 +735,7 @@ dependencies = [ "indexmap", "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", "uuid", ] @@ -763,7 +763,7 @@ checksum = "bcda45913b1d6470c6b751656e72fb3f25ca6b5b7b2ee055b294aaed1eb7e5ba" dependencies = [ "bevy_macro_utils", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -878,15 +878,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", + "cpufeatures", ] [[package]] @@ -943,7 +944,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -975,9 +976,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.51" +version = "1.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ "find-msvc-tools", "jobserver", @@ -1106,9 +1107,9 @@ checksum = "87ca1caa64ef4ed453e68bb3db612e51cf1b2f5b871337f0fcab1c8f87cc3dff" [[package]] name = "constant_time_eq" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" [[package]] name = "constgebra" @@ -1243,9 +1244,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.11" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ "darling_core", "darling_macro", @@ -1253,27 +1254,26 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.11" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ - "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] name = "darling_macro" -version = "0.20.11" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -1310,7 +1310,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.112", + "syn 2.0.114", "unicode-xid", ] @@ -1409,7 +1409,7 @@ checksum = "1796db3d892515842ca2dfb11124c4bb4a9e58d9f2c5c1072e5bca1b2334507b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -1508,9 +1508,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" [[package]] name = "finl_unicode" @@ -1651,9 +1651,9 @@ checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "glam" -version = "0.30.9" +version = "0.30.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46" +checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" dependencies = [ "bytemuck", "libm", @@ -1770,9 +1770,9 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown", @@ -1789,15 +1789,15 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6778b0196eefee7df739db78758e5cf9b37412268bfa5650bfeed028aed20d9c" +checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" dependencies = [ "darling", "indoc", "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -1833,6 +1833,47 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "jiff" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" +dependencies = [ + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-sys 0.61.2", +] + +[[package]] +name = "jiff-static" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68971ebff725b9e2ca27a601c5eb38a4c5d64422c4cbab0c535f248087eda5c2" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "jni" version = "0.21.1" @@ -1900,9 +1941,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.178" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libm" @@ -1961,9 +2002,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.16.2" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" +checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" dependencies = [ "hashbrown", ] @@ -2138,7 +2179,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -2170,7 +2211,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -2258,9 +2299,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.8.4" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" +checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" dependencies = [ "memchr", "ucd-trie", @@ -2268,9 +2309,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.4" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" +checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" dependencies = [ "pest", "pest_generator", @@ -2278,22 +2319,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.4" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" +checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] name = "pest_meta" -version = "2.8.4" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" +checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" dependencies = [ "pest", "sha2", @@ -2339,7 +2380,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -2368,7 +2409,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -2455,9 +2496,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.104" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] @@ -2473,9 +2514,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.42" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] @@ -2806,7 +2847,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -2953,9 +2994,12 @@ dependencies = [ "color-eyre", "crossterm", "futures-concurrency", + "jiff", + "postcard", "rapidhash", "ratatui", "sachy-mdns", + "serde", "socket2", "striker-proto", ] @@ -2992,7 +3036,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -3014,9 +3058,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.112" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -3112,7 +3156,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -3123,7 +3167,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -3220,7 +3264,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -3387,7 +3431,7 @@ checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] [[package]] @@ -3475,7 +3519,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", "wasm-bindgen-shared", ] @@ -3807,20 +3851,20 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.112", + "syn 2.0.114", ] diff --git a/Cargo.toml b/Cargo.toml index 0e2e2d0..b8abe79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,9 @@ socket2 = "0.6.1" sachy-mdns = { git = "https://tangled.org/sachy.dev/sachy-embed-core", package = "sachy-mdns", features = [ "client", ] } +serde.workspace = true +postcard.workspace = true +jiff = "0.2.18" [lib] path = "src/lib.rs" @@ -46,3 +49,9 @@ path = "src/lib.rs" [[bin]] path = "src/main.rs" name = "striker" + +[profile.release] +codegen-units = 1 +lto = true +strip = true +panic = "abort" diff --git a/src/device.rs b/src/device.rs index 1b7153f..ba91d7e 100644 --- a/src/device.rs +++ b/src/device.rs @@ -11,6 +11,7 @@ use bevy::{ system::{Commands, Res, ResMut}, world::DeferredWorld, }, + prelude::Deref, }; use rapidhash::RapidHashSet; @@ -47,14 +48,14 @@ pub struct StormLevel(pub u16); #[derive(Debug, Component)] #[component(immutable)] -pub struct Timestamp(pub i64); +pub struct Timestamp(pub jiff::Timestamp); #[derive(Debug, Component)] #[relationship(relationship_target = StormLevels)] -pub struct Source(pub Entity); +pub struct StormSource(pub Entity); -#[derive(Component, Debug)] -#[relationship_target(relationship = Source)] +#[derive(Component, Debug, Deref)] +#[relationship_target(relationship = StormSource)] pub struct StormLevels { levels: Vec, } diff --git a/src/net.rs b/src/net.rs index 322db51..f721b29 100644 --- a/src/net.rs +++ b/src/net.rs @@ -1,6 +1,7 @@ use core::net::IpAddr; use std::{ - net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}, + io::Read, + net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpStream, UdpSocket}, time::Duration, }; @@ -26,6 +27,17 @@ use socket2::{Domain, Protocol, Socket, Type}; #[derive(Debug, Resource)] pub struct DiscoverResponse(pub Receiver); +#[derive(Debug, Resource)] +pub struct StrikeUpdates(pub Receiver); + +#[derive(Debug, Resource)] +pub struct StrikeConnect(pub Sender); + +pub enum StrikeConnection { + Connect(SocketAddr), + Disconnect, +} + pub struct InstanceDetails { pub host: String, pub address: String, @@ -121,10 +133,54 @@ pub fn setup_mdns_task(mut commands: Commands) -> Result { Ok(()) } +pub fn setup_strike_connection(mut commands: Commands) { + let io = IoTaskPool::get(); + + let (signal_tx, signal_rx) = async_channel::bounded(1); + let (resp_tx, resp_rx) = async_channel::bounded(64); + + io.spawn(async move { + let mut buf = vec![0u8; 4096]; + + while let Ok(StrikeConnection::Connect(addr)) = signal_rx.recv().await { + let net_fut = async { + loop { + let Ok(stream) = Async::::connect(addr).await else { + Timer::after(Duration::from_secs(1)).await; + continue; + }; + + while let Ok(read) = stream.read_with(|mut a| a.read(&mut buf)).await { + resp_tx + .send(postcard::from_bytes(&buf[..read]).unwrap()) + .await + .ok(); + } + } + }; + + let cancel_fut = async { + while signal_rx + .recv() + .await + .is_ok_and(|strike| !matches!(strike, StrikeConnection::Disconnect)) + { + } + }; + + (net_fut, cancel_fut).race().await; + } + }) + .detach(); + + commands.insert_resource(StrikeConnect(signal_tx)); + commands.insert_resource(StrikeUpdates(resp_rx)); +} + pub struct NetPlugin; impl Plugin for NetPlugin { fn build(&self, app: &mut bevy::app::App) { - app.add_systems(Startup, setup_mdns_task); + app.add_systems(Startup, (setup_mdns_task, setup_strike_connection)); } } diff --git a/src/views.rs b/src/views.rs index 8d72bae..bfa340e 100644 --- a/src/views.rs +++ b/src/views.rs @@ -35,14 +35,20 @@ pub struct MonitoringViewPlugin; impl Plugin for MonitoringViewPlugin { fn build(&self, app: &mut bevy::app::App) { - app.add_systems( - PostUpdate, - ( - monitoring::monitoring_message_handler, - monitoring::monitoring_view, - ) - .chain() - .run_if(in_state(AppState::Monitoring)), - ); + app.add_systems(OnEnter(AppState::Monitoring), monitoring::enter_monitoring) + .add_systems(OnExit(AppState::Monitoring), monitoring::exit_monitoring) + .add_systems( + PostUpdate, + ( + monitoring::monitoring_message_handler, + ( + monitoring::update_device_data, + monitoring::clear_device_data, + ), + monitoring::monitoring_view, + ) + .chain() + .run_if(in_state(AppState::Monitoring)), + ); } } diff --git a/src/views/home.rs b/src/views/home.rs index 81ff5c9..14fd10e 100644 --- a/src/views/home.rs +++ b/src/views/home.rs @@ -15,7 +15,7 @@ use bevy::{ use bevy_ratatui::RatatuiContext; use ratatui::{ layout::{Constraint, HorizontalAlignment, Layout}, - style::Color, + style::{Color, Style}, widgets::{Block, List, ListDirection, ListItem, ListState, Padding, Paragraph}, }; @@ -96,8 +96,12 @@ pub fn home_view( q_devices: Query<(&Name, &DeviceSocket), With>, ) -> Result { context.draw(|frame| { - let [top, mid, bottom] = - Layout::vertical([Constraint::Length(3), Constraint::Fill(1), Constraint::Length(3)]).areas(frame.area()); + let [top, mid, bottom] = Layout::vertical([ + Constraint::Length(3), + Constraint::Fill(1), + Constraint::Length(3), + ]) + .areas(frame.area()); let searching = if is_searching.searching.is_some() { "Searching..." @@ -119,7 +123,9 @@ pub fn home_view( )) }); - let help = Paragraph::new("Keys: 'q'/ESC Quit, 's' Toggle Search, UP/DOWN Choose Device(s), ENTER/SPACE Connect to Device").block( + let help_text = "Keys: 'q'/ESC Quit, 's' Toggle Search, UP/DOWN Choose Device(s), ENTER/SPACE Connect to Device"; + + let help = Paragraph::new(help_text).block( Block::bordered() .padding(Padding::horizontal(2)) .border_style(Color::LightBlue), @@ -127,6 +133,8 @@ pub fn home_view( let list = List::new(items) .highlight_symbol(">> ") + .highlight_spacing(ratatui::widgets::HighlightSpacing::Always) + .highlight_style(Style::new().bg(Color::DarkGray)) .direction(ListDirection::TopToBottom) .block( Block::bordered() diff --git a/src/views/monitoring.rs b/src/views/monitoring.rs index 51950ae..4babf49 100644 --- a/src/views/monitoring.rs +++ b/src/views/monitoring.rs @@ -1,27 +1,41 @@ +use core::net::SocketAddr; + use bevy::{ ecs::{ + entity::Entity, error::Result, message::MessageReader, name::Name, query::With, + resource::Resource, system::{Commands, Query, Res, ResMut}, world::World, }, state::state::NextState, + time::{Real, Time, Timer}, }; use bevy_ratatui::RatatuiContext; +use jiff::SignedDuration; use ratatui::{ layout::{Constraint, HorizontalAlignment, Layout}, style::Color, + text::{Line, Span}, widgets::{Block, Padding, Paragraph}, }; +use striker_proto::{StrikerResponse, Update}; use crate::{ - device::{ConnectedDevice, Device}, + device::{ + ConnectedDevice, Device, DeviceSocket, StormLevel, StormLevels, StormSource, Timestamp, + }, messages::StrikeMessage, + net::{StrikeConnect, StrikeConnection, StrikeUpdates}, state::AppState, }; +#[derive(Debug, Resource)] +pub struct DataClear(pub Timer); + pub fn monitoring_message_handler( mut strike_reader: MessageReader, mut commands: Commands, @@ -36,18 +50,105 @@ pub fn monitoring_message_handler( } } +pub fn enter_monitoring( + connected: Res, + signal: Res, + q_devices: Query<&DeviceSocket, With>, + mut commands: Commands, +) -> Result { + let addr = q_devices.get(connected.0)?; + + signal + .0 + .try_send(StrikeConnection::Connect(SocketAddr::new( + addr.ip, addr.port, + )))?; + + commands.insert_resource(DataClear(Timer::from_seconds( + 60.0 * 1.0, + bevy::time::TimerMode::Repeating, + ))); + + Ok(()) +} + +pub fn exit_monitoring(signal: Res, mut commands: Commands) -> Result { + signal.0.try_send(StrikeConnection::Disconnect)?; + commands.remove_resource::(); + + Ok(()) +} + +pub fn clear_device_data( + mut timer: ResMut, + time: Res>, + mut commands: Commands, +) { + timer.0.tick(time.delta()); + + if timer.0.just_finished() { + commands.queue(|world: &mut World| { + let now = jiff::Timestamp::now(); + let to_remove: Vec<_> = world + .query_filtered::<(Entity, &Timestamp), With>() + .iter(world) + .filter_map(|(entity, time)| { + (time.0.duration_since(now) > SignedDuration::from_mins(5)).then_some(entity) + }) + .collect(); + + for remove in to_remove { + world.despawn(remove); + } + }); + } +} + +pub fn update_device_data( + updates: Res, + connected: Res, + mut commands: Commands, +) -> Result { + let mut entity = commands.entity(connected.0); + + while let Ok(StrikerResponse::Update(Update::Warning { timestamp, level })) = + updates.0.try_recv() + { + entity.with_related::(( + Timestamp(jiff::Timestamp::from_second(timestamp)?), + StormLevel(level), + )); + } + + Ok(()) +} + pub fn monitoring_view( mut context: ResMut, connected: Res, - q_devices: Query<&Name, With>, + q_devices: Query<(&Name, Option<&StormLevels>), With>, + q_levels: Query<(&Timestamp, &StormLevel)>, ) -> Result { context.draw(|frame| { - let [top, bottom] = - Layout::vertical([Constraint::Length(3), Constraint::Fill(1)]).areas(frame.area()); + let [top, mid, bottom] = Layout::vertical([ + Constraint::Length(3), + Constraint::Fill(1), + Constraint::Length(3), + ]) + .areas(frame.area()); + + let [left, center, right] = Layout::horizontal([ + Constraint::Fill(1), + Constraint::Fill(1), + Constraint::Fill(1), + ]) + .areas(top); - let device = q_devices.get(connected.0).unwrap(); + let (device, children) = q_devices.get(connected.0).unwrap(); - let paragraph = Paragraph::new(device.as_str()).block( + let latest = children.and_then(|c| c.last().copied().and_then(|a| q_levels.get(a).ok())); + + let name = Paragraph::new(device.as_str()).block( Block::bordered() .padding(Padding::horizontal(2)) .title("Device") @@ -55,13 +156,46 @@ pub fn monitoring_view( .border_style(Color::LightGreen), ); + let timestamp = Paragraph::new(Line::from_iter( + latest.map(|(t, _)| Span::from(format!("{}", t.0))), + )) + .block( + Block::bordered() + .padding(Padding::horizontal(2)) + .title("Timestamp") + .title_alignment(HorizontalAlignment::Center) + .border_style(Color::LightGreen), + ); + + let warn_level = Paragraph::new(Line::from_iter( + Some(Span::raw("Level: ")) + .into_iter() + .chain(latest.map(|(_, s)| Span::from(format!("{}", s.0)))), + )) + .block( + Block::bordered() + .padding(Padding::horizontal(2)) + .title("Storm Warning") + .title_alignment(HorizontalAlignment::Center) + .border_style(Color::LightGreen), + ); + let block = Block::bordered() .title("Details") .padding(Padding::new(2, 2, 1, 1)) .border_style(Color::LightGreen); - frame.render_widget(paragraph, top); - frame.render_widget(block, bottom); + let help = Paragraph::new("Keys: 'q'/ESC Quit, BACKSPACE Return to Device select").block( + Block::bordered() + .padding(Padding::horizontal(2)) + .border_style(Color::LightGreen), + ); + + frame.render_widget(name, left); + frame.render_widget(timestamp, center); + frame.render_widget(warn_level, right); + frame.render_widget(block, mid); + frame.render_widget(help, bottom); })?; Ok(()) -- 2.51.2