UI Corners & Pingpong

This commit is contained in:
Alex Emmet 2025-10-03 12:24:42 +02:00
commit befc35bbfd
7 changed files with 267 additions and 194 deletions

View file

@ -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::langu::language_manager::from_key;
use crate::{APP_STATE, omikron::ping_pong_task::PingPongTask};
use crossterm::{ use crossterm::{
ExecutableCommand, ExecutableCommand,
terminal::{LeaveAlternateScreen, disable_raw_mode, enable_raw_mode}, terminal::{LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
}; };
use ratatui::widgets::canvas::{Canvas, Line}; use ratatui::widgets::{
BorderType,
canvas::{Canvas, Line},
};
use ratatui::{ use ratatui::{
Terminal, Terminal,
backend::CrosstermBackend, backend::CrosstermBackend,
layout::{Constraint, Direction, Layout}, layout::{Constraint, Direction, Layout},
style::Color, style::Color,
symbols,
widgets::{Block, Borders, List, ListItem, Paragraph}, widgets::{Block, Borders, List, ListItem, Paragraph},
}; };
use std::{collections::VecDeque, io::stdout, process::Command, thread, time::Duration}; 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)> { fn downsample_to_fit_width(data: &[(f64, f64)], width: u16) -> Vec<(f64, f64)> {
let width_usize = (width as usize) * 2; let width_usize = (width as usize) * 2;
let len = data.len(); let len = data.len();
if len == 0 {
return Vec::new(); if len >= width_usize {
} // Trim data to fit
let slice = if len <= width_usize {
data.to_vec()
} else {
data[len - width_usize..].to_vec() data[len - width_usize..].to_vec()
}; } else {
slice 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> { 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_down((counter, net_down));
st.push_net_up((counter, net_up)); st.push_net_up((counter, net_up));
st.sys_info = format!( st.sys_info = format!("NetDown: {} NetUp: {}", delta_received, delta_transmitted);
"CPU: {:.1}% RAM: {:.1}%\nNetDown: {} NetUp: {}",
tcpu, ram, delta_received, delta_transmitted
);
} }
counter += 1.0; counter += 1.0;
@ -216,6 +236,8 @@ pub fn setup() {
loop { loop {
terminal terminal
.draw(|f| { .draw(|f| {
let sys = System::new_all();
let size = f.size(); let size = f.size();
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
@ -240,47 +262,55 @@ pub fn setup() {
let right_chunks = Layout::default() let right_chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([Constraint::Length(5), Constraint::Min(0)]) .constraints([Constraint::Length(3), Constraint::Min(0)])
.split(right); .split(right);
{ {
let st = APP_STATE.lock().unwrap(); let st = APP_STATE.lock().unwrap();
let header = Paragraph::new(st.sys_info.clone()) let header = Paragraph::new(st.sys_info.clone()).block(
.block(Block::default().title("System Info").borders(Borders::ALL)); Block::default()
.title("System Info")
.borders(Borders::TOP.union(Borders::LEFT).union(Borders::RIGHT)),
);
f.render_widget(header, right_chunks[0]); f.render_widget(header, right_chunks[0]);
} }
let grid_chunks = Layout::default() let grid_chunks = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) .constraints([Constraint::Percentage(49), Constraint::Percentage(51)])
.split(right_chunks[1]); .split(right_chunks[1]);
let left_column = Layout::default() let left_column = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) .constraints([Constraint::Percentage(49), Constraint::Percentage(51)])
.split(grid_chunks[0]); .split(grid_chunks[0]);
let right_column = Layout::default() let right_column = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) .constraints([Constraint::Percentage(49), Constraint::Percentage(51)])
.split(grid_chunks[1]); .split(grid_chunks[1]);
let st = APP_STATE.lock().unwrap(); let st = APP_STATE.lock().unwrap();
let w = grid_chunks[0].width.saturating_sub(2); 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 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 ram_ds = downsample_to_fit_width(&smooth_data(&st.ram, 3), w);
let down_ds = downsample_to_fit_width(&st.net_down, w); let down_ds = downsample_to_fit_width(&st.net_down, w + 1);
let up_ds = downsample_to_fit_width(&st.net_up, w); let up_ds = downsample_to_fit_width(&st.net_up, w + 1);
// ---- Render CPU ---- // ---- Render CPU ----
{ {
let min_x = cpu_ds.first().map(|(x, _)| *x).unwrap_or(0.0); 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 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() let canvas = Canvas::default()
.block(Block::default().title("CPU %").borders(Borders::ALL)) .block(block)
.x_bounds([min_x, max_x]) .x_bounds([min_x, max_x])
.y_bounds([0.0, 100.0]) .y_bounds([0.0, 100.0])
.paint(|ctx| { .paint(|ctx| {
@ -295,14 +325,38 @@ pub fn setup() {
} }
}); });
f.render_widget(canvas, left_column[0]); 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 ---- // ---- Render RAM ----
{ {
let min_x = ram_ds.first().map(|(x, _)| *x).unwrap_or(0.0); 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 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() 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]) .x_bounds([min_x, max_x])
.y_bounds([0.0, 100.0]) .y_bounds([0.0, 100.0])
.paint(|ctx| { .paint(|ctx| {
@ -316,7 +370,14 @@ pub fn setup() {
}); });
} }
}); });
f.render_widget(canvas, left_column[1]); f.render_widget(canvas, left_column[1]);
draw_block_joins(
f,
left_column[1],
Borders::ALL,
Borders::TOP.union(Borders::RIGHT),
);
} }
// ---- Render Ping ---- // ---- Render Ping ----
@ -328,8 +389,8 @@ pub fn setup() {
let canvas = Canvas::default() let canvas = Canvas::default()
.block( .block(
Block::default() Block::default()
.title(format!("Ping {}(ms)", max_y)) .title(format!("Ping {:.1}(ms)", max_y))
.borders(Borders::ALL), .borders(Borders::TOP.union(Borders::RIGHT)),
) )
.x_bounds([min_x, max_x]) .x_bounds([min_x, max_x])
.y_bounds([0.0, max_y]) .y_bounds([0.0, max_y])
@ -345,6 +406,12 @@ pub fn setup() {
} }
}); });
f.render_widget(canvas, right_column[0]); f.render_widget(canvas, right_column[0]);
draw_block_joins(
f,
right_column[0],
Borders::RIGHT.union(Borders::TOP),
Borders::TOP,
);
} }
// ---- Render Network ---- // ---- Render Network ----
@ -356,12 +423,11 @@ pub fn setup() {
max_sum = max_sum.max(up + down); max_sum = max_sum.max(up + down);
} }
let canvas = Canvas::default() let canvas =
.block( Canvas::default()
Block::default() .block(Block::default().title("Network Up/Down").borders(
.title("Network Up/Down") Borders::TOP.union(Borders::RIGHT).union(Borders::BOTTOM),
.borders(Borders::ALL), ))
)
.x_bounds([min_x, max_x]) .x_bounds([min_x, max_x])
.y_bounds([0.0, max_sum]) .y_bounds([0.0, max_sum])
.paint(|ctx| { .paint(|ctx| {
@ -392,6 +458,12 @@ pub fn setup() {
}); });
f.render_widget(canvas, right_column[1]); 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(); .unwrap();

View file

@ -1,2 +1,5 @@
pub mod log_panel; pub mod log_panel;
pub mod ratatui_interface; pub mod ratatui_interface;
pub mod widgets {
pub mod betterblock;
}

View file

@ -1,14 +1,10 @@
use color_eyre::Result; use color_eyre::Result;
use color_eyre::eyre::Error; use ratatui::Frame;
use crossterm::event::{self, Event};
use ratatui::layout::{Constraint, Direction, Layout}; use ratatui::layout::{Constraint, Direction, Layout};
use ratatui::widgets::{Block, Paragraph}; use ratatui::widgets::{Block, Paragraph};
use ratatui::{DefaultTerminal, Frame};
use sys_info; use sys_info;
use tokio::task; use tokio::task;
use crate::omikron::omikron_connection::OmikronConnection;
pub fn launch(connection_status: bool) -> Result<()> { pub fn launch(connection_status: bool) -> Result<()> {
color_eyre::install()?; color_eyre::install()?;
task::spawn(run(connection_status)); task::spawn(run(connection_status));

View 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);
}
}

