From 15bbe9b8bc8e43e98a141a2b25bbeb973ccd39b7 Mon Sep 17 00:00:00 2001 From: hinto-janaiyo <hinto.janaiyo@protonmail.com> Date: Wed, 30 Nov 2022 17:21:55 -0500 Subject: [PATCH] helper: add initial struct, add [HumanTime] for formatting uptime --- src/helper.rs | 130 +++++++++++++++++++++++++++++++++++++++++++++----- src/main.rs | 32 ++++++++----- 2 files changed, 138 insertions(+), 24 deletions(-) diff --git a/src/helper.rs b/src/helper.rs index 8312ad5..2d4ccbf 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -39,11 +39,34 @@ use std::{ sync::{Arc,Mutex}, path::PathBuf, process::Command, + time::*, thread, }; use crate::constants::*; use log::*; +//---------------------------------------------------------------------------------------------------- [Helper] Struct +// A meta struct holding all the data that gets processed in this thread +pub struct Helper { + uptime: HumanTime, // Gupax uptime formatting for humans + p2pool: Process, // P2Pool process state + xmrig: Process, // XMRig process state + p2pool_api: P2poolApi, // P2Pool API state + xmrig_api: XmrigApi, // XMRig API state +} + +impl Helper { + pub fn new(instant: std::time::Instant) -> Self { + Self { + uptime: HumanTime::into_human(instant.elapsed()), + p2pool: Process::new(ProcessName::P2pool, String::new(), PathBuf::new()), + xmrig: Process::new(ProcessName::Xmrig, String::new(), PathBuf::new()), + p2pool_api: P2poolApi::new(), + xmrig_api: XmrigApi::new(), + } + } +} + //---------------------------------------------------------------------------------------------------- [Process] Struct // This holds all the state of a (child) process. // The main GUI thread will use this to display console text, online state, etc. @@ -51,6 +74,8 @@ pub struct Process { name: ProcessName, // P2Pool or XMRig? state: ProcessState, // The state of the process (alive, dead, etc) signal: ProcessSignal, // Did the user click [Start/Stop/Restart]? + start: Instant, // Starttime of process + uptime: HumanTime, // Uptime of process output: String, // This is the process's stdout + stderr // STDIN Problem: // - User can input many many commands in 1 second @@ -68,10 +93,13 @@ pub struct Process { //---------------------------------------------------------------------------------------------------- [Process] Impl impl Process { pub fn new(name: ProcessName, args: String, path: PathBuf) -> Self { + let now = Instant::now(); Self { name, state: ProcessState::Dead, signal: ProcessSignal::None, + start: now, + uptime: HumanTime::into_human(now.elapsed()), // P2Pool log level 1 produces a bit less than 100,000 lines a day. // Assuming each line averages 80 UTF-8 scalars (80 bytes), then this // initial buffer should last around a week (56MB) before resetting. @@ -108,27 +136,105 @@ pub enum ProcessSignal { #[derive(Copy,Clone,Eq,PartialEq,Debug)] pub enum ProcessName { - P2Pool, - XMRig, + P2pool, + Xmrig, } impl std::fmt::Display for ProcessState { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{:#?}", self) } } impl std::fmt::Display for ProcessSignal { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{:#?}", self) } } impl std::fmt::Display for ProcessName { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{:#?}", self) } } +//---------------------------------------------------------------------------------------------------- [HumanTime] +// This converts a [std::time::Duration] into something more readable. +// Used for uptime display purposes: [7 years, 8 months, 15 days, 23 hours, 35 minutes, 1 second] +// Code taken from [https://docs.rs/humantime/] and edited to remove sub-second time, change spacing and some words. +use std::time::Duration; + +#[derive(Debug, Clone)] +pub struct HumanTime(Duration); + +impl HumanTime { + pub fn into_human(d: Duration) -> HumanTime { + HumanTime(d) + } + + fn plural(f: &mut std::fmt::Formatter, started: &mut bool, name: &str, value: u64) -> std::fmt::Result { + if value > 0 { + if *started { f.write_str(" ")?; } + } + write!(f, "{}{}", value, name)?; + if value > 1 { + f.write_str("s")?; + } + *started = true; + Ok(()) + } +} + +impl std::fmt::Display for HumanTime { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let secs = self.0.as_secs(); + if secs == 0 { + f.write_str("0s")?; + return Ok(()); + } + + let years = secs / 31_557_600; // 365.25d + let ydays = secs % 31_557_600; + let months = ydays / 2_630_016; // 30.44d + let mdays = ydays % 2_630_016; + let days = mdays / 86400; + let day_secs = mdays % 86400; + let hours = day_secs / 3600; + let minutes = day_secs % 3600 / 60; + let seconds = day_secs % 60; + + let ref mut started = false; + Self::plural(f, started, " year", years)?; + Self::plural(f, started, " month", months)?; + Self::plural(f, started, " day", days)?; + Self::plural(f, started, " hour", hours)?; + Self::plural(f, started, " minute", minutes)?; + Self::plural(f, started, " second", seconds)?; + Ok(()) + } +} + +//---------------------------------------------------------------------------------------------------- [P2poolApi] +pub struct P2poolApi { + +} + +impl P2poolApi { + pub fn new() -> Self { + Self { + } + } +} + +//---------------------------------------------------------------------------------------------------- [XmrigApi] +pub struct XmrigApi { + +} + +impl XmrigApi { + pub fn new() -> Self { + Self { + } + } +} + //---------------------------------------------------------------------------------------------------- The "helper" loop #[tokio::main] pub async fn helper() { thread::spawn(|| { loop { - // 1. Check process signals, match, do action (start, kill, read) - // 2. Spawn P2Pool API task - // 3. Spawn XMRig HTTP API task - // 4. - // 5. - // 6. - // 7. - // 8. - // 9. - // 10. + // 1. Spawn child processes (if signal found) + // 2. Create stdout pipe thread (if new child process) + // 3. Send stdin (if signal found) + // 4. Kill child process (if signal found) + // 4. Collect P2Pool API task (if alive) + // 5. Collect XMRig HTTP API task (if alive) + // 6. Execute all async tasks + // 7. Set Gupax/P2Pool/XMRig uptime }}); } diff --git a/src/main.rs b/src/main.rs index 3362b70..52cbfc1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,7 +61,7 @@ mod p2pool; mod xmrig; mod update; mod helper; -use {ferris::*,constants::*,node::*,disk::*,status::*,update::*,gupax::*}; +use {ferris::*,constants::*,node::*,disk::*,status::*,update::*,gupax::*,helper::*}; //---------------------------------------------------------------------------------------------------- Struct + Impl // The state of the outer main [App]. @@ -98,13 +98,17 @@ pub struct App { // indicate if an error message needs to be displayed // (it takes up the whole screen with [error_msg] and buttons for ok/quit/etc) error_state: ErrorState, - // Process State: - // This holds everything related to the - // child processes when starting P2Pool/XMRig. + // Helper State: + // This holds everything related to the data + // processed by the "helper thread", including + helper: Arc<Mutex<Helper>>, + +// Fix-me. +// These shouldn't exist +// Just for debugging. p2pool: bool, xmrig: bool, -// p2pool: Arc<Mutex<Process>>, -// xmrig: Arc<Mutex<Process>>, + // State from [--flags] no_startup: bool, // Static stuff @@ -133,7 +137,7 @@ impl App { } } - fn new() -> Self { + fn new(now: Instant) -> Self { info!("Initializing App Struct..."); let mut app = Self { tab: Tab::default(), @@ -153,12 +157,17 @@ impl App { restart: Arc::new(Mutex::new(Restart::No)), diff: false, error_state: ErrorState::new(), + helper: Arc::new(Mutex::new(Helper::new(now))), + +// TODO +// these p2pool/xmrig bools are here for debugging purposes +// they represent the online/offline status. +// fix this later when [Helper] is integrated. + p2pool: false, xmrig: false, -// p2pool: Arc::new(Mutex::new(Process::new())), -// xmrig: Arc::new(Mutex::new(Process::new())), no_startup: false, - now: Instant::now(), + now, exe: String::new(), dir: String::new(), resolution: Vec2::new(APP_DEFAULT_HEIGHT, APP_DEFAULT_WIDTH), @@ -664,8 +673,7 @@ fn print_disk_file(path: &PathBuf) { fn main() { let now = Instant::now(); init_logger(now); - let mut app = App::new(); - app.now = now; + let mut app = App::new(now); init_auto(&mut app); let initial_window_size = match app.state.gupax.simple { true => Some(Vec2::new(app.state.gupax.selected_width as f32, app.state.gupax.selected_height as f32)),