2021-11-11 14:56:37 +00:00
|
|
|
use rand::{
|
|
|
|
distributions::{Distribution, Uniform},
|
|
|
|
rngs::ThreadRng,
|
|
|
|
};
|
2023-07-16 09:11:59 +00:00
|
|
|
use ratatui::widgets::*;
|
2019-02-10 21:35:44 +00:00
|
|
|
|
2020-05-16 22:51:57 +00:00
|
|
|
const TASKS: [&str; 24] = [
|
2019-02-10 21:35:44 +00:00
|
|
|
"Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7", "Item8", "Item9", "Item10",
|
|
|
|
"Item11", "Item12", "Item13", "Item14", "Item15", "Item16", "Item17", "Item18", "Item19",
|
|
|
|
"Item20", "Item21", "Item22", "Item23", "Item24",
|
|
|
|
];
|
|
|
|
|
2020-05-16 22:51:57 +00:00
|
|
|
const LOGS: [(&str, &str); 26] = [
|
2019-02-10 21:35:44 +00:00
|
|
|
("Event1", "INFO"),
|
|
|
|
("Event2", "INFO"),
|
|
|
|
("Event3", "CRITICAL"),
|
|
|
|
("Event4", "ERROR"),
|
|
|
|
("Event5", "INFO"),
|
|
|
|
("Event6", "INFO"),
|
|
|
|
("Event7", "WARNING"),
|
|
|
|
("Event8", "INFO"),
|
|
|
|
("Event9", "INFO"),
|
|
|
|
("Event10", "INFO"),
|
|
|
|
("Event11", "CRITICAL"),
|
|
|
|
("Event12", "INFO"),
|
|
|
|
("Event13", "INFO"),
|
|
|
|
("Event14", "INFO"),
|
|
|
|
("Event15", "INFO"),
|
|
|
|
("Event16", "INFO"),
|
|
|
|
("Event17", "ERROR"),
|
|
|
|
("Event18", "ERROR"),
|
|
|
|
("Event19", "INFO"),
|
|
|
|
("Event20", "INFO"),
|
|
|
|
("Event21", "WARNING"),
|
|
|
|
("Event22", "INFO"),
|
|
|
|
("Event23", "INFO"),
|
|
|
|
("Event24", "WARNING"),
|
|
|
|
("Event25", "INFO"),
|
|
|
|
("Event26", "INFO"),
|
|
|
|
];
|
|
|
|
|
2020-05-16 22:51:57 +00:00
|
|
|
const EVENTS: [(&str, u64); 24] = [
|
2019-02-10 21:35:44 +00:00
|
|
|
("B1", 9),
|
|
|
|
("B2", 12),
|
|
|
|
("B3", 5),
|
|
|
|
("B4", 8),
|
|
|
|
("B5", 2),
|
|
|
|
("B6", 4),
|
|
|
|
("B7", 5),
|
|
|
|
("B8", 9),
|
|
|
|
("B9", 14),
|
|
|
|
("B10", 15),
|
|
|
|
("B11", 1),
|
|
|
|
("B12", 0),
|
|
|
|
("B13", 4),
|
|
|
|
("B14", 6),
|
|
|
|
("B15", 4),
|
|
|
|
("B16", 6),
|
|
|
|
("B17", 4),
|
|
|
|
("B18", 7),
|
|
|
|
("B19", 13),
|
|
|
|
("B20", 8),
|
|
|
|
("B21", 11),
|
|
|
|
("B22", 9),
|
|
|
|
("B23", 3),
|
|
|
|
("B24", 5),
|
|
|
|
];
|
|
|
|
|
2021-11-11 14:56:37 +00:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct RandomSignal {
|
|
|
|
distribution: Uniform<u64>,
|
|
|
|
rng: ThreadRng,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RandomSignal {
|
|
|
|
pub fn new(lower: u64, upper: u64) -> RandomSignal {
|
|
|
|
RandomSignal {
|
|
|
|
distribution: Uniform::new(lower, upper),
|
|
|
|
rng: rand::thread_rng(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for RandomSignal {
|
|
|
|
type Item = u64;
|
|
|
|
fn next(&mut self) -> Option<u64> {
|
|
|
|
Some(self.distribution.sample(&mut self.rng))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct SinSignal {
|
|
|
|
x: f64,
|
|
|
|
interval: f64,
|
|
|
|
period: f64,
|
|
|
|
scale: f64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SinSignal {
|
|
|
|
pub fn new(interval: f64, period: f64, scale: f64) -> SinSignal {
|
|
|
|
SinSignal {
|
|
|
|
x: 0.0,
|
|
|
|
interval,
|
|
|
|
period,
|
|
|
|
scale,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for SinSignal {
|
|
|
|
type Item = (f64, f64);
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
let point = (self.x, (self.x * 1.0 / self.period).sin() * self.scale);
|
|
|
|
self.x += self.interval;
|
|
|
|
Some(point)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct TabsState<'a> {
|
|
|
|
pub titles: Vec<&'a str>,
|
|
|
|
pub index: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> TabsState<'a> {
|
|
|
|
pub fn new(titles: Vec<&'a str>) -> TabsState {
|
|
|
|
TabsState { titles, index: 0 }
|
|
|
|
}
|
|
|
|
pub fn next(&mut self) {
|
|
|
|
self.index = (self.index + 1) % self.titles.len();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn previous(&mut self) {
|
|
|
|
if self.index > 0 {
|
|
|
|
self.index -= 1;
|
|
|
|
} else {
|
|
|
|
self.index = self.titles.len() - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct StatefulList<T> {
|
|
|
|
pub state: ListState,
|
|
|
|
pub items: Vec<T>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> StatefulList<T> {
|
|
|
|
pub fn with_items(items: Vec<T>) -> StatefulList<T> {
|
|
|
|
StatefulList {
|
|
|
|
state: ListState::default(),
|
|
|
|
items,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn next(&mut self) {
|
|
|
|
let i = match self.state.selected() {
|
|
|
|
Some(i) => {
|
|
|
|
if i >= self.items.len() - 1 {
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
i + 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => 0,
|
|
|
|
};
|
|
|
|
self.state.select(Some(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn previous(&mut self) {
|
|
|
|
let i = match self.state.selected() {
|
|
|
|
Some(i) => {
|
|
|
|
if i == 0 {
|
|
|
|
self.items.len() - 1
|
|
|
|
} else {
|
|
|
|
i - 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => 0,
|
|
|
|
};
|
|
|
|
self.state.select(Some(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-10 21:35:44 +00:00
|
|
|
pub struct Signal<S: Iterator> {
|
|
|
|
source: S,
|
|
|
|
pub points: Vec<S::Item>,
|
|
|
|
tick_rate: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<S> Signal<S>
|
|
|
|
where
|
|
|
|
S: Iterator,
|
|
|
|
{
|
|
|
|
fn on_tick(&mut self) {
|
|
|
|
for _ in 0..self.tick_rate {
|
|
|
|
self.points.remove(0);
|
|
|
|
}
|
|
|
|
self.points
|
|
|
|
.extend(self.source.by_ref().take(self.tick_rate));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Signals {
|
|
|
|
pub sin1: Signal<SinSignal>,
|
|
|
|
pub sin2: Signal<SinSignal>,
|
|
|
|
pub window: [f64; 2],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Signals {
|
|
|
|
fn on_tick(&mut self) {
|
|
|
|
self.sin1.on_tick();
|
|
|
|
self.sin2.on_tick();
|
|
|
|
self.window[0] += 1.0;
|
|
|
|
self.window[1] += 1.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Server<'a> {
|
|
|
|
pub name: &'a str,
|
|
|
|
pub location: &'a str,
|
|
|
|
pub coords: (f64, f64),
|
|
|
|
pub status: &'a str,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct App<'a> {
|
|
|
|
pub title: &'a str,
|
|
|
|
pub should_quit: bool,
|
|
|
|
pub tabs: TabsState<'a>,
|
|
|
|
pub show_chart: bool,
|
2020-04-13 23:04:07 +00:00
|
|
|
pub progress: f64,
|
2019-02-10 21:35:44 +00:00
|
|
|
pub sparkline: Signal<RandomSignal>,
|
2019-12-15 20:38:18 +00:00
|
|
|
pub tasks: StatefulList<&'a str>,
|
|
|
|
pub logs: StatefulList<(&'a str, &'a str)>,
|
2019-02-10 21:35:44 +00:00
|
|
|
pub signals: Signals,
|
|
|
|
pub barchart: Vec<(&'a str, u64)>,
|
|
|
|
pub servers: Vec<Server<'a>>,
|
2020-04-13 23:04:07 +00:00
|
|
|
pub enhanced_graphics: bool,
|
2019-02-10 21:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> App<'a> {
|
2020-04-13 23:04:07 +00:00
|
|
|
pub fn new(title: &'a str, enhanced_graphics: bool) -> App<'a> {
|
2019-02-10 21:35:44 +00:00
|
|
|
let mut rand_signal = RandomSignal::new(0, 100);
|
|
|
|
let sparkline_points = rand_signal.by_ref().take(300).collect();
|
|
|
|
let mut sin_signal = SinSignal::new(0.2, 3.0, 18.0);
|
|
|
|
let sin1_points = sin_signal.by_ref().take(100).collect();
|
|
|
|
let mut sin_signal2 = SinSignal::new(0.1, 2.0, 10.0);
|
|
|
|
let sin2_points = sin_signal2.by_ref().take(200).collect();
|
|
|
|
App {
|
|
|
|
title,
|
|
|
|
should_quit: false,
|
2021-01-01 14:29:04 +00:00
|
|
|
tabs: TabsState::new(vec!["Tab0", "Tab1", "Tab2"]),
|
2019-02-10 21:35:44 +00:00
|
|
|
show_chart: true,
|
2020-04-13 23:04:07 +00:00
|
|
|
progress: 0.0,
|
2019-02-10 21:35:44 +00:00
|
|
|
sparkline: Signal {
|
|
|
|
source: rand_signal,
|
|
|
|
points: sparkline_points,
|
|
|
|
tick_rate: 1,
|
|
|
|
},
|
2019-12-15 20:38:18 +00:00
|
|
|
tasks: StatefulList::with_items(TASKS.to_vec()),
|
|
|
|
logs: StatefulList::with_items(LOGS.to_vec()),
|
2019-02-10 21:35:44 +00:00
|
|
|
signals: Signals {
|
|
|
|
sin1: Signal {
|
|
|
|
source: sin_signal,
|
|
|
|
points: sin1_points,
|
|
|
|
tick_rate: 5,
|
|
|
|
},
|
|
|
|
sin2: Signal {
|
|
|
|
source: sin_signal2,
|
|
|
|
points: sin2_points,
|
|
|
|
tick_rate: 10,
|
|
|
|
},
|
|
|
|
window: [0.0, 20.0],
|
|
|
|
},
|
|
|
|
barchart: EVENTS.to_vec(),
|
|
|
|
servers: vec![
|
|
|
|
Server {
|
|
|
|
name: "NorthAmerica-1",
|
|
|
|
location: "New York City",
|
|
|
|
coords: (40.71, -74.00),
|
|
|
|
status: "Up",
|
|
|
|
},
|
|
|
|
Server {
|
|
|
|
name: "Europe-1",
|
|
|
|
location: "Paris",
|
|
|
|
coords: (48.85, 2.35),
|
|
|
|
status: "Failure",
|
|
|
|
},
|
|
|
|
Server {
|
|
|
|
name: "SouthAmerica-1",
|
|
|
|
location: "São Paulo",
|
|
|
|
coords: (-23.54, -46.62),
|
|
|
|
status: "Up",
|
|
|
|
},
|
|
|
|
Server {
|
|
|
|
name: "Asia-1",
|
|
|
|
location: "Singapore",
|
|
|
|
coords: (1.35, 103.86),
|
|
|
|
status: "Up",
|
|
|
|
},
|
|
|
|
],
|
2020-04-13 23:04:07 +00:00
|
|
|
enhanced_graphics,
|
2019-02-10 21:35:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn on_up(&mut self) {
|
2019-12-15 20:38:18 +00:00
|
|
|
self.tasks.previous();
|
2019-02-10 21:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn on_down(&mut self) {
|
2019-12-15 20:38:18 +00:00
|
|
|
self.tasks.next();
|
2019-02-10 21:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn on_right(&mut self) {
|
|
|
|
self.tabs.next();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn on_left(&mut self) {
|
|
|
|
self.tabs.previous();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn on_key(&mut self, c: char) {
|
|
|
|
match c {
|
|
|
|
'q' => {
|
|
|
|
self.should_quit = true;
|
|
|
|
}
|
|
|
|
't' => {
|
|
|
|
self.show_chart = !self.show_chart;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn on_tick(&mut self) {
|
|
|
|
// Update progress
|
2020-04-13 23:04:07 +00:00
|
|
|
self.progress += 0.001;
|
|
|
|
if self.progress > 1.0 {
|
|
|
|
self.progress = 0.0;
|
2019-02-10 21:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.sparkline.on_tick();
|
|
|
|
self.signals.on_tick();
|
|
|
|
|
|
|
|
let log = self.logs.items.pop().unwrap();
|
|
|
|
self.logs.items.insert(0, log);
|
|
|
|
|
|
|
|
let event = self.barchart.pop().unwrap();
|
|
|
|
self.barchart.insert(0, event);
|
|
|
|
}
|
|
|
|
}
|