View file

@ -19,7 +19,6 @@ use crate::gui::log_panel;
use crate::gui::log_panel::{AppState, log_message, log_message_trans}; use crate::gui::log_panel::{AppState, log_message, log_message_trans};
use crate::langu::language_creator; use crate::langu::language_creator;
use crate::omikron::omikron_connection::OmikronConnection; use crate::omikron::omikron_connection::OmikronConnection;
use crate::omikron::ping_pong_task::PingPongTask;
use crate::users::user_manager::UserManager; use crate::users::user_manager::UserManager;
use crate::util::config_util::CONFIG; use crate::util::config_util::CONFIG;
@ -75,7 +74,6 @@ async fn main() {
// IDENTIFICATION ON OMIKRON // IDENTIFICATION ON OMIKRON
let omikron: OmikronConnection = OmikronConnection::new(); let omikron: OmikronConnection = OmikronConnection::new();
omikron.connect().await; omikron.connect().await;
let _ping_pong_task = PingPongTask::new(Arc::new(OmikronConnection::new()));
omikron omikron
.send_message( .send_message(
CommunicationValue::new(CommunicationType::identification) CommunicationValue::new(CommunicationType::identification)

View file

@ -2,7 +2,7 @@ use crate::APP_STATE;
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
use crate::gui::log_panel::log_message; use crate::gui::log_panel::log_message;
use crate::gui::log_panel::log_message_trans; 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::contact::Contact;
use crate::users::user_community_util::UserCommunityUtil; use crate::users::user_community_util::UserCommunityUtil;
use crate::util::chat_files::ChatFiles; use crate::util::chat_files::ChatFiles;
@ -11,13 +11,13 @@ use futures_util::{SinkExt, StreamExt};
use json::JsonValue; use json::JsonValue;
use json::number::Number; use json::number::Number;
use std::collections::HashMap; use std::collections::HashMap;
use std::pin::Pin; use std::ops::Deref;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tokio::time::{Duration, sleep}; use tokio::time::{Duration, Instant, sleep};
use tokio_tungstenite::{ use tokio_tungstenite::{
MaybeTlsStream, WebSocketStream, connect_async, tungstenite::protocol::Message, 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 waiting: Arc<Mutex<HashMap<Uuid, Box<dyn Fn(CommunicationValue) + Send + Sync>>>>, // waiting for responses
pingpong: Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>, // ping-pong handler 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 { impl OmikronConnection {
@ -46,10 +47,15 @@ impl OmikronConnection {
writer: Arc::new(Mutex::new(None)), writer: Arc::new(Mutex::new(None)),
waiting: Arc::new(Mutex::new(HashMap::new())), waiting: Arc::new(Mutex::new(HashMap::new())),
pingpong: Arc::new(Mutex::new(None)), 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 /// Connect loop with retry
pub async fn connect(&self) { pub async fn connect(&self) {
loop { loop {
@ -58,18 +64,19 @@ impl OmikronConnection {
let (write_half, read_half) = ws_stream.split(); let (write_half, read_half) = ws_stream.split();
*self.writer.lock().await = Some(write_half); *self.writer.lock().await = Some(write_half);
self.spawn_listener(read_half).await; self.spawn_listener(read_half).await;
let cloned_self = self.clone();
let ppt = PingPongTask::new(Arc::new(self.clone())); let handle = tokio::spawn(async move {
*self.ping_pong_task.lock().await = Some(Arc::new(ppt.clone()));
tokio::spawn(async move {
loop { loop {
ppt.send_ping(); cloned_self.send_ping().await;
sleep(Duration::from_secs(5)).await; sleep(Duration::from_secs(1)).await;
} }
}); });
*self.pingpong.lock().await = Some(handle);
break; break;
} }
Err(e) => { Err(e) => {
log_message("CONNECTION FAILED");
sleep(Duration::from_secs(2)).await; sleep(Duration::from_secs(2)).await;
} }
} }
@ -106,15 +113,10 @@ impl OmikronConnection {
break; break;
} }
Ok(Message::Text(text)) => { 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) { if cv.is_type(CommunicationType::pong) {
sel.ping_pong_task sel.handle_pong(&cv, true).await;
.lock() continue;
.await
.as_mut()
.unwrap()
.handle_pong(&cv, true);
return;
} }
// ************************************************ // // ************************************************ //
// Direct messages // // Direct messages //
@ -342,18 +344,4 @@ impl OmikronConnection {
Err(tokio_tungstenite::tungstenite::Error::ConnectionClosed) 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;
}
} }

View file

@ -1,113 +1,61 @@
use crate::APP_STATE; use crate::APP_STATE;
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; 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 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 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::collections::HashMap;
use std::ops::Deref;
use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::net::TcpStream;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tokio::time::{Duration, Instant, sleep}; use tokio::time::{Duration, Instant, sleep};
use tokio_tungstenite::{
MaybeTlsStream, WebSocketStream, connect_async, tungstenite::protocol::Message,
};
use uuid::Uuid; use uuid::Uuid;
#[derive(Clone)] impl OmikronConnection {
pub struct PingPongTask { pub async fn send_ping(&self) {
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>() {}
#[test]
fn check_omikron_connection_send_sync() {
assert_send_sync::<OmikronConnection>();
}
#[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));
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(),
};
task
}
pub fn send_ping(&self) {
let sel = self.clone();
tokio::spawn(async move {
let uuid = Uuid::new_v4(); let uuid = Uuid::new_v4();
let send_time = Instant::now(); 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 = { self.message_send_times.lock().await.insert(uuid, send_time);
let no_ping_in = sel.no_ping_in.lock().await; self.send_ping_message(uuid).await;
*no_ping_in }
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();
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()
}; };
if no_ping_in_val != -1 { if let Some(send_time) = send_time_opt {
sel.handle_slow_connection(no_ping_in_val).await; let ping = Instant::now().duration_since(send_time).as_millis() as f64;
} else { self.message_send_times.lock().await.remove(&id);
sel.parent.send_ping_message(uuid).await;
}
});
}
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 { if log {
APP_STATE.lock().unwrap().push_log("12.0".to_string()); APP_STATE.lock().unwrap().push_ping_val(ping);
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;
} }
} }