UI Corners & Pingpong
This commit is contained in:
parent
cbd357ce14
commit
befc35bbfd
7 changed files with 267 additions and 194 deletions
|
|
@ -1,15 +1,20 @@
|
|||
use crate::APP_STATE;
|
||||
use crate::gui::widgets::betterblock::draw_block_joins;
|
||||
use crate::langu::language_manager::from_key;
|
||||
use crate::{APP_STATE, omikron::ping_pong_task::PingPongTask};
|
||||
use crossterm::{
|
||||
ExecutableCommand,
|
||||
terminal::{LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
||||
};
|
||||
use ratatui::widgets::canvas::{Canvas, Line};
|
||||
use ratatui::widgets::{
|
||||
BorderType,
|
||||
canvas::{Canvas, Line},
|
||||
};
|
||||
use ratatui::{
|
||||
Terminal,
|
||||
backend::CrosstermBackend,
|
||||
layout::{Constraint, Direction, Layout},
|
||||
style::Color,
|
||||
symbols,
|
||||
widgets::{Block, Borders, List, ListItem, Paragraph},
|
||||
};
|
||||
use std::{collections::VecDeque, io::stdout, process::Command, thread, time::Duration};
|
||||
|
|
@ -112,15 +117,33 @@ fn smooth_data(data: &[(f64, f64)], window_size: usize) -> Vec<(f64, f64)> {
|
|||
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 == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
let slice = if len <= width_usize {
|
||||
data.to_vec()
|
||||
} else {
|
||||
|
||||
if len >= width_usize {
|
||||
// Trim data to fit
|
||||
data[len - width_usize..].to_vec()
|
||||
};
|
||||
slice
|
||||
} 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 y = 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
|
||||
}
|
||||
}
|
||||
|
||||
fn measure_ping_ms(host: &str) -> Option<f64> {
|
||||
|
|
@ -195,10 +218,7 @@ pub fn setup() {
|
|||
st.push_net_down((counter, net_down));
|
||||
st.push_net_up((counter, net_up));
|
||||
|
||||
st.sys_info = format!(
|
||||
"CPU: {:.1}% RAM: {:.1}%\nNetDown: {} NetUp: {}",
|
||||
tcpu, ram, delta_received, delta_transmitted
|
||||
);
|
||||
st.sys_info = format!("NetDown: {} NetUp: {}", delta_received, delta_transmitted);
|
||||
}
|
||||
|
||||
counter += 1.0;
|
||||
|
|
@ -216,6 +236,8 @@ pub fn setup() {
|
|||
loop {
|
||||
terminal
|
||||
.draw(|f| {
|
||||
let sys = System::new_all();
|
||||
|
||||
let size = f.size();
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
|
|
@ -240,47 +262,55 @@ pub fn setup() {
|
|||
|
||||
let right_chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Length(5), Constraint::Min(0)])
|
||||
.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::ALL));
|
||||
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(50), Constraint::Percentage(50)])
|
||||
.constraints([Constraint::Percentage(49), Constraint::Percentage(51)])
|
||||
.split(right_chunks[1]);
|
||||
|
||||
let left_column = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||
.constraints([Constraint::Percentage(49), Constraint::Percentage(51)])
|
||||
.split(grid_chunks[0]);
|
||||
|
||||
let right_column = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||
.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);
|
||||
let ping_ds = downsample_to_fit_width(&smooth_data(&st.ping, 3), w + 1);
|
||||
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);
|
||||
let up_ds = downsample_to_fit_width(&st.net_up, w);
|
||||
let down_ds = downsample_to_fit_width(&st.net_down, w + 1);
|
||||
let up_ds = downsample_to_fit_width(&st.net_up, w + 1);
|
||||
|
||||
// ---- 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::default().title("CPU %").borders(Borders::ALL))
|
||||
.block(block)
|
||||
.x_bounds([min_x, max_x])
|
||||
.y_bounds([0.0, 100.0])
|
||||
.paint(|ctx| {
|
||||
|
|
@ -295,14 +325,38 @@ pub fn setup() {
|
|||
}
|
||||
});
|
||||
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("RAM %").borders(Borders::ALL))
|
||||
.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| {
|
||||
|
|
@ -316,7 +370,14 @@ pub fn setup() {
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
f.render_widget(canvas, left_column[1]);
|
||||
draw_block_joins(
|
||||
f,
|
||||
left_column[1],
|
||||
Borders::ALL,
|
||||
Borders::TOP.union(Borders::RIGHT),
|
||||
);
|
||||
}
|
||||
|
||||
// ---- Render Ping ----
|
||||
|
|
@ -328,8 +389,8 @@ pub fn setup() {
|
|||
let canvas = Canvas::default()
|
||||
.block(
|
||||
Block::default()
|
||||
.title(format!("Ping {}(ms)", max_y))
|
||||
.borders(Borders::ALL),
|
||||
.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])
|
||||
|
|
@ -345,6 +406,12 @@ pub fn setup() {
|
|||
}
|
||||
});
|
||||
f.render_widget(canvas, right_column[0]);
|
||||
draw_block_joins(
|
||||
f,
|
||||
right_column[0],
|
||||
Borders::RIGHT.union(Borders::TOP),
|
||||
Borders::TOP,
|
||||
);
|
||||
}
|
||||
|
||||
// ---- Render Network ----
|
||||
|
|
@ -356,42 +423,47 @@ pub fn setup() {
|
|||
max_sum = max_sum.max(up + down);
|
||||
}
|
||||
|
||||
let canvas = Canvas::default()
|
||||
.block(
|
||||
Block::default()
|
||||
.title("Network Up/Down")
|
||||
.borders(Borders::ALL),
|
||||
)
|
||||
.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);
|
||||
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,
|
||||
});
|
||||
}
|
||||
});
|
||||
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,2 +1,5 @@
|
|||
pub mod log_panel;
|
||||
pub mod ratatui_interface;
|
||||
pub mod widgets {
|
||||
pub mod betterblock;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
use color_eyre::Result;
|
||||
use color_eyre::eyre::Error;
|
||||
use crossterm::event::{self, Event};
|
||||
use ratatui::Frame;
|
||||
use ratatui::layout::{Constraint, Direction, Layout};
|
||||
use ratatui::widgets::{Block, Paragraph};
|
||||
use ratatui::{DefaultTerminal, Frame};
|
||||
use sys_info;
|
||||
use tokio::task;
|
||||
|
||||
use crate::omikron::omikron_connection::OmikronConnection;
|
||||
|
||||
pub fn launch(connection_status: bool) -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
task::spawn(run(connection_status));
|
||||
|
|
|
|||
68
src/gui/widgets/betterblock.rs
Normal file
68
src/gui/widgets/betterblock.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
use ratatui::layout::Rect;
|
||||
use ratatui::prelude::*;
|
||||
use ratatui::style::Style;
|
||||
use ratatui::widgets::Borders;
|
||||
|
||||
fn set_join_char(frame: &mut Frame, x: u16, y: u16, c: char) {
|
||||
frame
|
||||
.buffer_mut()
|
||||
.set_string(x, y, c.to_string(), Style::default());
|
||||
}
|
||||
|
||||
/// Draws corner join characters only if the block has borders on that side
|
||||
pub fn draw_block_joins(frame: &mut Frame, area: Rect, borders: Borders, joins: Borders) {
|
||||
let x0 = area.x;
|
||||
let y0 = area.y;
|
||||
let x1 = area.x + area.width - 1;
|
||||
let y1 = area.y + area.height - 1;
|
||||
|
||||
// Top-left corner
|
||||
if borders.contains(Borders::TOP) && borders.contains(Borders::LEFT) {
|
||||
let top_left = match (joins.contains(Borders::TOP), joins.contains(Borders::LEFT)) {
|
||||
(true, true) => '┼',
|
||||
(true, false) => '├',
|
||||
(false, true) => '┬',
|
||||
(false, false) => '┌',
|
||||
};
|
||||
set_join_char(frame, x0, y0, top_left);
|
||||
}
|
||||
|
||||
// Top-right corner
|
||||
if borders.contains(Borders::TOP) && borders.contains(Borders::RIGHT) {
|
||||
let top_right = match (joins.contains(Borders::TOP), joins.contains(Borders::RIGHT)) {
|
||||
(true, true) => '┼',
|
||||
(true, false) => '┤',
|
||||
(false, true) => '┬',
|
||||
(false, false) => '┐',
|
||||
};
|
||||
set_join_char(frame, x1, y0, top_right);
|
||||
}
|
||||
|
||||
// Bottom-left corner
|
||||
if borders.contains(Borders::BOTTOM) && borders.contains(Borders::LEFT) {
|
||||
let bottom_left = match (
|
||||
joins.contains(Borders::BOTTOM),
|
||||
joins.contains(Borders::LEFT),
|
||||
) {
|
||||
(true, true) => '┼',
|
||||
(true, false) => '├',
|
||||
(false, true) => '┴',
|
||||
(false, false) => '└',
|
||||
};
|
||||
set_join_char(frame, x0, y1, bottom_left);
|
||||
}
|
||||
|
||||
// Bottom-right corner
|
||||
if borders.contains(Borders::BOTTOM) && borders.contains(Borders::RIGHT) {
|
||||
let bottom_right = match (
|
||||
joins.contains(Borders::BOTTOM),
|
||||
joins.contains(Borders::RIGHT),
|
||||
) {
|
||||
(true, true) => '┼',
|
||||
(true, false) => '┤',
|
||||
(false, true) => '┴',
|
||||
(false, false) => '┘',
|
||||
};
|
||||
set_join_char(frame, x1, y1, bottom_right);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ use crate::gui::log_panel;
|
|||
use crate::gui::log_panel::{AppState, log_message, log_message_trans};
|
||||
use crate::langu::language_creator;
|
||||
use crate::omikron::omikron_connection::OmikronConnection;
|
||||
use crate::omikron::ping_pong_task::PingPongTask;
|
||||
use crate::users::user_manager::UserManager;
|
||||
use crate::util::config_util::CONFIG;
|
||||
|
||||
|
|
@ -75,7 +74,6 @@ async fn main() {
|
|||
// IDENTIFICATION ON OMIKRON
|
||||
let omikron: OmikronConnection = OmikronConnection::new();
|
||||
omikron.connect().await;
|
||||
let _ping_pong_task = PingPongTask::new(Arc::new(OmikronConnection::new()));
|
||||
omikron
|
||||
.send_message(
|
||||
CommunicationValue::new(CommunicationType::identification)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::APP_STATE;
|
|||
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
||||
use crate::gui::log_panel::log_message;
|
||||
use crate::gui::log_panel::log_message_trans;
|
||||
use crate::omikron::ping_pong_task::{self, PingPongTask};
|
||||
use crate::omikron::ping_pong_task::*;
|
||||
use crate::users::contact::Contact;
|
||||
use crate::users::user_community_util::UserCommunityUtil;
|
||||
use crate::util::chat_files::ChatFiles;
|
||||
|
|
@ -11,13 +11,13 @@ use futures_util::{SinkExt, StreamExt};
|
|||
use json::JsonValue;
|
||||
use json::number::Number;
|
||||
use std::collections::HashMap;
|
||||
use std::pin::Pin;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::time::{Duration, sleep};
|
||||
use tokio::time::{Duration, Instant, sleep};
|
||||
use tokio_tungstenite::{
|
||||
MaybeTlsStream, WebSocketStream, connect_async, tungstenite::protocol::Message,
|
||||
};
|
||||
|
|
@ -37,7 +37,8 @@ pub struct OmikronConnection {
|
|||
>,
|
||||
waiting: Arc<Mutex<HashMap<Uuid, Box<dyn Fn(CommunicationValue) + Send + Sync>>>>, // waiting for responses
|
||||
pingpong: Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>, // ping-pong handler
|
||||
ping_pong_task: Arc<Mutex<Option<Arc<PingPongTask>>>>,
|
||||
pub message_queue: Arc<Mutex<Vec<String>>>,
|
||||
pub message_send_times: Arc<Mutex<HashMap<Uuid, Instant>>>,
|
||||
}
|
||||
|
||||
impl OmikronConnection {
|
||||
|
|
@ -46,10 +47,15 @@ impl OmikronConnection {
|
|||
writer: Arc::new(Mutex::new(None)),
|
||||
waiting: Arc::new(Mutex::new(HashMap::new())),
|
||||
pingpong: Arc::new(Mutex::new(None)),
|
||||
ping_pong_task: Arc::new(Mutex::new(None)),
|
||||
message_queue: Arc::new(Mutex::new(Vec::new())),
|
||||
message_send_times: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn reconnect(&self) {
|
||||
self.disconnect().await;
|
||||
self.connect().await;
|
||||
}
|
||||
/// Connect loop with retry
|
||||
pub async fn connect(&self) {
|
||||
loop {
|
||||
|
|
@ -58,18 +64,19 @@ impl OmikronConnection {
|
|||
let (write_half, read_half) = ws_stream.split();
|
||||
*self.writer.lock().await = Some(write_half);
|
||||
self.spawn_listener(read_half).await;
|
||||
|
||||
let ppt = PingPongTask::new(Arc::new(self.clone()));
|
||||
*self.ping_pong_task.lock().await = Some(Arc::new(ppt.clone()));
|
||||
tokio::spawn(async move {
|
||||
let cloned_self = self.clone();
|
||||
let handle = tokio::spawn(async move {
|
||||
loop {
|
||||
ppt.send_ping();
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
cloned_self.send_ping().await;
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
});
|
||||
|
||||
*self.pingpong.lock().await = Some(handle);
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
log_message("CONNECTION FAILED");
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
}
|
||||
}
|
||||
|
|
@ -106,15 +113,10 @@ impl OmikronConnection {
|
|||
break;
|
||||
}
|
||||
Ok(Message::Text(text)) => {
|
||||
let mut cv = CommunicationValue::from_json(&text); // needs CommunicationValue parser
|
||||
let mut cv = CommunicationValue::from_json(&text);
|
||||
if cv.is_type(CommunicationType::pong) {
|
||||
sel.ping_pong_task
|
||||
.lock()
|
||||
.await
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.handle_pong(&cv, true);
|
||||
return;
|
||||
sel.handle_pong(&cv, true).await;
|
||||
continue;
|
||||
}
|
||||
// ************************************************ //
|
||||
// Direct messages //
|
||||
|
|
@ -342,18 +344,4 @@ impl OmikronConnection {
|
|||
Err(tokio_tungstenite::tungstenite::Error::ConnectionClosed)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_ping_message(&self, uuid: Uuid) {
|
||||
// Send the ping message over the connection
|
||||
let ping_message = CommunicationValue::new(CommunicationType::ping)
|
||||
.with_id(uuid)
|
||||
.add_data_num(DataTypes::last_ping, Number::from(2))
|
||||
.to_json()
|
||||
.to_string();
|
||||
self.send_message(ping_message).await;
|
||||
}
|
||||
pub async fn reconnect(&self) {
|
||||
self.disconnect().await;
|
||||
self.connect().await;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,113 +1,61 @@
|
|||
use crate::APP_STATE;
|
||||
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
||||
use crate::gui::log_panel::AppState;
|
||||
use crate::gui::log_panel::{log_message, log_message_trans};
|
||||
use crate::omikron::omikron_connection::OmikronConnection;
|
||||
use color_eyre::owo_colors::OwoColorize;
|
||||
use crate::users::contact::Contact;
|
||||
use crate::users::user_community_util::UserCommunityUtil;
|
||||
use crate::util::chat_files::ChatFiles;
|
||||
use crate::util::chats_util::{get_user, get_users, mod_user};
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use std::arch::x86_64::_SIDD_MASKED_NEGATIVE_POLARITY;
|
||||
use json::JsonValue;
|
||||
use json::number::Number;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::time::{Duration, Instant, sleep};
|
||||
use tokio_tungstenite::{
|
||||
MaybeTlsStream, WebSocketStream, connect_async, tungstenite::protocol::Message,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PingPongTask {
|
||||
pub parent: Arc<OmikronConnection>,
|
||||
pub message_send_times: Arc<Mutex<HashMap<Uuid, Instant>>>,
|
||||
pub no_ping_in: Arc<Mutex<i32>>,
|
||||
pub last_ping: Arc<Mutex<Option<u64>>>,
|
||||
}
|
||||
fn assert_send_sync<T: Send + Sync>() {}
|
||||
impl OmikronConnection {
|
||||
pub async fn send_ping(&self) {
|
||||
let uuid = Uuid::new_v4();
|
||||
let send_time = Instant::now();
|
||||
|
||||
#[test]
|
||||
fn check_omikron_connection_send_sync() {
|
||||
assert_send_sync::<OmikronConnection>();
|
||||
}
|
||||
self.message_send_times.lock().await.insert(uuid, send_time);
|
||||
self.send_ping_message(uuid).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_pingpong_task_send_sync() {
|
||||
assert_send_sync::<PingPongTask>();
|
||||
}
|
||||
impl PingPongTask {
|
||||
pub fn new(parent: Arc<OmikronConnection>) -> Self {
|
||||
let message_send_times = Arc::new(Mutex::new(HashMap::new()));
|
||||
let no_ping_in = Arc::new(Mutex::new(-1));
|
||||
let last_ping = Arc::new(Mutex::new(None));
|
||||
pub async fn send_ping_message(&self, uuid: Uuid) {
|
||||
let ping_message = CommunicationValue::new(CommunicationType::ping)
|
||||
.with_id(uuid)
|
||||
.add_data_num(DataTypes::last_ping, Number::from(2))
|
||||
.to_json()
|
||||
.to_string();
|
||||
|
||||
let task = PingPongTask {
|
||||
parent: parent.clone(),
|
||||
message_send_times: message_send_times.clone(),
|
||||
no_ping_in: no_ping_in.clone(),
|
||||
last_ping: last_ping.clone(),
|
||||
self.send_message(ping_message).await;
|
||||
}
|
||||
|
||||
/// Handles incoming pong and calculates latency
|
||||
pub async fn handle_pong(&self, cv: &CommunicationValue, log: bool) {
|
||||
let id = cv.get_id();
|
||||
let send_time_opt = {
|
||||
let queue = self.message_send_times.lock().await;
|
||||
queue.get(&id).cloned()
|
||||
};
|
||||
|
||||
task
|
||||
}
|
||||
if let Some(send_time) = send_time_opt {
|
||||
let ping = Instant::now().duration_since(send_time).as_millis() as f64;
|
||||
self.message_send_times.lock().await.remove(&id);
|
||||
|
||||
pub fn send_ping(&self) {
|
||||
let sel = self.clone();
|
||||
tokio::spawn(async move {
|
||||
let uuid = Uuid::new_v4();
|
||||
let send_time = Instant::now();
|
||||
let mut message_send_times = sel.message_send_times.lock().await;
|
||||
message_send_times.insert(uuid, send_time);
|
||||
|
||||
let no_ping_in_val = {
|
||||
let no_ping_in = sel.no_ping_in.lock().await;
|
||||
*no_ping_in
|
||||
};
|
||||
|
||||
if no_ping_in_val != -1 {
|
||||
sel.handle_slow_connection(no_ping_in_val).await;
|
||||
} else {
|
||||
sel.parent.send_ping_message(uuid).await;
|
||||
if log {
|
||||
APP_STATE.lock().unwrap().push_ping_val(ping);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn handle_slow_connection(&self, no_ping_in: i32) {
|
||||
if no_ping_in > 8 {
|
||||
self.parent.reconnect().await;
|
||||
self.reconnect().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn reconnect(&self) {
|
||||
let mut no_ping_in = self.no_ping_in.lock().await;
|
||||
*no_ping_in = -1;
|
||||
}
|
||||
|
||||
pub fn handle_pong(&self, cv: &CommunicationValue, log: bool) {
|
||||
let sel = self.clone();
|
||||
let cv = cv.clone();
|
||||
tokio::spawn(async move {
|
||||
let send_time = {
|
||||
let message_send_times = sel.message_send_times.lock().await;
|
||||
message_send_times.get(&cv.get_id()).cloned()
|
||||
};
|
||||
|
||||
if let Some(send_time) = send_time {
|
||||
let receive_time = Instant::now();
|
||||
let ping = receive_time.duration_since(send_time).as_millis() as u64;
|
||||
|
||||
let mut last_ping = sel.last_ping.lock().await;
|
||||
*last_ping = Some(ping);
|
||||
let mut no_ping_in = sel.no_ping_in.lock().await;
|
||||
*no_ping_in = -1;
|
||||
let mut message_send_times = sel.message_send_times.lock().await;
|
||||
message_send_times.remove(&cv.get_id());
|
||||
|
||||
if log {
|
||||
APP_STATE.lock().unwrap().push_log("12.0".to_string());
|
||||
APP_STATE.lock().unwrap().push_ping_val(12.0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn cancel(&self) {
|
||||
let mut no_ping_in = self.no_ping_in.lock().await;
|
||||
*no_ping_in = -1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue