diff --git a/src/constants.rs b/src/constants.rs index c3f3b90..dd73109 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -36,7 +36,8 @@ pub const BYTES_ICON: &[u8] = include_bytes!("../images/icons/icon@2x.png"); pub const BYTES_ICON: &[u8] = include_bytes!("../images/icons/icon.png"); pub const BYTES_BANNER: &[u8] = include_bytes!("../images/banner.png"); pub const HORIZONTAL: &str = "--------------------------------------------"; -pub const HORI_DOUBLE: &str = "----------------------------------------------------------------------------------------"; +// The text to separate my "process stopped, here's stats" text from the process output in the console. +pub const HORI_CONSOLE: &str = "---------------------------------------------------------------------------------------------------------------------------"; // P2Pool & XMRig default API stuff #[cfg(target_os = "windows")] @@ -73,6 +74,7 @@ pub const DARK_GRAY: egui::Color32 = egui::Color32::from_rgb(18, 18, 18); // [Duration] constants pub const SECOND: std::time::Duration = std::time::Duration::from_secs(1); +pub const ZERO_SECONDS: std::time::Duration = std::time::Duration::from_secs(0); pub const MILLI_900: std::time::Duration = std::time::Duration::from_millis(900); pub const TOKIO_SECOND: tokio::time::Duration = std::time::Duration::from_secs(1); diff --git a/src/helper.rs b/src/helper.rs index 3463935..c6ff4f3 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -71,8 +71,6 @@ pub struct Process { pub name: ProcessName, // P2Pool or XMRig? pub state: ProcessState, // The state of the process (alive, dead, etc) pub signal: ProcessSignal, // Did the user click [Start/Stop/Restart]? - pub start: Instant, // Start time of process - pub uptime: HumanTime, // Human readable process uptime // STDIN Problem: // - User can input many many commands in 1 second // - The process loop only processes every 1 second @@ -96,18 +94,19 @@ pub struct Process { // The "watchdog" threads mutate this, the "helper" thread synchronizes the [Pub*Api] structs // so that the data in here is cloned there roughly once a second. GUI thread never touches this. output: Arc>, + + // Start time of process. + start: std::time::Instant, } //---------------------------------------------------------------------------------------------------- [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()), + start: Instant::now(), stdin: Option::None, child: Option::None, // P2Pool log level 1 produces a bit less than 100,000 lines a day. @@ -331,6 +330,7 @@ impl Helper { path.push(P2POOL_API_PATH); let regex = P2poolRegex::new(); let output = Arc::clone(&process.lock().unwrap().output); + let start = process.lock().unwrap().start; // 4. Loop as watchdog loop { @@ -341,43 +341,46 @@ impl Helper { if process.lock().unwrap().signal == ProcessSignal::Stop { child_pty.lock().unwrap().kill(); // This actually sends a SIGHUP to p2pool (closes the PTY, hangs up on p2pool) // Wait to get the exit status - let mut lock = process.lock().unwrap(); let exit_status = match child_pty.lock().unwrap().wait() { - Ok(e) => if e.success() { lock.state = ProcessState::Dead; "Successful" } else { lock.state = ProcessState::Failed; "Failed" }, - _ => { lock.state = ProcessState::Failed; "Unknown Error" }, + Ok(e) => { + if e.success() { + process.lock().unwrap().state = ProcessState::Dead; "Successful" + } else { + process.lock().unwrap().state = ProcessState::Failed; "Failed" + } + }, + _ => { process.lock().unwrap().state = ProcessState::Failed; "Unknown Error" }, }; - let uptime = lock.uptime.clone(); + let uptime = HumanTime::into_human(start.elapsed()); info!("P2Pool | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status); // This is written directly into the public API, because sometimes the 900ms event loop can't catch it. - writeln!(pub_api.lock().unwrap().output, "{}\nP2Pool stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n", HORI_DOUBLE, uptime, exit_status, HORI_DOUBLE); - lock.signal = ProcessSignal::None; + writeln!(pub_api.lock().unwrap().output, "{}\nP2Pool stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n", HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE); + process.lock().unwrap().signal = ProcessSignal::None; break } else if process.lock().unwrap().signal == ProcessSignal::Restart { child_pty.lock().unwrap().kill(); // This actually sends a SIGHUP to p2pool (closes the PTY, hangs up on p2pool) // Wait to get the exit status - let mut lock = process.lock().unwrap(); let exit_status = match child_pty.lock().unwrap().wait() { Ok(e) => if e.success() { "Successful" } else { "Failed" }, _ => "Unknown Error", }; - let uptime = lock.uptime.clone(); + let uptime = HumanTime::into_human(start.elapsed()); info!("P2Pool | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status); // This is written directly into the public API, because sometimes the 900ms event loop can't catch it. - writeln!(pub_api.lock().unwrap().output, "{}\nP2Pool stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n", HORI_DOUBLE, uptime, exit_status, HORI_DOUBLE); - lock.state = ProcessState::Waiting; + writeln!(pub_api.lock().unwrap().output, "{}\nP2Pool stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n", HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE); + process.lock().unwrap().state = ProcessState::Waiting; break // Check if the process is secretly died without us knowing :) } else if let Ok(Some(code)) = child_pty.lock().unwrap().try_wait() { - let mut lock = process.lock().unwrap(); let exit_status = match code.success() { - true => { lock.state = ProcessState::Dead; "Successful" }, - false => { lock.state = ProcessState::Failed; "Failed" }, + true => { process.lock().unwrap().state = ProcessState::Dead; "Successful" }, + false => { process.lock().unwrap().state = ProcessState::Failed; "Failed" }, }; - let uptime = lock.uptime.clone(); + let uptime = HumanTime::into_human(start.elapsed()); info!("P2Pool | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status); // This is written directly into the public API, because sometimes the 900ms event loop can't catch it. - writeln!(pub_api.lock().unwrap().output, "{}\nP2Pool stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n", HORI_DOUBLE, uptime, exit_status, HORI_DOUBLE); - lock.signal = ProcessSignal::None; + writeln!(pub_api.lock().unwrap().output, "{}\nP2Pool stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n", HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE); + process.lock().unwrap().signal = ProcessSignal::None; break } @@ -392,7 +395,7 @@ impl Helper { drop(lock); // Always update from output - PubP2poolApi::update_from_output(&pub_api, &output, process.lock().unwrap().start.elapsed().as_secs_f64(), ®ex); + PubP2poolApi::update_from_output(&pub_api, &output, start.elapsed(), ®ex); // Read API file into string if let Ok(string) = Self::read_p2pool_api(&path) { @@ -506,19 +509,25 @@ use std::time::Duration; pub struct HumanTime(Duration); impl HumanTime { + pub fn new() -> HumanTime { + HumanTime(ZERO_SECONDS) + } + 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(" ")?; } + if *started { + f.write_str(", ")?; + } + write!(f, "{} {}", value, name)?; + if value > 1 { + f.write_str("s")?; + } + *started = true; } - write!(f, "{}{}", value, name)?; - if value > 1 { - f.write_str("s")?; - } - *started = true; Ok(()) } } @@ -527,7 +536,7 @@ 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")?; + f.write_str("0 seconds")?; return Ok(()); } @@ -542,12 +551,12 @@ impl std::fmt::Display for HumanTime { 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)?; + 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(()) } } @@ -701,6 +710,8 @@ pub struct PubP2poolApi { pub mini: bool, // Output pub output: String, + // Uptime + pub uptime: HumanTime, // These are manually parsed from the STDOUT. pub payouts: u128, pub payouts_hour: f64, @@ -725,6 +736,7 @@ impl PubP2poolApi { Self { mini: true, output: String::with_capacity(56_000_000), + uptime: HumanTime::new(), payouts: 0, payouts_hour: 0.0, payouts_day: 0.0, @@ -744,26 +756,30 @@ impl PubP2poolApi { } // Mutate [PubP2poolApi] with data the process output. - fn update_from_output(public: &Arc>, output: &Arc>, elapsed: f64, regex: &P2poolRegex) { + fn update_from_output(public: &Arc>, output: &Arc>, elapsed: std::time::Duration, regex: &P2poolRegex) { + // 1. Clone output let output = output.lock().unwrap().clone(); - // 1. Parse STDOUT + + // 2. Parse STDOUT let (payouts, xmr) = Self::calc_payouts_and_xmr(&output, ®ex); - // 2. Calculate hour/day/month given elapsed time + // 3. Calculate hour/day/month given elapsed time + let elapsed_as_secs_f64 = elapsed.as_secs_f64(); // Payouts - let per_sec = (payouts as f64) / elapsed; + let per_sec = (payouts as f64) / elapsed_as_secs_f64; let payouts_hour = (per_sec * 60.0) * 60.0; let payouts_day = payouts_hour * 24.0; let payouts_month = payouts_day * 30.0; // Total XMR - let per_sec = xmr / elapsed; + let per_sec = xmr / elapsed_as_secs_f64; let xmr_hour = (per_sec * 60.0) * 60.0; let xmr_day = payouts_hour * 24.0; let xmr_month = payouts_day * 30.0; - // 3. Mutate the struct with the new info + // 4. Mutate the struct with the new info let mut public = public.lock().unwrap(); *public = Self { + uptime: HumanTime::into_human(elapsed), output, payouts, xmr, @@ -779,7 +795,7 @@ impl PubP2poolApi { // Mutate [PubP2poolApi] with data from a [PrivP2poolApi] and the process output. fn update_from_priv(public: &Arc>, private: &Arc>) { - // 3. Final priv -> pub conversion + // priv -> pub conversion let private = private.lock().unwrap(); let mut public = public.lock().unwrap(); *public = Self {