GUI
This commit is contained in:
parent
2b62fe03ee
commit
1cbbe36c9e
8 changed files with 273 additions and 523 deletions
171
Cargo.lock
generated
171
Cargo.lock
generated
|
|
@ -2,59 +2,6 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "Iota"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"aes-gcm",
|
|
||||||
"async-trait",
|
|
||||||
"async-tungstenite",
|
|
||||||
"aws-lc-rs",
|
|
||||||
"axum",
|
|
||||||
"base64",
|
|
||||||
"bytes",
|
|
||||||
"cmake",
|
|
||||||
"color-eyre",
|
|
||||||
"crossterm 0.29.0",
|
|
||||||
"der",
|
|
||||||
"futures",
|
|
||||||
"futures-util",
|
|
||||||
"hex",
|
|
||||||
"hkdf",
|
|
||||||
"hmac",
|
|
||||||
"http-body-util",
|
|
||||||
"hyper",
|
|
||||||
"hyper-util",
|
|
||||||
"json",
|
|
||||||
"native-tls",
|
|
||||||
"once_cell",
|
|
||||||
"pkcs8",
|
|
||||||
"rand 0.8.5",
|
|
||||||
"rand_core 0.6.4",
|
|
||||||
"ratatui",
|
|
||||||
"reactive-rs",
|
|
||||||
"reqwest",
|
|
||||||
"rustls",
|
|
||||||
"rustls-pemfile",
|
|
||||||
"serde",
|
|
||||||
"sha1",
|
|
||||||
"sha2",
|
|
||||||
"sys-info",
|
|
||||||
"sysinfo",
|
|
||||||
"tokio",
|
|
||||||
"tokio-rustls",
|
|
||||||
"tokio-tungstenite",
|
|
||||||
"tokio-util",
|
|
||||||
"tower",
|
|
||||||
"tungstenite",
|
|
||||||
"uuid",
|
|
||||||
"walkdir",
|
|
||||||
"warp",
|
|
||||||
"x448",
|
|
||||||
"x509",
|
|
||||||
"zip",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.25.1"
|
version = "0.25.1"
|
||||||
|
|
@ -474,15 +421,6 @@ version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
|
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "convert_case"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-segmentation",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie-factory"
|
name = "cookie-factory"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
|
@ -582,24 +520,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossterm"
|
|
||||||
version = "0.29.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"crossterm_winapi",
|
|
||||||
"derive_more",
|
|
||||||
"document-features",
|
|
||||||
"mio",
|
|
||||||
"parking_lot",
|
|
||||||
"rustix 1.1.2",
|
|
||||||
"signal-hook",
|
|
||||||
"signal-hook-mio",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm_winapi"
|
name = "crossterm_winapi"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
|
|
@ -706,27 +626,6 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_more"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
|
||||||
dependencies = [
|
|
||||||
"derive_more-impl",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_more-impl"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
|
||||||
dependencies = [
|
|
||||||
"convert_case",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
|
|
@ -749,15 +648,6 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "document-features"
|
|
||||||
version = "0.2.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
|
|
||||||
dependencies = [
|
|
||||||
"litrs",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dunce"
|
name = "dunce"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
|
|
@ -1427,6 +1317,59 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iota"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"aes-gcm",
|
||||||
|
"async-trait",
|
||||||
|
"async-tungstenite",
|
||||||
|
"aws-lc-rs",
|
||||||
|
"axum",
|
||||||
|
"base64",
|
||||||
|
"bytes",
|
||||||
|
"cmake",
|
||||||
|
"color-eyre",
|
||||||
|
"crossterm",
|
||||||
|
"der",
|
||||||
|
"futures",
|
||||||
|
"futures-util",
|
||||||
|
"hex",
|
||||||
|
"hkdf",
|
||||||
|
"hmac",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"json",
|
||||||
|
"native-tls",
|
||||||
|
"once_cell",
|
||||||
|
"pkcs8",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"ratatui",
|
||||||
|
"reactive-rs",
|
||||||
|
"reqwest",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pemfile",
|
||||||
|
"serde",
|
||||||
|
"sha1",
|
||||||
|
"sha2",
|
||||||
|
"sys-info",
|
||||||
|
"sysinfo",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tokio-tungstenite",
|
||||||
|
"tokio-util",
|
||||||
|
"tower",
|
||||||
|
"tungstenite",
|
||||||
|
"uuid",
|
||||||
|
"walkdir",
|
||||||
|
"warp",
|
||||||
|
"x448",
|
||||||
|
"x509",
|
||||||
|
"zip",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.11.0"
|
version = "2.11.0"
|
||||||
|
|
@ -1539,12 +1482,6 @@ version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
|
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "litrs"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
|
|
@ -2018,7 +1955,7 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cassowary",
|
"cassowary",
|
||||||
"compact_str",
|
"compact_str",
|
||||||
"crossterm 0.28.1",
|
"crossterm",
|
||||||
"indoc",
|
"indoc",
|
||||||
"instability",
|
"instability",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,11 @@
|
||||||
use crate::APP_STATE;
|
use crate::APP_STATE;
|
||||||
use crate::SHUTDOWN;
|
use crate::SHUTDOWN;
|
||||||
use crate::gui::ratatui_interface::TERMINAL;
|
use crate::gui::tui::UNIQUE;
|
||||||
use crate::gui::widgets::betterblock::draw_block_joins;
|
|
||||||
use crate::langu::language_manager::format;
|
use crate::langu::language_manager::format;
|
||||||
use crate::langu::language_manager::from_key;
|
use crate::langu::language_manager::from_key;
|
||||||
|
|
||||||
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
||||||
use ratatui::widgets::canvas::{Canvas, Line};
|
|
||||||
use ratatui::{
|
|
||||||
layout::{Constraint, Direction, Layout},
|
|
||||||
style::Color,
|
|
||||||
widgets::{Block, Borders, List, ListItem, Paragraph},
|
|
||||||
};
|
|
||||||
use std::{thread, time::Duration};
|
use std::{thread, time::Duration};
|
||||||
use sysinfo::{RefreshKind, System};
|
use sysinfo::{RefreshKind, System};
|
||||||
|
|
||||||
|
|
@ -22,66 +16,26 @@ pub fn log_cv(cv: &CommunicationValue) {
|
||||||
} else {
|
} else {
|
||||||
log_message_trans(format!("{:?}", &cv.comm_type));
|
log_message_trans(format!("{:?}", &cv.comm_type));
|
||||||
}
|
}
|
||||||
|
tokio::spawn(async move {
|
||||||
|
*UNIQUE.write().await = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
pub fn log_message_trans(key: impl Into<String>) {
|
pub fn log_message_trans(key: impl Into<String>) {
|
||||||
APP_STATE.lock().unwrap().push_log(from_key(&key.into()));
|
APP_STATE.lock().unwrap().push_log(from_key(&key.into()));
|
||||||
|
tokio::spawn(async move {
|
||||||
|
*UNIQUE.write().await = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
pub fn log_message(msg: impl Into<String>) {
|
pub fn log_message(msg: impl Into<String>) {
|
||||||
APP_STATE.lock().unwrap().push_log(msg.into());
|
APP_STATE.lock().unwrap().push_log(msg.into());
|
||||||
}
|
tokio::spawn(async move {
|
||||||
fn smooth_data(data: &[(f64, f64)], window_size: usize) -> Vec<(f64, f64)> {
|
*UNIQUE.write().await = true;
|
||||||
if data.len() < window_size {
|
});
|
||||||
return data.to_vec();
|
|
||||||
}
|
|
||||||
let mut smoothed = Vec::with_capacity(data.len());
|
|
||||||
for i in 0..data.len() {
|
|
||||||
let start = if i + 1 >= window_size {
|
|
||||||
i + 1 - window_size
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
let window = &data[start..=i];
|
|
||||||
let avg = window.iter().map(|(_, y)| y).sum::<f64>() / window.len() as f64;
|
|
||||||
smoothed.push((data[i].0, avg));
|
|
||||||
}
|
|
||||||
smoothed
|
|
||||||
}
|
|
||||||
|
|
||||||
fn downsample_to_fit_width(data: &[(f64, f64)], width: u16) -> Vec<(f64, f64)> {
|
|
||||||
let width_usize = (width as usize) * 2;
|
|
||||||
let len = data.len();
|
|
||||||
|
|
||||||
if len >= width_usize {
|
|
||||||
// Trim data to fit
|
|
||||||
data[len - width_usize..].to_vec()
|
|
||||||
} else {
|
|
||||||
let mut result = Vec::with_capacity(width_usize);
|
|
||||||
|
|
||||||
// Define X spacing (so dummy points are properly spaced across the canvas)
|
|
||||||
let dx = 1.0;
|
|
||||||
let pad_len = width_usize - len;
|
|
||||||
|
|
||||||
// If we have real data, use its first x position to determine where to start padding
|
|
||||||
let start_x = data
|
|
||||||
.first()
|
|
||||||
.map(|(x, _)| x - (dx * pad_len as f64))
|
|
||||||
.unwrap_or(0.0);
|
|
||||||
let _ = data.first().map(|(_, y)| *y).unwrap_or(0.0);
|
|
||||||
|
|
||||||
// Fill padding with increasing x positions so they're visible
|
|
||||||
for i in 0..pad_len {
|
|
||||||
result.push((start_x + i as f64 * dx, -1 as f64));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then append the real data
|
|
||||||
result.extend_from_slice(data);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup() {
|
pub fn setup() {
|
||||||
// Start a background thread to sample metrics
|
// Start a background thread to sample metrics
|
||||||
thread::spawn(async move || {
|
tokio::spawn(async move {
|
||||||
let mut sys = System::new_with_specifics(RefreshKind::new());
|
let mut sys = System::new_with_specifics(RefreshKind::new());
|
||||||
let mut last_total_received = 0u64;
|
let mut last_total_received = 0u64;
|
||||||
let mut last_total_transmitted = 0u64;
|
let mut last_total_transmitted = 0u64;
|
||||||
|
|
@ -129,248 +83,8 @@ pub fn setup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
counter += 1.0;
|
counter += 1.0;
|
||||||
|
*UNIQUE.write().await = true;
|
||||||
thread::sleep(Duration::from_millis(1000));
|
thread::sleep(Duration::from_millis(1000));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render() {
|
|
||||||
tokio::spawn(async move {
|
|
||||||
TERMINAL
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.draw(|f| {
|
|
||||||
let sys = System::new_all();
|
|
||||||
|
|
||||||
let size = f.area();
|
|
||||||
let chunks = Layout::default()
|
|
||||||
.direction(Direction::Horizontal)
|
|
||||||
.constraints([Constraint::Percentage(40), Constraint::Percentage(60)])
|
|
||||||
.split(size);
|
|
||||||
|
|
||||||
let left = chunks[0];
|
|
||||||
let right = chunks[1];
|
|
||||||
|
|
||||||
{
|
|
||||||
let st = APP_STATE.lock().unwrap();
|
|
||||||
let items: Vec<ListItem> = st
|
|
||||||
.logs
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.map(|s| ListItem::new(s.clone()))
|
|
||||||
.collect();
|
|
||||||
let list = List::new(items)
|
|
||||||
.block(Block::default().title("Logs").borders(Borders::ALL));
|
|
||||||
f.render_widget(list, left);
|
|
||||||
}
|
|
||||||
|
|
||||||
let right_chunks = Layout::default()
|
|
||||||
.direction(Direction::Vertical)
|
|
||||||
.constraints([Constraint::Length(3), Constraint::Min(0)])
|
|
||||||
.split(right);
|
|
||||||
|
|
||||||
{
|
|
||||||
let st = APP_STATE.lock().unwrap();
|
|
||||||
let header = Paragraph::new(st.sys_info.clone()).block(
|
|
||||||
Block::default()
|
|
||||||
.title("System Info")
|
|
||||||
.borders(Borders::TOP.union(Borders::LEFT).union(Borders::RIGHT)),
|
|
||||||
);
|
|
||||||
f.render_widget(header, right_chunks[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let grid_chunks = Layout::default()
|
|
||||||
.direction(Direction::Horizontal)
|
|
||||||
.constraints([Constraint::Percentage(49), Constraint::Percentage(51)])
|
|
||||||
.split(right_chunks[1]);
|
|
||||||
|
|
||||||
let left_column = Layout::default()
|
|
||||||
.direction(Direction::Vertical)
|
|
||||||
.constraints([Constraint::Percentage(49), Constraint::Percentage(51)])
|
|
||||||
.split(grid_chunks[0]);
|
|
||||||
|
|
||||||
let right_column = Layout::default()
|
|
||||||
.direction(Direction::Vertical)
|
|
||||||
.constraints([Constraint::Percentage(49), Constraint::Percentage(51)])
|
|
||||||
.split(grid_chunks[1]);
|
|
||||||
|
|
||||||
let st = APP_STATE.lock().unwrap();
|
|
||||||
let w = grid_chunks[0].width.saturating_sub(2);
|
|
||||||
|
|
||||||
let ping_ds = downsample_to_fit_width(&smooth_data(&st.ping, 3), w + 4);
|
|
||||||
let cpu_ds = downsample_to_fit_width(&smooth_data(&st.cpu, 3), w);
|
|
||||||
let ram_ds = downsample_to_fit_width(&smooth_data(&st.ram, 3), w);
|
|
||||||
let down_ds = downsample_to_fit_width(&st.net_down, w + 4);
|
|
||||||
let up_ds = downsample_to_fit_width(&st.net_up, w + 4);
|
|
||||||
|
|
||||||
// ---- Render CPU ----
|
|
||||||
{
|
|
||||||
let min_x = cpu_ds.first().map(|(x, _)| *x).unwrap_or(0.0);
|
|
||||||
let max_x = cpu_ds.last().map(|(x, _)| *x).unwrap_or(100.0);
|
|
||||||
let block = Block::default()
|
|
||||||
.title(format!(
|
|
||||||
"CPU {}%",
|
|
||||||
&st.cpu.last().unwrap_or(&(0.0 as f64, 0.0 as f64)).1
|
|
||||||
))
|
|
||||||
.borders(Borders::TOP.union(Borders::RIGHT).union(Borders::LEFT));
|
|
||||||
let canvas = Canvas::default()
|
|
||||||
.block(block)
|
|
||||||
.x_bounds([min_x, max_x])
|
|
||||||
.y_bounds([0.0, 100.0])
|
|
||||||
.paint(|ctx| {
|
|
||||||
for (x, y) in &cpu_ds {
|
|
||||||
ctx.draw(&Line {
|
|
||||||
x1: *x,
|
|
||||||
y1: 0.0,
|
|
||||||
x2: *x,
|
|
||||||
y2: *y,
|
|
||||||
color: Color::Cyan,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
f.render_widget(canvas, left_column[0]);
|
|
||||||
draw_block_joins(
|
|
||||||
f,
|
|
||||||
left_column[0],
|
|
||||||
Borders::TOP.union(Borders::LEFT),
|
|
||||||
Borders::TOP,
|
|
||||||
);
|
|
||||||
draw_block_joins(
|
|
||||||
f,
|
|
||||||
left_column[0],
|
|
||||||
Borders::TOP.union(Borders::RIGHT),
|
|
||||||
Borders::RIGHT,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Render RAM ----
|
|
||||||
{
|
|
||||||
let min_x = ram_ds.first().map(|(x, _)| *x).unwrap_or(0.0);
|
|
||||||
let max_x = ram_ds.last().map(|(x, _)| *x).unwrap_or(100.0);
|
|
||||||
|
|
||||||
let ram_used = sys.used_memory() as f64 / 1024.0 / 1024.0 / 1024.0;
|
|
||||||
let ram_total = sys.total_memory() as f64 / 1024.0 / 1024.0 / 1024.0;
|
|
||||||
let ram = (sys.used_memory() as f64 / sys.total_memory() as f64) * 100.0;
|
|
||||||
|
|
||||||
let canvas = Canvas::default()
|
|
||||||
.block(
|
|
||||||
Block::default()
|
|
||||||
.title(format!(
|
|
||||||
"RAM {:.1}% ({:.1}GiB/{:.1}GiB)",
|
|
||||||
ram, ram_used, ram_total
|
|
||||||
))
|
|
||||||
.borders(Borders::ALL),
|
|
||||||
)
|
|
||||||
.x_bounds([min_x, max_x])
|
|
||||||
.y_bounds([0.0, 100.0])
|
|
||||||
.paint(|ctx| {
|
|
||||||
for (x, y) in &ram_ds {
|
|
||||||
ctx.draw(&Line {
|
|
||||||
x1: *x,
|
|
||||||
y1: 0.0,
|
|
||||||
x2: *x,
|
|
||||||
y2: *y,
|
|
||||||
color: Color::Magenta,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
f.render_widget(canvas, left_column[1]);
|
|
||||||
draw_block_joins(
|
|
||||||
f,
|
|
||||||
left_column[1],
|
|
||||||
Borders::ALL,
|
|
||||||
Borders::TOP.union(Borders::RIGHT),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Render Ping ----
|
|
||||||
{
|
|
||||||
let min_x = ping_ds.first().map(|(x, _)| *x).unwrap_or(0.0);
|
|
||||||
let max_x = ping_ds.last().map(|(x, _)| *x).unwrap_or(100.0);
|
|
||||||
let max_y = ping_ds.iter().map(|(_, y)| *y).fold(1.0, f64::max);
|
|
||||||
|
|
||||||
let canvas = Canvas::default()
|
|
||||||
.block(
|
|
||||||
Block::default()
|
|
||||||
.title(format!("Ping {:.1}(ms)", max_y))
|
|
||||||
.borders(Borders::TOP.union(Borders::RIGHT)),
|
|
||||||
)
|
|
||||||
.x_bounds([min_x, max_x])
|
|
||||||
.y_bounds([0.0, max_y])
|
|
||||||
.paint(|ctx| {
|
|
||||||
for (x, y) in &ping_ds {
|
|
||||||
ctx.draw(&Line {
|
|
||||||
x1: *x,
|
|
||||||
y1: 0.0,
|
|
||||||
x2: *x,
|
|
||||||
y2: *y,
|
|
||||||
color: Color::Yellow,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
f.render_widget(canvas, right_column[0]);
|
|
||||||
draw_block_joins(
|
|
||||||
f,
|
|
||||||
right_column[0],
|
|
||||||
Borders::RIGHT.union(Borders::TOP),
|
|
||||||
Borders::TOP,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Render Network ----
|
|
||||||
{
|
|
||||||
let min_x = up_ds.first().map(|(x, _)| *x).unwrap_or(0.0);
|
|
||||||
let max_x = up_ds.last().map(|(x, _)| *x).unwrap_or(100.0);
|
|
||||||
let mut max_sum: f64 = 1.0;
|
|
||||||
for ((_, up), (_, down)) in up_ds.iter().zip(down_ds.iter()) {
|
|
||||||
max_sum = max_sum.max(up + down);
|
|
||||||
}
|
|
||||||
|
|
||||||
let canvas = Canvas::default()
|
|
||||||
.block(
|
|
||||||
Block::default()
|
|
||||||
.title("Network Up/Down")
|
|
||||||
.borders(Borders::TOP.union(Borders::RIGHT).union(Borders::BOTTOM)),
|
|
||||||
)
|
|
||||||
.x_bounds([min_x, max_x])
|
|
||||||
.y_bounds([0.0, max_sum])
|
|
||||||
.paint(|ctx| {
|
|
||||||
for (x, dval) in &down_ds {
|
|
||||||
ctx.draw(&Line {
|
|
||||||
x1: *x,
|
|
||||||
y1: 0.0,
|
|
||||||
x2: *x,
|
|
||||||
y2: *dval,
|
|
||||||
color: Color::Red,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (x, uval) in &up_ds {
|
|
||||||
let dval = down_ds
|
|
||||||
.iter()
|
|
||||||
.find(|(xx, _)| (*xx - *x).abs() < f64::EPSILON)
|
|
||||||
.map(|(_, y)| *y)
|
|
||||||
.unwrap_or(0.0);
|
|
||||||
|
|
||||||
ctx.draw(&Line {
|
|
||||||
x1: *x,
|
|
||||||
y1: dval,
|
|
||||||
x2: *x,
|
|
||||||
y2: dval + *uval,
|
|
||||||
color: Color::Green,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
f.render_widget(canvas, right_column[1]);
|
|
||||||
draw_block_joins(
|
|
||||||
f,
|
|
||||||
right_column[1],
|
|
||||||
Borders::TOP.union(Borders::RIGHT).union(Borders::BOTTOM),
|
|
||||||
Borders::TOP,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
pub mod app_state;
|
pub mod app_state;
|
||||||
pub mod log_panel;
|
pub mod log_panel;
|
||||||
pub mod nav_bar;
|
|
||||||
pub mod ratatui_interface;
|
|
||||||
pub mod widgets {
|
pub mod widgets {
|
||||||
pub mod betterblock;
|
pub mod betterblock;
|
||||||
}
|
}
|
||||||
|
pub mod tui;
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
use std::str;
|
|
||||||
|
|
||||||
use crate::gui::log_panel;
|
|
||||||
|
|
||||||
pub struct Screen {
|
|
||||||
pub title: String,
|
|
||||||
pub render: Box<dyn Fn() + Send + Sync + 'static>,
|
|
||||||
}
|
|
||||||
impl Screen {
|
|
||||||
pub fn new(title: &str, render: Box<dyn Fn() + Send + Sync + 'static>) -> Self {
|
|
||||||
Screen {
|
|
||||||
title: String::from(title),
|
|
||||||
render,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn renderf(&self) {
|
|
||||||
(self.render)();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub struct NavBar {
|
|
||||||
pub current_screen: Screen,
|
|
||||||
pub screens: Vec<Screen>,
|
|
||||||
}
|
|
||||||
impl NavBar {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
NavBar {
|
|
||||||
current_screen: Screen {
|
|
||||||
title: String::from("Main"),
|
|
||||||
render: Box::new(|| log_panel::render()),
|
|
||||||
},
|
|
||||||
screens: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
use std::{
|
|
||||||
io::Stdout,
|
|
||||||
sync::{Arc, LazyLock},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{SHUTDOWN, gui::nav_bar::NavBar};
|
|
||||||
use color_eyre::Result;
|
|
||||||
use crossterm::{
|
|
||||||
execute,
|
|
||||||
terminal::{EnterAlternateScreen, enable_raw_mode},
|
|
||||||
};
|
|
||||||
use futures_util::lock::Mutex;
|
|
||||||
use ratatui::{Terminal, prelude::CrosstermBackend};
|
|
||||||
use std::io::stdout;
|
|
||||||
use tokio::task;
|
|
||||||
|
|
||||||
pub static TERMINAL: LazyLock<Arc<Mutex<Terminal<CrosstermBackend<Stdout>>>>> =
|
|
||||||
LazyLock::new(|| {
|
|
||||||
Arc::new(Mutex::new(
|
|
||||||
Terminal::new(CrosstermBackend::new(stdout())).unwrap(),
|
|
||||||
))
|
|
||||||
});
|
|
||||||
pub static NAV_BAR: LazyLock<Arc<Mutex<NavBar>>> =
|
|
||||||
LazyLock::new(|| Arc::new(Mutex::new(NavBar::new())));
|
|
||||||
|
|
||||||
pub fn launch() -> Result<()> {
|
|
||||||
color_eyre::install()?;
|
|
||||||
task::spawn(run());
|
|
||||||
ratatui::restore();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_terminal() {
|
|
||||||
let mut stdout = stdout();
|
|
||||||
execute!(stdout, EnterAlternateScreen).unwrap();
|
|
||||||
enable_raw_mode().unwrap();
|
|
||||||
}
|
|
||||||
async fn run() -> Result<()> {
|
|
||||||
init_terminal();
|
|
||||||
loop {
|
|
||||||
if *SHUTDOWN.read().await {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
NAV_BAR.lock().await.current_screen.renderf();
|
|
||||||
tokio::time::sleep(Duration::from_millis(1000)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
189
src/gui/tui.rs
189
src/gui/tui.rs
|
|
@ -1,7 +1,32 @@
|
||||||
use sha1::digest::block_buffer::Lazy;
|
use std::{
|
||||||
use tokio::{self, sync::RwLock};
|
default,
|
||||||
|
io::{Stdout, stdout},
|
||||||
|
sync::Arc,
|
||||||
|
thread,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{gui::log_panel, main::SHUTDOWN};
|
use crossterm::{
|
||||||
|
execute,
|
||||||
|
terminal::{EnterAlternateScreen, enable_raw_mode},
|
||||||
|
};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use ratatui::{
|
||||||
|
Frame, Terminal,
|
||||||
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
|
prelude::CrosstermBackend,
|
||||||
|
style::Color,
|
||||||
|
widgets::{
|
||||||
|
Block, Borders, List, ListItem,
|
||||||
|
canvas::{Canvas, Line},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use tokio::{
|
||||||
|
self,
|
||||||
|
sync::{Mutex, RwLock},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{APP_STATE, SHUTDOWN};
|
||||||
|
|
||||||
// ****** UTIL ******
|
// ****** UTIL ******
|
||||||
fn init_terminal() {
|
fn init_terminal() {
|
||||||
|
|
@ -12,25 +37,159 @@ fn init_terminal() {
|
||||||
|
|
||||||
// ****** MAIN ******
|
// ****** MAIN ******
|
||||||
pub static UNIQUE: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(false));
|
pub static UNIQUE: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(false));
|
||||||
pub static TERMINAL: LazyLock<Arc<Mutex<Terminal<CrosstermBackend<Stdout>>>>> =
|
pub static TERMINAL: Lazy<Arc<Mutex<Terminal<CrosstermBackend<Stdout>>>>> = Lazy::new(|| {
|
||||||
LazyLock::new(|| {
|
Arc::new(Mutex::new(
|
||||||
Arc::new(Mutex::new(
|
Terminal::new(CrosstermBackend::new(stdout())).unwrap(),
|
||||||
Terminal::new(CrosstermBackend::new(stdout())).unwrap(),
|
))
|
||||||
))
|
});
|
||||||
});
|
|
||||||
|
|
||||||
pub fn start_tui() {
|
pub fn start_tui() {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
init_terminal();
|
init_terminal();
|
||||||
while !SHUTDOWN.read().await {
|
loop {
|
||||||
if UNIQUE.read().await {
|
if *SHUTDOWN.read().await {
|
||||||
render_tui();
|
break;
|
||||||
|
}
|
||||||
|
if *UNIQUE.read().await {
|
||||||
|
render_tui().await;
|
||||||
} else {
|
} else {
|
||||||
thread::sleep(Duration::from_millis(50));
|
thread::sleep(Duration::from_millis(50));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
pub fn render_tui() {
|
pub async fn render_tui() {
|
||||||
log_panel::render();
|
TERMINAL
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.draw(|f| {
|
||||||
|
let area = f.area();
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints([
|
||||||
|
Constraint::Percentage(40),
|
||||||
|
Constraint::Percentage(60),
|
||||||
|
Constraint::Max(40),
|
||||||
|
])
|
||||||
|
.constraints([
|
||||||
|
Constraint::Percentage(40),
|
||||||
|
Constraint::Percentage(60),
|
||||||
|
Constraint::Min(40),
|
||||||
|
])
|
||||||
|
.split(area);
|
||||||
|
let state;
|
||||||
|
{
|
||||||
|
state = APP_STATE.lock().unwrap().clone();
|
||||||
|
}
|
||||||
|
// LOGS
|
||||||
|
{
|
||||||
|
let items: Vec<ListItem> = state
|
||||||
|
.logs
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|s| ListItem::new(s.clone()))
|
||||||
|
.collect();
|
||||||
|
let list = List::new(items).block(
|
||||||
|
Block::default()
|
||||||
|
.title("Logs")
|
||||||
|
.borders(Borders::LEFT.union(Borders::TOP).union(Borders::BOTTOM)),
|
||||||
|
);
|
||||||
|
f.render_widget(list, chunks[0]);
|
||||||
|
}
|
||||||
|
// SETTINGS
|
||||||
|
{
|
||||||
|
let items: Vec<ListItem> = state
|
||||||
|
.logs
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|s| ListItem::new(s.clone()))
|
||||||
|
.collect();
|
||||||
|
let list = List::new(items).block(
|
||||||
|
Block::default()
|
||||||
|
.title("Settings")
|
||||||
|
.borders(Borders::LEFT.union(Borders::TOP).union(Borders::BOTTOM)),
|
||||||
|
);
|
||||||
|
f.render_widget(list, chunks[1]);
|
||||||
|
}
|
||||||
|
// GRAPHS
|
||||||
|
{
|
||||||
|
let stack = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints(
|
||||||
|
[
|
||||||
|
Constraint::Ratio(1, 3),
|
||||||
|
Constraint::Ratio(1, 3),
|
||||||
|
Constraint::Ratio(1, 3),
|
||||||
|
]
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
|
.split(chunks[2]);
|
||||||
|
render_graphs(
|
||||||
|
f,
|
||||||
|
stack[0],
|
||||||
|
"CPU".to_string(),
|
||||||
|
state.with_width(38).cpu,
|
||||||
|
Color::Cyan,
|
||||||
|
);
|
||||||
|
render_graphs(
|
||||||
|
f,
|
||||||
|
stack[1],
|
||||||
|
"RAM".to_string(),
|
||||||
|
state.with_width(38).ram,
|
||||||
|
Color::Green,
|
||||||
|
);
|
||||||
|
render_graphs(
|
||||||
|
f,
|
||||||
|
stack[2],
|
||||||
|
"PING".to_string(),
|
||||||
|
state.with_width(38).ping,
|
||||||
|
Color::Magenta,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
*UNIQUE.write().await = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_graphs(
|
||||||
|
f: &mut Frame<'_>,
|
||||||
|
area: Rect,
|
||||||
|
title: String,
|
||||||
|
graph: Vec<(f64, f64)>,
|
||||||
|
color: Color,
|
||||||
|
) {
|
||||||
|
let min_x = graph.first().map(|(x, _)| *x).unwrap_or(0.0);
|
||||||
|
let max_x = graph.last().map(|(x, _)| *x).unwrap_or(100.0);
|
||||||
|
let min_y = graph
|
||||||
|
.iter()
|
||||||
|
.map(|(_, y)| *y)
|
||||||
|
.filter(|y| *y > 0.0)
|
||||||
|
.min_by(|a, b| a.total_cmp(b))
|
||||||
|
.unwrap_or(0.0);
|
||||||
|
let max_y = graph.iter().map(|(_, y)| *y).fold(f64::MIN, f64::max);
|
||||||
|
let block = Block::default()
|
||||||
|
.title(format!(
|
||||||
|
"{}: {}, {}/{} MIN/MAX ",
|
||||||
|
title,
|
||||||
|
graph.last().unwrap_or(&(0.0 as f64, 0.0 as f64)).1 as i64,
|
||||||
|
min_y as i64,
|
||||||
|
max_y as i64
|
||||||
|
))
|
||||||
|
.borders(Borders::ALL);
|
||||||
|
let canvas = Canvas::default()
|
||||||
|
.block(block)
|
||||||
|
.x_bounds([min_x, max_x])
|
||||||
|
.y_bounds([0.0, 100.0])
|
||||||
|
.paint(|ctx| {
|
||||||
|
for (x, y) in &graph {
|
||||||
|
ctx.draw(&Line {
|
||||||
|
x1: *x,
|
||||||
|
y1: 0.0,
|
||||||
|
x2: *x,
|
||||||
|
y2: *y,
|
||||||
|
color,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
f.render_widget(canvas, area);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ use std::sync::LazyLock;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tokio::time::{Duration, sleep};
|
use tokio::time::{Duration, sleep};
|
||||||
use tower::make::AsService;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
mod auth;
|
mod auth;
|
||||||
|
|
@ -23,8 +22,9 @@ use crate::communities::community_manager;
|
||||||
use crate::communities::interactables::registry;
|
use crate::communities::interactables::registry;
|
||||||
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
||||||
use crate::gui::app_state::AppState;
|
use crate::gui::app_state::AppState;
|
||||||
|
use crate::gui::log_panel;
|
||||||
use crate::gui::log_panel::{log_message, log_message_trans};
|
use crate::gui::log_panel::{log_message, log_message_trans};
|
||||||
use crate::gui::{log_panel, ratatui_interface};
|
use crate::gui::tui;
|
||||||
use crate::langu::language_creator;
|
use crate::langu::language_creator;
|
||||||
use crate::langu::language_manager::format;
|
use crate::langu::language_manager::format;
|
||||||
use crate::omikron::omikron_connection::OMIKRON_CONNECTION;
|
use crate::omikron::omikron_connection::OMIKRON_CONNECTION;
|
||||||
|
|
@ -56,10 +56,7 @@ async fn main() {
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
log_panel::setup();
|
log_panel::setup();
|
||||||
if let Err(e) = ratatui_interface::launch() {
|
tui::start_tui();
|
||||||
println!("Ui launch failed: {}", &e.to_string());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// BASIC CONFIGURATION
|
// BASIC CONFIGURATION
|
||||||
CONFIG.lock().await.load();
|
CONFIG.lock().await.load();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::auth::auth_connector;
|
use crate::auth::{auth_connector, crypto_helper};
|
||||||
use crate::users::user_profile::UserProfile;
|
use crate::users::user_profile::UserProfile;
|
||||||
use crate::util::config_util::CONFIG;
|
use crate::util::config_util::CONFIG;
|
||||||
use crate::util::file_util::{load_file, save_file};
|
use crate::util::file_util::{load_file, save_file};
|
||||||
|
|
@ -10,7 +10,8 @@ use rand::Rng;
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
use rand_core::RngCore;
|
use rand_core::RngCore;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::io;
|
use std::io::{self};
|
||||||
|
use std::str::FromStr;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use x448::{PublicKey, Secret};
|
use x448::{PublicKey, Secret};
|
||||||
|
|
@ -18,6 +19,31 @@ use x448::{PublicKey, Secret};
|
||||||
static USERS: Lazy<Mutex<Vec<UserProfile>>> = Lazy::new(|| Mutex::new(Vec::new()));
|
static USERS: Lazy<Mutex<Vec<UserProfile>>> = Lazy::new(|| Mutex::new(Vec::new()));
|
||||||
static UNIQUE: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
|
static UNIQUE: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
|
||||||
|
|
||||||
|
// uuid::private_key
|
||||||
|
pub async fn load_from_tu(username: &str) -> Result<(), ()> {
|
||||||
|
let file_content = load_file("", &format!("{}.tu", username));
|
||||||
|
let segments = file_content.split("::").collect::<Vec<&str>>();
|
||||||
|
let uuid = Uuid::from_str(segments[0]).unwrap();
|
||||||
|
let b64_private_key = segments[1];
|
||||||
|
|
||||||
|
let secret: Secret = crypto_helper::load_secret_key(b64_private_key).unwrap();
|
||||||
|
let public_key = PublicKey::from(&secret);
|
||||||
|
|
||||||
|
let mut bytes = [0u8; 192];
|
||||||
|
OsRng.fill(bytes.as_mut());
|
||||||
|
let reset_token = STANDARD.encode(&bytes);
|
||||||
|
|
||||||
|
let user_profile = UserProfile::new(
|
||||||
|
uuid,
|
||||||
|
username.to_string(),
|
||||||
|
Some(username.to_string()),
|
||||||
|
crypto_helper::public_key_to_base64(&public_key),
|
||||||
|
crypto_helper::hex_hash(b64_private_key),
|
||||||
|
reset_token,
|
||||||
|
);
|
||||||
|
USERS.lock().unwrap().push(user_profile);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
pub async fn create_user(username: &str) -> (Option<UserProfile>, Option<String>) {
|
pub async fn create_user(username: &str) -> (Option<UserProfile>, Option<String>) {
|
||||||
let user_id = auth_connector::get_register().await.unwrap();
|
let user_id = auth_connector::get_register().await.unwrap();
|
||||||
let mut buf = [0u8; 56];
|
let mut buf = [0u8; 56];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue