Bevy+Ratutui powered Monitoring of Pico-Strike devices
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Strike connection and update loop

+363 -96
+9
.cargo/config.toml
··· 1 + [target.x86_64-unknown-linux-gnu] 2 + linker = "clang" 3 + rustflags = ["-C", "link-arg=-fuse-ld=/usr/bin/mold"] 4 + 5 + [profile.dev.build-override] 6 + opt-level = 1 7 + 8 + [env] 9 + RUST_LOG = "info"
+113 -69
Cargo.lock
··· 106 106 dependencies = [ 107 107 "proc-macro2", 108 108 "quote", 109 - "syn 2.0.112", 109 + "syn 2.0.114", 110 110 ] 111 111 112 112 [[package]] ··· 354 354 "bevy_macro_utils", 355 355 "proc-macro2", 356 356 "quote", 357 - "syn 2.0.112", 357 + "syn 2.0.114", 358 358 ] 359 359 360 360 [[package]] ··· 407 407 dependencies = [ 408 408 "bevy_macro_utils", 409 409 "quote", 410 - "syn 2.0.112", 410 + "syn 2.0.114", 411 411 ] 412 412 413 413 [[package]] ··· 464 464 "bevy_macro_utils", 465 465 "proc-macro2", 466 466 "quote", 467 - "syn 2.0.112", 467 + "syn 2.0.114", 468 468 ] 469 469 470 470 [[package]] ··· 582 582 "parking_lot", 583 583 "proc-macro2", 584 584 "quote", 585 - "syn 2.0.112", 585 + "syn 2.0.114", 586 586 "toml_edit", 587 587 ] 588 588 ··· 735 735 "indexmap", 736 736 "proc-macro2", 737 737 "quote", 738 - "syn 2.0.112", 738 + "syn 2.0.114", 739 739 "uuid", 740 740 ] 741 741 ··· 763 763 dependencies = [ 764 764 "bevy_macro_utils", 765 765 "quote", 766 - "syn 2.0.112", 766 + "syn 2.0.114", 767 767 ] 768 768 769 769 [[package]] ··· 878 878 879 879 [[package]] 880 880 name = "blake3" 881 - version = "1.8.2" 881 + version = "1.8.3" 882 882 source = "registry+https://github.com/rust-lang/crates.io-index" 883 - checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" 883 + checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" 884 884 dependencies = [ 885 885 "arrayref", 886 886 "arrayvec", 887 887 "cc", 888 888 "cfg-if", 889 889 "constant_time_eq", 890 + "cpufeatures", 890 891 ] 891 892 892 893 [[package]] ··· 943 944 dependencies = [ 944 945 "proc-macro2", 945 946 "quote", 946 - "syn 2.0.112", 947 + "syn 2.0.114", 947 948 ] 948 949 949 950 [[package]] ··· 975 976 976 977 [[package]] 977 978 name = "cc" 978 - version = "1.2.51" 979 + version = "1.2.52" 979 980 source = "registry+https://github.com/rust-lang/crates.io-index" 980 - checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" 981 + checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" 981 982 dependencies = [ 982 983 "find-msvc-tools", 983 984 "jobserver", ··· 1106 1107 1107 1108 [[package]] 1108 1109 name = "constant_time_eq" 1109 - version = "0.3.1" 1110 + version = "0.4.2" 1110 1111 source = "registry+https://github.com/rust-lang/crates.io-index" 1111 - checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" 1112 + checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" 1112 1113 1113 1114 [[package]] 1114 1115 name = "constgebra" ··· 1243 1244 1244 1245 [[package]] 1245 1246 name = "darling" 1246 - version = "0.20.11" 1247 + version = "0.23.0" 1247 1248 source = "registry+https://github.com/rust-lang/crates.io-index" 1248 - checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" 1249 + checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" 1249 1250 dependencies = [ 1250 1251 "darling_core", 1251 1252 "darling_macro", ··· 1253 1254 1254 1255 [[package]] 1255 1256 name = "darling_core" 1256 - version = "0.20.11" 1257 + version = "0.23.0" 1257 1258 source = "registry+https://github.com/rust-lang/crates.io-index" 1258 - checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" 1259 + checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" 1259 1260 dependencies = [ 1260 - "fnv", 1261 1261 "ident_case", 1262 1262 "proc-macro2", 1263 1263 "quote", 1264 1264 "strsim", 1265 - "syn 2.0.112", 1265 + "syn 2.0.114", 1266 1266 ] 1267 1267 1268 1268 [[package]] 1269 1269 name = "darling_macro" 1270 - version = "0.20.11" 1270 + version = "0.23.0" 1271 1271 source = "registry+https://github.com/rust-lang/crates.io-index" 1272 - checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" 1272 + checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" 1273 1273 dependencies = [ 1274 1274 "darling_core", 1275 1275 "quote", 1276 - "syn 2.0.112", 1276 + "syn 2.0.114", 1277 1277 ] 1278 1278 1279 1279 [[package]] ··· 1310 1310 "proc-macro2", 1311 1311 "quote", 1312 1312 "rustc_version", 1313 - "syn 2.0.112", 1313 + "syn 2.0.114", 1314 1314 "unicode-xid", 1315 1315 ] 1316 1316 ··· 1409 1409 dependencies = [ 1410 1410 "proc-macro2", 1411 1411 "quote", 1412 - "syn 2.0.112", 1412 + "syn 2.0.114", 1413 1413 ] 1414 1414 1415 1415 [[package]] ··· 1508 1508 1509 1509 [[package]] 1510 1510 name = "find-msvc-tools" 1511 - version = "0.1.6" 1511 + version = "0.1.7" 1512 1512 source = "registry+https://github.com/rust-lang/crates.io-index" 1513 - checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" 1513 + checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" 1514 1514 1515 1515 [[package]] 1516 1516 name = "finl_unicode" ··· 1651 1651 1652 1652 [[package]] 1653 1653 name = "glam" 1654 - version = "0.30.9" 1654 + version = "0.30.10" 1655 1655 source = "registry+https://github.com/rust-lang/crates.io-index" 1656 - checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46" 1656 + checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" 1657 1657 dependencies = [ 1658 1658 "bytemuck", 1659 1659 "libm", ··· 1770 1770 1771 1771 [[package]] 1772 1772 name = "indexmap" 1773 - version = "2.12.1" 1773 + version = "2.13.0" 1774 1774 source = "registry+https://github.com/rust-lang/crates.io-index" 1775 - checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" 1775 + checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" 1776 1776 dependencies = [ 1777 1777 "equivalent", 1778 1778 "hashbrown", ··· 1789 1789 1790 1790 [[package]] 1791 1791 name = "instability" 1792 - version = "0.3.10" 1792 + version = "0.3.11" 1793 1793 source = "registry+https://github.com/rust-lang/crates.io-index" 1794 - checksum = "6778b0196eefee7df739db78758e5cf9b37412268bfa5650bfeed028aed20d9c" 1794 + checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" 1795 1795 dependencies = [ 1796 1796 "darling", 1797 1797 "indoc", 1798 1798 "proc-macro2", 1799 1799 "quote", 1800 - "syn 2.0.112", 1800 + "syn 2.0.114", 1801 1801 ] 1802 1802 1803 1803 [[package]] ··· 1834 1834 checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" 1835 1835 1836 1836 [[package]] 1837 + name = "jiff" 1838 + version = "0.2.18" 1839 + source = "registry+https://github.com/rust-lang/crates.io-index" 1840 + checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" 1841 + dependencies = [ 1842 + "jiff-static", 1843 + "jiff-tzdb-platform", 1844 + "log", 1845 + "portable-atomic", 1846 + "portable-atomic-util", 1847 + "serde_core", 1848 + "windows-sys 0.61.2", 1849 + ] 1850 + 1851 + [[package]] 1852 + name = "jiff-static" 1853 + version = "0.2.18" 1854 + source = "registry+https://github.com/rust-lang/crates.io-index" 1855 + checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" 1856 + dependencies = [ 1857 + "proc-macro2", 1858 + "quote", 1859 + "syn 2.0.114", 1860 + ] 1861 + 1862 + [[package]] 1863 + name = "jiff-tzdb" 1864 + version = "0.1.5" 1865 + source = "registry+https://github.com/rust-lang/crates.io-index" 1866 + checksum = "68971ebff725b9e2ca27a601c5eb38a4c5d64422c4cbab0c535f248087eda5c2" 1867 + 1868 + [[package]] 1869 + name = "jiff-tzdb-platform" 1870 + version = "0.1.3" 1871 + source = "registry+https://github.com/rust-lang/crates.io-index" 1872 + checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" 1873 + dependencies = [ 1874 + "jiff-tzdb", 1875 + ] 1876 + 1877 + [[package]] 1837 1878 name = "jni" 1838 1879 version = "0.21.1" 1839 1880 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1900 1941 1901 1942 [[package]] 1902 1943 name = "libc" 1903 - version = "0.2.178" 1944 + version = "0.2.180" 1904 1945 source = "registry+https://github.com/rust-lang/crates.io-index" 1905 - checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" 1946 + checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" 1906 1947 1907 1948 [[package]] 1908 1949 name = "libm" ··· 1961 2002 1962 2003 [[package]] 1963 2004 name = "lru" 1964 - version = "0.16.2" 2005 + version = "0.16.3" 1965 2006 source = "registry+https://github.com/rust-lang/crates.io-index" 1966 - checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" 2007 + checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" 1967 2008 dependencies = [ 1968 2009 "hashbrown", 1969 2010 ] ··· 2138 2179 dependencies = [ 2139 2180 "proc-macro2", 2140 2181 "quote", 2141 - "syn 2.0.112", 2182 + "syn 2.0.114", 2142 2183 ] 2143 2184 2144 2185 [[package]] ··· 2170 2211 "proc-macro-crate", 2171 2212 "proc-macro2", 2172 2213 "quote", 2173 - "syn 2.0.112", 2214 + "syn 2.0.114", 2174 2215 ] 2175 2216 2176 2217 [[package]] ··· 2258 2299 2259 2300 [[package]] 2260 2301 name = "pest" 2261 - version = "2.8.4" 2302 + version = "2.8.5" 2262 2303 source = "registry+https://github.com/rust-lang/crates.io-index" 2263 - checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" 2304 + checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" 2264 2305 dependencies = [ 2265 2306 "memchr", 2266 2307 "ucd-trie", ··· 2268 2309 2269 2310 [[package]] 2270 2311 name = "pest_derive" 2271 - version = "2.8.4" 2312 + version = "2.8.5" 2272 2313 source = "registry+https://github.com/rust-lang/crates.io-index" 2273 - checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" 2314 + checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" 2274 2315 dependencies = [ 2275 2316 "pest", 2276 2317 "pest_generator", ··· 2278 2319 2279 2320 [[package]] 2280 2321 name = "pest_generator" 2281 - version = "2.8.4" 2322 + version = "2.8.5" 2282 2323 source = "registry+https://github.com/rust-lang/crates.io-index" 2283 - checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" 2324 + checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" 2284 2325 dependencies = [ 2285 2326 "pest", 2286 2327 "pest_meta", 2287 2328 "proc-macro2", 2288 2329 "quote", 2289 - "syn 2.0.112", 2330 + "syn 2.0.114", 2290 2331 ] 2291 2332 2292 2333 [[package]] 2293 2334 name = "pest_meta" 2294 - version = "2.8.4" 2335 + version = "2.8.5" 2295 2336 source = "registry+https://github.com/rust-lang/crates.io-index" 2296 - checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" 2337 + checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" 2297 2338 dependencies = [ 2298 2339 "pest", 2299 2340 "sha2", ··· 2339 2380 "phf_shared", 2340 2381 "proc-macro2", 2341 2382 "quote", 2342 - "syn 2.0.112", 2383 + "syn 2.0.114", 2343 2384 ] 2344 2385 2345 2386 [[package]] ··· 2368 2409 dependencies = [ 2369 2410 "proc-macro2", 2370 2411 "quote", 2371 - "syn 2.0.112", 2412 + "syn 2.0.114", 2372 2413 ] 2373 2414 2374 2415 [[package]] ··· 2455 2496 2456 2497 [[package]] 2457 2498 name = "proc-macro2" 2458 - version = "1.0.104" 2499 + version = "1.0.105" 2459 2500 source = "registry+https://github.com/rust-lang/crates.io-index" 2460 - checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" 2501 + checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" 2461 2502 dependencies = [ 2462 2503 "unicode-ident", 2463 2504 ] ··· 2473 2514 2474 2515 [[package]] 2475 2516 name = "quote" 2476 - version = "1.0.42" 2517 + version = "1.0.43" 2477 2518 source = "registry+https://github.com/rust-lang/crates.io-index" 2478 - checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 2519 + checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" 2479 2520 dependencies = [ 2480 2521 "proc-macro2", 2481 2522 ] ··· 2806 2847 dependencies = [ 2807 2848 "proc-macro2", 2808 2849 "quote", 2809 - "syn 2.0.112", 2850 + "syn 2.0.114", 2810 2851 ] 2811 2852 2812 2853 [[package]] ··· 2953 2994 "color-eyre", 2954 2995 "crossterm", 2955 2996 "futures-concurrency", 2997 + "jiff", 2998 + "postcard", 2956 2999 "rapidhash", 2957 3000 "ratatui", 2958 3001 "sachy-mdns", 3002 + "serde", 2959 3003 "socket2", 2960 3004 "striker-proto", 2961 3005 ] ··· 2992 3036 "heck", 2993 3037 "proc-macro2", 2994 3038 "quote", 2995 - "syn 2.0.112", 3039 + "syn 2.0.114", 2996 3040 ] 2997 3041 2998 3042 [[package]] ··· 3014 3058 3015 3059 [[package]] 3016 3060 name = "syn" 3017 - version = "2.0.112" 3061 + version = "2.0.114" 3018 3062 source = "registry+https://github.com/rust-lang/crates.io-index" 3019 - checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4" 3063 + checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" 3020 3064 dependencies = [ 3021 3065 "proc-macro2", 3022 3066 "quote", ··· 3112 3156 dependencies = [ 3113 3157 "proc-macro2", 3114 3158 "quote", 3115 - "syn 2.0.112", 3159 + "syn 2.0.114", 3116 3160 ] 3117 3161 3118 3162 [[package]] ··· 3123 3167 dependencies = [ 3124 3168 "proc-macro2", 3125 3169 "quote", 3126 - "syn 2.0.112", 3170 + "syn 2.0.114", 3127 3171 ] 3128 3172 3129 3173 [[package]] ··· 3220 3264 dependencies = [ 3221 3265 "proc-macro2", 3222 3266 "quote", 3223 - "syn 2.0.112", 3267 + "syn 2.0.114", 3224 3268 ] 3225 3269 3226 3270 [[package]] ··· 3387 3431 dependencies = [ 3388 3432 "proc-macro2", 3389 3433 "quote", 3390 - "syn 2.0.112", 3434 + "syn 2.0.114", 3391 3435 ] 3392 3436 3393 3437 [[package]] ··· 3475 3519 "bumpalo", 3476 3520 "proc-macro2", 3477 3521 "quote", 3478 - "syn 2.0.112", 3522 + "syn 2.0.114", 3479 3523 "wasm-bindgen-shared", 3480 3524 ] 3481 3525 ··· 3807 3851 3808 3852 [[package]] 3809 3853 name = "zerocopy" 3810 - version = "0.8.31" 3854 + version = "0.8.33" 3811 3855 source = "registry+https://github.com/rust-lang/crates.io-index" 3812 - checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" 3856 + checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" 3813 3857 dependencies = [ 3814 3858 "zerocopy-derive", 3815 3859 ] 3816 3860 3817 3861 [[package]] 3818 3862 name = "zerocopy-derive" 3819 - version = "0.8.31" 3863 + version = "0.8.33" 3820 3864 source = "registry+https://github.com/rust-lang/crates.io-index" 3821 - checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" 3865 + checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" 3822 3866 dependencies = [ 3823 3867 "proc-macro2", 3824 3868 "quote", 3825 - "syn 2.0.112", 3869 + "syn 2.0.114", 3826 3870 ]
+9
Cargo.toml
··· 39 39 sachy-mdns = { git = "https://tangled.org/sachy.dev/sachy-embed-core", package = "sachy-mdns", features = [ 40 40 "client", 41 41 ] } 42 + serde.workspace = true 43 + postcard.workspace = true 44 + jiff = "0.2.18" 42 45 43 46 [lib] 44 47 path = "src/lib.rs" ··· 46 49 [[bin]] 47 50 path = "src/main.rs" 48 51 name = "striker" 52 + 53 + [profile.release] 54 + codegen-units = 1 55 + lto = true 56 + strip = true 57 + panic = "abort"
+5 -4
src/device.rs
··· 11 11 system::{Commands, Res, ResMut}, 12 12 world::DeferredWorld, 13 13 }, 14 + prelude::Deref, 14 15 }; 15 16 use rapidhash::RapidHashSet; 16 17 ··· 47 48 48 49 #[derive(Debug, Component)] 49 50 #[component(immutable)] 50 - pub struct Timestamp(pub i64); 51 + pub struct Timestamp(pub jiff::Timestamp); 51 52 52 53 #[derive(Debug, Component)] 53 54 #[relationship(relationship_target = StormLevels)] 54 - pub struct Source(pub Entity); 55 + pub struct StormSource(pub Entity); 55 56 56 - #[derive(Component, Debug)] 57 - #[relationship_target(relationship = Source)] 57 + #[derive(Component, Debug, Deref)] 58 + #[relationship_target(relationship = StormSource)] 58 59 pub struct StormLevels { 59 60 levels: Vec<Entity>, 60 61 }
+58 -2
src/net.rs
··· 1 1 use core::net::IpAddr; 2 2 use std::{ 3 - net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}, 3 + io::Read, 4 + net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpStream, UdpSocket}, 4 5 time::Duration, 5 6 }; 6 7 ··· 25 26 26 27 #[derive(Debug, Resource)] 27 28 pub struct DiscoverResponse(pub Receiver<InstanceDetails>); 29 + 30 + #[derive(Debug, Resource)] 31 + pub struct StrikeUpdates(pub Receiver<striker_proto::StrikerResponse>); 32 + 33 + #[derive(Debug, Resource)] 34 + pub struct StrikeConnect(pub Sender<StrikeConnection>); 35 + 36 + pub enum StrikeConnection { 37 + Connect(SocketAddr), 38 + Disconnect, 39 + } 28 40 29 41 pub struct InstanceDetails { 30 42 pub host: String, ··· 121 133 Ok(()) 122 134 } 123 135 136 + pub fn setup_strike_connection(mut commands: Commands) { 137 + let io = IoTaskPool::get(); 138 + 139 + let (signal_tx, signal_rx) = async_channel::bounded(1); 140 + let (resp_tx, resp_rx) = async_channel::bounded(64); 141 + 142 + io.spawn(async move { 143 + let mut buf = vec![0u8; 4096]; 144 + 145 + while let Ok(StrikeConnection::Connect(addr)) = signal_rx.recv().await { 146 + let net_fut = async { 147 + loop { 148 + let Ok(stream) = Async::<TcpStream>::connect(addr).await else { 149 + Timer::after(Duration::from_secs(1)).await; 150 + continue; 151 + }; 152 + 153 + while let Ok(read) = stream.read_with(|mut a| a.read(&mut buf)).await { 154 + resp_tx 155 + .send(postcard::from_bytes(&buf[..read]).unwrap()) 156 + .await 157 + .ok(); 158 + } 159 + } 160 + }; 161 + 162 + let cancel_fut = async { 163 + while signal_rx 164 + .recv() 165 + .await 166 + .is_ok_and(|strike| !matches!(strike, StrikeConnection::Disconnect)) 167 + { 168 + } 169 + }; 170 + 171 + (net_fut, cancel_fut).race().await; 172 + } 173 + }) 174 + .detach(); 175 + 176 + commands.insert_resource(StrikeConnect(signal_tx)); 177 + commands.insert_resource(StrikeUpdates(resp_rx)); 178 + } 179 + 124 180 pub struct NetPlugin; 125 181 126 182 impl Plugin for NetPlugin { 127 183 fn build(&self, app: &mut bevy::app::App) { 128 - app.add_systems(Startup, setup_mdns_task); 184 + app.add_systems(Startup, (setup_mdns_task, setup_strike_connection)); 129 185 } 130 186 }
+15 -9
src/views.rs
··· 35 35 36 36 impl Plugin for MonitoringViewPlugin { 37 37 fn build(&self, app: &mut bevy::app::App) { 38 - app.add_systems( 39 - PostUpdate, 40 - ( 41 - monitoring::monitoring_message_handler, 42 - monitoring::monitoring_view, 43 - ) 44 - .chain() 45 - .run_if(in_state(AppState::Monitoring)), 46 - ); 38 + app.add_systems(OnEnter(AppState::Monitoring), monitoring::enter_monitoring) 39 + .add_systems(OnExit(AppState::Monitoring), monitoring::exit_monitoring) 40 + .add_systems( 41 + PostUpdate, 42 + ( 43 + monitoring::monitoring_message_handler, 44 + ( 45 + monitoring::update_device_data, 46 + monitoring::clear_device_data, 47 + ), 48 + monitoring::monitoring_view, 49 + ) 50 + .chain() 51 + .run_if(in_state(AppState::Monitoring)), 52 + ); 47 53 } 48 54 }
+12 -4
src/views/home.rs
··· 15 15 use bevy_ratatui::RatatuiContext; 16 16 use ratatui::{ 17 17 layout::{Constraint, HorizontalAlignment, Layout}, 18 - style::Color, 18 + style::{Color, Style}, 19 19 widgets::{Block, List, ListDirection, ListItem, ListState, Padding, Paragraph}, 20 20 }; 21 21 ··· 96 96 q_devices: Query<(&Name, &DeviceSocket), With<Device>>, 97 97 ) -> Result { 98 98 context.draw(|frame| { 99 - let [top, mid, bottom] = 100 - Layout::vertical([Constraint::Length(3), Constraint::Fill(1), Constraint::Length(3)]).areas(frame.area()); 99 + let [top, mid, bottom] = Layout::vertical([ 100 + Constraint::Length(3), 101 + Constraint::Fill(1), 102 + Constraint::Length(3), 103 + ]) 104 + .areas(frame.area()); 101 105 102 106 let searching = if is_searching.searching.is_some() { 103 107 "Searching..." ··· 119 123 )) 120 124 }); 121 125 122 - let help = Paragraph::new("Keys: 'q'/ESC Quit, 's' Toggle Search, UP/DOWN Choose Device(s), ENTER/SPACE Connect to Device").block( 126 + let help_text = "Keys: 'q'/ESC Quit, 's' Toggle Search, UP/DOWN Choose Device(s), ENTER/SPACE Connect to Device"; 127 + 128 + let help = Paragraph::new(help_text).block( 123 129 Block::bordered() 124 130 .padding(Padding::horizontal(2)) 125 131 .border_style(Color::LightBlue), ··· 127 133 128 134 let list = List::new(items) 129 135 .highlight_symbol(">> ") 136 + .highlight_spacing(ratatui::widgets::HighlightSpacing::Always) 137 + .highlight_style(Style::new().bg(Color::DarkGray)) 130 138 .direction(ListDirection::TopToBottom) 131 139 .block( 132 140 Block::bordered()
+142 -8
src/views/monitoring.rs
··· 1 + use core::net::SocketAddr; 2 + 1 3 use bevy::{ 2 4 ecs::{ 5 + entity::Entity, 3 6 error::Result, 4 7 message::MessageReader, 5 8 name::Name, 6 9 query::With, 10 + resource::Resource, 7 11 system::{Commands, Query, Res, ResMut}, 8 12 world::World, 9 13 }, 10 14 state::state::NextState, 15 + time::{Real, Time, Timer}, 11 16 }; 12 17 use bevy_ratatui::RatatuiContext; 18 + use jiff::SignedDuration; 13 19 use ratatui::{ 14 20 layout::{Constraint, HorizontalAlignment, Layout}, 15 21 style::Color, 22 + text::{Line, Span}, 16 23 widgets::{Block, Padding, Paragraph}, 17 24 }; 25 + use striker_proto::{StrikerResponse, Update}; 18 26 19 27 use crate::{ 20 - device::{ConnectedDevice, Device}, 28 + device::{ 29 + ConnectedDevice, Device, DeviceSocket, StormLevel, StormLevels, StormSource, Timestamp, 30 + }, 21 31 messages::StrikeMessage, 32 + net::{StrikeConnect, StrikeConnection, StrikeUpdates}, 22 33 state::AppState, 23 34 }; 35 + 36 + #[derive(Debug, Resource)] 37 + pub struct DataClear(pub Timer); 24 38 25 39 pub fn monitoring_message_handler( 26 40 mut strike_reader: MessageReader<StrikeMessage>, ··· 36 50 } 37 51 } 38 52 53 + pub fn enter_monitoring( 54 + connected: Res<ConnectedDevice>, 55 + signal: Res<StrikeConnect>, 56 + q_devices: Query<&DeviceSocket, With<Device>>, 57 + mut commands: Commands, 58 + ) -> Result { 59 + let addr = q_devices.get(connected.0)?; 60 + 61 + signal 62 + .0 63 + .try_send(StrikeConnection::Connect(SocketAddr::new( 64 + addr.ip, addr.port, 65 + )))?; 66 + 67 + commands.insert_resource(DataClear(Timer::from_seconds( 68 + 60.0 * 1.0, 69 + bevy::time::TimerMode::Repeating, 70 + ))); 71 + 72 + Ok(()) 73 + } 74 + 75 + pub fn exit_monitoring(signal: Res<StrikeConnect>, mut commands: Commands) -> Result { 76 + signal.0.try_send(StrikeConnection::Disconnect)?; 77 + commands.remove_resource::<DataClear>(); 78 + 79 + Ok(()) 80 + } 81 + 82 + pub fn clear_device_data( 83 + mut timer: ResMut<DataClear>, 84 + time: Res<Time<Real>>, 85 + mut commands: Commands, 86 + ) { 87 + timer.0.tick(time.delta()); 88 + 89 + if timer.0.just_finished() { 90 + commands.queue(|world: &mut World| { 91 + let now = jiff::Timestamp::now(); 92 + let to_remove: Vec<_> = world 93 + .query_filtered::<(Entity, &Timestamp), With<StormLevel>>() 94 + .iter(world) 95 + .filter_map(|(entity, time)| { 96 + (time.0.duration_since(now) > SignedDuration::from_mins(5)).then_some(entity) 97 + }) 98 + .collect(); 99 + 100 + for remove in to_remove { 101 + world.despawn(remove); 102 + } 103 + }); 104 + } 105 + } 106 + 107 + pub fn update_device_data( 108 + updates: Res<StrikeUpdates>, 109 + connected: Res<ConnectedDevice>, 110 + mut commands: Commands, 111 + ) -> Result { 112 + let mut entity = commands.entity(connected.0); 113 + 114 + while let Ok(StrikerResponse::Update(Update::Warning { timestamp, level })) = 115 + updates.0.try_recv() 116 + { 117 + entity.with_related::<StormSource>(( 118 + Timestamp(jiff::Timestamp::from_second(timestamp)?), 119 + StormLevel(level), 120 + )); 121 + } 122 + 123 + Ok(()) 124 + } 125 + 39 126 pub fn monitoring_view( 40 127 mut context: ResMut<RatatuiContext>, 41 128 connected: Res<ConnectedDevice>, 42 - q_devices: Query<&Name, With<Device>>, 129 + q_devices: Query<(&Name, Option<&StormLevels>), With<Device>>, 130 + q_levels: Query<(&Timestamp, &StormLevel)>, 43 131 ) -> Result { 44 132 context.draw(|frame| { 45 - let [top, bottom] = 46 - Layout::vertical([Constraint::Length(3), Constraint::Fill(1)]).areas(frame.area()); 133 + let [top, mid, bottom] = Layout::vertical([ 134 + Constraint::Length(3), 135 + Constraint::Fill(1), 136 + Constraint::Length(3), 137 + ]) 138 + .areas(frame.area()); 47 139 48 - let device = q_devices.get(connected.0).unwrap(); 140 + let [left, center, right] = Layout::horizontal([ 141 + Constraint::Fill(1), 142 + Constraint::Fill(1), 143 + Constraint::Fill(1), 144 + ]) 145 + .areas(top); 146 + 147 + let (device, children) = q_devices.get(connected.0).unwrap(); 148 + 149 + let latest = children.and_then(|c| c.last().copied().and_then(|a| q_levels.get(a).ok())); 49 150 50 - let paragraph = Paragraph::new(device.as_str()).block( 151 + let name = Paragraph::new(device.as_str()).block( 51 152 Block::bordered() 52 153 .padding(Padding::horizontal(2)) 53 154 .title("Device") ··· 55 156 .border_style(Color::LightGreen), 56 157 ); 57 158 159 + let timestamp = Paragraph::new(Line::from_iter( 160 + latest.map(|(t, _)| Span::from(format!("{}", t.0))), 161 + )) 162 + .block( 163 + Block::bordered() 164 + .padding(Padding::horizontal(2)) 165 + .title("Timestamp") 166 + .title_alignment(HorizontalAlignment::Center) 167 + .border_style(Color::LightGreen), 168 + ); 169 + 170 + let warn_level = Paragraph::new(Line::from_iter( 171 + Some(Span::raw("Level: ")) 172 + .into_iter() 173 + .chain(latest.map(|(_, s)| Span::from(format!("{}", s.0)))), 174 + )) 175 + .block( 176 + Block::bordered() 177 + .padding(Padding::horizontal(2)) 178 + .title("Storm Warning") 179 + .title_alignment(HorizontalAlignment::Center) 180 + .border_style(Color::LightGreen), 181 + ); 182 + 58 183 let block = Block::bordered() 59 184 .title("Details") 60 185 .padding(Padding::new(2, 2, 1, 1)) 61 186 .border_style(Color::LightGreen); 62 187 63 - frame.render_widget(paragraph, top); 64 - frame.render_widget(block, bottom); 188 + let help = Paragraph::new("Keys: 'q'/ESC Quit, BACKSPACE Return to Device select").block( 189 + Block::bordered() 190 + .padding(Padding::horizontal(2)) 191 + .border_style(Color::LightGreen), 192 + ); 193 + 194 + frame.render_widget(name, left); 195 + frame.render_widget(timestamp, center); 196 + frame.render_widget(warn_level, right); 197 + frame.render_widget(block, mid); 198 + frame.render_widget(help, bottom); 65 199 })?; 66 200 67 201 Ok(())