helper: add initial struct, add [HumanTime] for formatting uptime

This commit is contained in:
hinto-janaiyo 2022-11-30 17:21:55 -05:00
parent eb4a70c483
commit c6dad5849d
No known key found for this signature in database
GPG key ID: B1C5A64B80691E45
2 changed files with 138 additions and 24 deletions

View file

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

View file

@ -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)),