Bevy+Ratutui powered Monitoring of Pico-Strike devices

Organise into widgets

+100 -41
+30
src/device.rs
··· 14 prelude::Deref, 15 }; 16 use rapidhash::RapidHashSet; 17 18 use crate::net::DiscoverResponse; 19 ··· 37 pub blip_size: usize, 38 pub max_duty: u16, 39 pub duty: u16, 40 } 41 42 fn on_remove_device(mut world: DeferredWorld, context: HookContext) {
··· 14 prelude::Deref, 15 }; 16 use rapidhash::RapidHashSet; 17 + use ratatui::{ 18 + style::{Color, Stylize}, 19 + text::{Line, Span, Text, ToSpan}, 20 + widgets::{Block, Padding, Paragraph, Widget}, 21 + }; 22 23 use crate::net::DiscoverResponse; 24 ··· 42 pub blip_size: usize, 43 pub max_duty: u16, 44 pub duty: u16, 45 + } 46 + 47 + impl Widget for DeviceDetector { 48 + fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) 49 + where 50 + Self: Sized, 51 + { 52 + let detector_lines = [ 53 + Line::from_iter([Span::raw("Threshold: "), self.blip_threshold.to_span()]), 54 + Line::from_iter([Span::raw("Blip Size: "), self.blip_size.to_span()]), 55 + Line::from_iter([Span::raw("Max Duty: "), self.max_duty.to_span()]), 56 + Line::from_iter([Span::raw("Current Duty: "), self.duty.to_span()]), 57 + ]; 58 + 59 + let detector_info = Paragraph::new(Text::from_iter(detector_lines)) 60 + .block( 61 + Block::bordered() 62 + .title("Settings") 63 + .padding(Padding::horizontal(1)) 64 + .border_style(Color::LightGreen), 65 + ) 66 + .white(); 67 + 68 + detector_info.render(area, buf); 69 + } 70 } 71 72 fn on_remove_device(mut world: DeferredWorld, context: HookContext) {
+70 -41
src/views/monitoring.rs
··· 20 layout::{Constraint, HorizontalAlignment, Layout}, 21 style::{Color, Stylize}, 22 text::{Line, Span, Text, ToSpan}, 23 - widgets::{Axis, Block, Chart, Dataset, Padding, Paragraph}, 24 }; 25 use striker_proto::{Response, StrikerRequest, StrikerResponse, Update}; 26 ··· 206 207 let timestamp = latest_signal.map(|(t, _, _)| t); 208 209 let lines = [ 210 - Line::from(device.to_span()), 211 - Line::from_iter([Span::raw("Status: "), connected.connection_state.to_span()]), 212 ] 213 .into_iter() 214 - .chain(timestamp.map(|t| Line::from(t.0.to_span()))) 215 .chain( 216 - latest_level.map(|level| Line::from_iter([Span::raw("Level: "), level.0.to_span()])), 217 ); 218 219 let info = Paragraph::new(Text::from_iter(lines)) 220 .block( 221 Block::bordered() 222 - .padding(Padding::horizontal(2)) 223 .title("Device") 224 .title_alignment(HorizontalAlignment::Center) 225 .border_style(Color::LightGreen), 226 ) 227 .white(); 228 229 - let (data, detected) = latest_signal 230 .map(|(_, samples, signals)| { 231 ( 232 samples ··· 298 ) 299 .block(block); 300 301 - let help = Paragraph::new("Keys: 'q'/ESC Quit, BACKSPACE Return to Device select") 302 - .block( 303 - Block::bordered() 304 - .padding(Padding::horizontal(2)) 305 - .border_style(Color::LightGreen), 306 - ) 307 - .white(); 308 - 309 - let detector = detector.copied().unwrap_or_default(); 310 - 311 - let detector_lines = [ 312 - Line::from_iter([Span::raw("Threshold: "), detector.blip_threshold.to_span()]), 313 - Line::from_iter([Span::raw("Blip Size: "), detector.blip_size.to_span()]), 314 - Line::from_iter([Span::raw("Max Duty: "), detector.max_duty.to_span()]), 315 - Line::from_iter([Span::raw("Current Duty: "), detector.duty.to_span()]), 316 - ]; 317 - 318 - let detector_info = Paragraph::new(Text::from_iter(detector_lines)) 319 - .block( 320 - Block::bordered() 321 - .title("Settings") 322 - .padding(Padding::horizontal(2)) 323 - .border_style(Color::LightGreen), 324 - ) 325 - .white(); 326 - 327 - frame.render_widget(info, device_block); 328 - frame.render_widget(chart2, chart_block); 329 - frame.render_widget(chart, chart_block); 330 - frame.render_widget(detector_info, detector_block); 331 - frame.render_widget(help, bottom); 332 - })?; 333 - 334 - Ok(()) 335 }
··· 20 layout::{Constraint, HorizontalAlignment, Layout}, 21 style::{Color, Stylize}, 22 text::{Line, Span, Text, ToSpan}, 23 + widgets::{Axis, Block, Chart, Dataset, Padding, Paragraph, Widget}, 24 }; 25 use striker_proto::{Response, StrikerRequest, StrikerResponse, Update}; 26 ··· 206 207 let timestamp = latest_signal.map(|(t, _, _)| t); 208 209 + let info = DeviceInfo { 210 + name: device, 211 + connected: &connected, 212 + timestamp, 213 + level: latest_level, 214 + }; 215 + 216 + let signal_chart = SignalChart { 217 + signal: latest_signal, 218 + }; 219 + 220 + let help = Paragraph::new("Keys: 'q'/ESC Quit, BACKSPACE Return to Device select") 221 + .block( 222 + Block::bordered() 223 + .padding(Padding::horizontal(2)) 224 + .border_style(Color::LightGreen), 225 + ) 226 + .white(); 227 + 228 + let detector = detector.copied().unwrap_or_default(); 229 + 230 + frame.render_widget(info, device_block); 231 + frame.render_widget(signal_chart, chart_block); 232 + frame.render_widget(detector, detector_block); 233 + frame.render_widget(help, bottom); 234 + })?; 235 + 236 + Ok(()) 237 + } 238 + 239 + struct DeviceInfo<'a> { 240 + name: &'a Name, 241 + connected: &'a ConnectedDevice, 242 + timestamp: Option<&'a Timestamp>, 243 + level: Option<&'a StormLevel>, 244 + } 245 + 246 + impl Widget for DeviceInfo<'_> { 247 + fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) 248 + where 249 + Self: Sized, 250 + { 251 let lines = [ 252 + Line::from(self.name.to_span()), 253 + Line::from_iter([ 254 + Span::raw("Status: "), 255 + self.connected.connection_state.to_span(), 256 + ]), 257 ] 258 .into_iter() 259 + .chain(self.timestamp.map(|t| Line::from(t.0.to_span()))) 260 .chain( 261 + self.level 262 + .map(|level| Line::from_iter([Span::raw("Level: "), level.0.to_span()])), 263 ); 264 265 let info = Paragraph::new(Text::from_iter(lines)) 266 .block( 267 Block::bordered() 268 + .padding(Padding::horizontal(1)) 269 .title("Device") 270 .title_alignment(HorizontalAlignment::Center) 271 .border_style(Color::LightGreen), 272 ) 273 .white(); 274 275 + info.render(area, buf); 276 + } 277 + } 278 + 279 + struct SignalChart<'a> { 280 + signal: Option<(&'a Timestamp, &'a StormSignal, &'a SignalPeaks)>, 281 + } 282 + 283 + impl Widget for SignalChart<'_> { 284 + fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) 285 + where 286 + Self: Sized, 287 + { 288 + let (data, detected) = self 289 + .signal 290 .map(|(_, samples, signals)| { 291 ( 292 samples ··· 358 ) 359 .block(block); 360 361 + chart2.render(area, buf); 362 + chart.render(area, buf); 363 + } 364 }