From 74355605e9c8e28aa2a03b240d0efdf6063bcdf5 Mon Sep 17 00:00:00 2001 From: Louis-Marie Baer Date: Tue, 12 Mar 2024 12:25:34 +0100 Subject: [PATCH] feat: manage XvB like a new process feat: maj TODO fix: use Vec2 instead of separating height and witdh as args in functions feat: status of XvB process feat: button to autostart XvB process into Gupax Tab feat: autostart XvB if button is checked feat: buttons to start/stop/restart XvB process in XvB Tab feat: check token and address inputs before allowing to start XvB feat: add description to buttons when hovering fix: adjust test feat: function to verify if token exist on XvB API clippy auto and manual fix --- TODO_XMRvsBeast.md | 23 +- src/app/eframe_impl.rs | 15 +- src/app/mod.rs | 17 +- src/app/panels/bottom.rs | 986 ++++++++++++--------- src/app/panels/middle/gupax.rs | 113 +-- src/app/panels/middle/mod.rs | 33 +- src/app/panels/middle/p2pool/mod.rs | 13 +- src/app/panels/middle/status/benchmarks.rs | 13 +- src/app/panels/middle/status/mod.rs | 19 +- src/app/panels/middle/status/p2pool.rs | 7 +- src/app/panels/middle/status/processes.rs | 16 +- src/app/panels/middle/xmrig.rs | 43 +- src/app/panels/middle/xvb.rs | 8 +- src/app/panels/quit_error.rs | 4 +- src/app/panels/top.rs | 4 +- src/app/resize.rs | 4 +- src/disk/state.rs | 46 +- src/disk/tests.rs | 5 +- src/helper/mod.rs | 5 + src/helper/xvb.rs | 154 +++- src/inits.rs | 10 +- src/utils/constants.rs | 8 + 22 files changed, 932 insertions(+), 614 deletions(-) diff --git a/TODO_XMRvsBeast.md b/TODO_XMRvsBeast.md index 5e5ef8c7..7b4764df 100644 --- a/TODO_XMRvsBeast.md +++ b/TODO_XMRvsBeast.md @@ -12,19 +12,20 @@ - [x] link to website - [ ] message overing explaining registration and needs to read the rules. - [ ] token input - - [ ] information - - [ ] status of h/s received by the raffle, authenfication by token. + - [ ] hero checkbox + - [ ] log section + - [ ] status of h/s received by the raffle, authentication by token. - [ ] status of 1h and 24h average h/s sent to raffle from this instance - [ ] number of failures - - [ ] log section - - [ ] winner of round - - [ ] round in - - [ ] hero checkbox -- [x] status process XvB - - [x] public information from [API](https://xmrvsbeast.com/p2pool/stats) -- [ ] if not enough hashrate for min round and share acquirement OR no share acquired, node destination for xmrig is only p2pool. -- [ ] if share acquired and enough hashrate to keep up round min hashrate and share acquirement, switch node destination for xmrig between p2pool and raffle giving raffle minimum round requirement + buffer. -- [ ] if hero checked, give maximum hasrate to raffle while keeping enough for p2pool. + - [ ] round type in + - [ ] win or loose + - [ ] new process for XvB + - [x] status process XvB + - [x] public information from [API](https://xmrvsbeast.com/p2pool/stats) + - [x] stop, start, restart buttons + - [x] button to autostart + - [ ] distribute hashrate conforming to the algorithm. + - [ ] output log to console in XvB tab - [ ] edit metadata of project - [ ] cargo package metadata - [ ] pgp signatures diff --git a/src/app/eframe_impl.rs b/src/app/eframe_impl.rs index f75fad2e..138ce604 100644 --- a/src/app/eframe_impl.rs +++ b/src/app/eframe_impl.rs @@ -35,19 +35,25 @@ impl eframe::App for App { let xmrig_is_waiting = xmrig.is_waiting(); let xmrig_state = xmrig.state; drop(xmrig); + debug!("App | Locking and collecting XvB state..."); + let xvb = lock!(self.xvb); + let xvb_is_alive = xvb.is_alive(); + let xvb_is_waiting = xvb.is_waiting(); + let xvb_state = xvb.state; + drop(xvb); // This sets the top level Ui dimensions. // Used as a reference for other uis. debug!("App | Setting width/height"); CentralPanel::default().show(ctx, |ui| { let available_width = ui.available_width(); - if self.width != available_width { - self.width = available_width; + if self.size.x != available_width { + self.size.x = available_width; if self.now.elapsed().as_secs() > 5 { self.must_resize = true; } }; - self.height = ui.available_height(); + self.size.y = ui.available_height(); }); self.resize(ctx); @@ -77,12 +83,15 @@ impl eframe::App for App { ctx, p2pool_state, xmrig_state, + xvb_state, &key, wants_input, p2pool_is_waiting, xmrig_is_waiting, + xvb_is_waiting, p2pool_is_alive, xmrig_is_alive, + xvb_is_alive, ); self.middle_panel(ctx, frame, key, p2pool_is_alive, xmrig_is_alive); } diff --git a/src/app/mod.rs b/src/app/mod.rs index 128f5e23..57172196 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -37,6 +37,7 @@ use crate::APP_DEFAULT_WIDTH; use crate::GUPAX_VERSION; use crate::OS; use eframe::CreationContext; +use egui::vec2; use egui::Vec2; use log::debug; use log::error; @@ -61,9 +62,8 @@ pub mod resize; // actual inner state of the tab settings. pub struct App { // Misc state - pub tab: Tab, // What tab are we on? - pub width: f32, // Top-level width - pub height: f32, // Top-level height + pub tab: Tab, // What tab are we on? + pub size: Vec2, // Top-level width and Top-level height // Alpha (transparency) // This value is used to incrementally increase/decrease // the transparency when resizing. Basically, it fades @@ -102,6 +102,7 @@ pub struct App { pub pub_sys: Arc>, // [Sys] state, read by [Status], mutated by [Helper] pub p2pool: Arc>, // [P2Pool] process state pub xmrig: Arc>, // [XMRig] process state + pub xvb: Arc>, // [Xvb] process state pub p2pool_api: Arc>, // Public ready-to-print P2Pool API made by the "helper" thread pub xmrig_api: Arc>, // Public ready-to-print XMRig API made by the "helper" thread pub xvb_api: Arc>, // Public XvB API @@ -181,6 +182,11 @@ impl App { String::new(), PathBuf::new() )); + let xvb = arc_mut!(Process::new( + ProcessName::Xvb, + String::new(), + PathBuf::new() + )); let p2pool_api = arc_mut!(PubP2poolApi::new()); let xmrig_api = arc_mut!(PubXmrigApi::new()); let xvb_api = arc_mut!(PubXvbApi::new()); @@ -220,8 +226,7 @@ impl App { let mut app = Self { tab: Tab::default(), ping: arc_mut!(Ping::new()), - width: APP_DEFAULT_WIDTH, - height: APP_DEFAULT_HEIGHT, + size: vec2(APP_DEFAULT_WIDTH, APP_DEFAULT_HEIGHT), must_resize: false, og: arc_mut!(State::new()), state: State::new(), @@ -244,6 +249,7 @@ impl App { pub_sys.clone(), p2pool.clone(), xmrig.clone(), + xvb.clone(), p2pool_api.clone(), xmrig_api.clone(), xvb_api.clone(), @@ -253,6 +259,7 @@ impl App { )), p2pool, xmrig, + xvb, p2pool_api, xvb_api, xmrig_api, diff --git a/src/app/panels/bottom.rs b/src/app/panels/bottom.rs index 80cfdc80..a0784e1a 100644 --- a/src/app/panels/bottom.rs +++ b/src/app/panels/bottom.rs @@ -14,38 +14,45 @@ use egui::TextStyle::Name; use egui::*; use log::debug; +use crate::helper::ProcessState::*; use crate::{app::Tab, utils::constants::SPACE}; impl crate::app::App { + #[allow(clippy::too_many_arguments)] pub fn bottom_panel( &mut self, ctx: &egui::Context, p2pool_state: ProcessState, xmrig_state: ProcessState, + xvb_state: ProcessState, key: &KeyPressed, wants_input: bool, p2pool_is_waiting: bool, xmrig_is_waiting: bool, + xvb_is_waiting: bool, p2pool_is_alive: bool, xmrig_is_alive: bool, + xvb_is_alive: bool, ) { // Bottom: app info + state/process buttons debug!("App | Rendering BOTTOM bar"); TopBottomPanel::bottom("bottom").show(ctx, |ui| { - let height = self.height / 22.0; + let height = self.size.y / 22.0; + // let width = self.size.x; ui.style_mut().override_text_style = Some(Name("Bottom".into())); ui.horizontal(|ui| { ui.group(|ui| { - let width = ((self.width / 2.0) / 4.0) - (SPACE * 2.0); + let width = ((self.size.x / 2.0) / 4.0) - (SPACE * 2.0); + let size = vec2(width, height); // [Gupax Version] // Is yellow if the user updated and should (but isn't required to) restart. match *lock!(self.restart) { Restart::Yes => ui .add_sized( - [width, height], + size, Label::new(RichText::new(&self.name_version).color(YELLOW)), ) .on_hover_text(GUPAX_SHOULD_RESTART), - _ => ui.add_sized([width, height], Label::new(&self.name_version)), + _ => ui.add_sized(size, Label::new(&self.name_version)), }; ui.separator(); // [OS] @@ -54,456 +61,571 @@ impl crate::app::App { // [App] is initialized, so no reason to check here. #[cfg(target_os = "windows")] if self.admin { - ui.add_sized([width, height], Label::new(self.os)); + ui.add_sized(size, Label::new(self.os)); } else { - ui.add_sized( - [width, height], - Label::new(RichText::new(self.os).color(RED)), - ) - .on_hover_text(WINDOWS_NOT_ADMIN); + ui.add_sized(size, Label::new(RichText::new(self.os).color(RED))) + .on_hover_text(WINDOWS_NOT_ADMIN); } #[cfg(target_family = "unix")] - ui.add_sized([width, height], Label::new(self.os)); + // [P2Pool/XMRig/XvB] Status + ui.add_sized(size, Label::new(self.os)); ui.separator(); - // [P2Pool/XMRig] Status - use crate::helper::ProcessState::*; - match p2pool_state { - Alive => ui - .add_sized( - [width, height], - Label::new(RichText::new("P2Pool ⏺").color(GREEN)), - ) - .on_hover_text(P2POOL_ALIVE), - Dead => ui - .add_sized( - [width, height], - Label::new(RichText::new("P2Pool ⏺").color(GRAY)), - ) - .on_hover_text(P2POOL_DEAD), - Failed => ui - .add_sized( - [width, height], - Label::new(RichText::new("P2Pool ⏺").color(RED)), - ) - .on_hover_text(P2POOL_FAILED), - Syncing => ui - .add_sized( - [width, height], - Label::new(RichText::new("P2Pool ⏺").color(ORANGE)), - ) - .on_hover_text(P2POOL_SYNCING), - Middle | Waiting | NotMining => ui - .add_sized( - [width, height], - Label::new(RichText::new("P2Pool ⏺").color(YELLOW)), - ) - .on_hover_text(P2POOL_MIDDLE), - }; + status_p2pool(p2pool_state, ui, size); ui.separator(); - match xmrig_state { - Alive => ui - .add_sized( - [width, height], - Label::new(RichText::new("XMRig ⏺").color(GREEN)), - ) - .on_hover_text(XMRIG_ALIVE), - Dead => ui - .add_sized( - [width, height], - Label::new(RichText::new("XMRig ⏺").color(GRAY)), - ) - .on_hover_text(XMRIG_DEAD), - Failed => ui - .add_sized( - [width, height], - Label::new(RichText::new("XMRig ⏺").color(RED)), - ) - .on_hover_text(XMRIG_FAILED), - NotMining => ui - .add_sized( - [width, height], - Label::new(RichText::new("XMRig ⏺").color(ORANGE)), - ) - .on_hover_text(XMRIG_NOT_MINING), - Middle | Waiting | Syncing => ui - .add_sized( - [width, height], - Label::new(RichText::new("XMRig ⏺").color(YELLOW)), - ) - .on_hover_text(XMRIG_MIDDLE), - }; + status_xmrig(xmrig_state, ui, size); + ui.separator(); + status_xvb(xvb_state, ui, size); }); - // [Save/Reset] ui.with_layout(Layout::right_to_left(Align::RIGHT), |ui| { let width = (ui.available_width() / 3.0) - (SPACE * 3.0); - ui.group(|ui| { - ui.set_enabled(self.diff); - let width = width / 2.0; - if key.is_r() && !wants_input && self.diff - || ui - .add_sized([width, height], Button::new("Reset")) - .on_hover_text("Reset changes") - .clicked() - { - let og = lock!(self.og).clone(); - self.state.status = og.status; - self.state.gupax = og.gupax; - self.state.p2pool = og.p2pool; - self.state.xmrig = og.xmrig; - self.node_vec = self.og_node_vec.clone(); - self.pool_vec = self.og_pool_vec.clone(); - } - if key.is_s() && !wants_input && self.diff - || ui - .add_sized([width, height], Button::new("Save")) - .on_hover_text("Save changes") - .clicked() - { - match State::save(&mut self.state, &self.state_path) { - Ok(_) => { - let mut og = lock!(self.og); - og.status = self.state.status.clone(); - og.gupax = self.state.gupax.clone(); - og.p2pool = self.state.p2pool.clone(); - og.xmrig = self.state.xmrig.clone(); - } - Err(e) => { - self.error_state.set( - format!("State file: {}", e), - ErrorFerris::Error, - ErrorButtons::Okay, - ); - } - }; - match Node::save(&self.node_vec, &self.node_path) { - Ok(_) => self.og_node_vec = self.node_vec.clone(), - Err(e) => self.error_state.set( - format!("Node list: {}", e), - ErrorFerris::Error, - ErrorButtons::Okay, - ), - }; - match Pool::save(&self.pool_vec, &self.pool_path) { - Ok(_) => self.og_pool_vec = self.pool_vec.clone(), - Err(e) => self.error_state.set( - format!("Pool list: {}", e), - ErrorFerris::Error, - ErrorButtons::Okay, - ), - }; - } - }); - + let size = vec2(width, height); + // [Save/Reset] + self.save_reset_ui(ui, size, key, wants_input); // [Simple/Advanced] + [Start/Stop/Restart] match self.tab { Tab::Status => { - ui.group(|ui| { - let width = (ui.available_width() / 3.0) - 14.0; - if ui - .add_sized( - [width, height], - SelectableLabel::new( - self.state.status.submenu == Submenu::Benchmarks, - "Benchmarks", - ), - ) - .on_hover_text(STATUS_SUBMENU_HASHRATE) - .clicked() - { - self.state.status.submenu = Submenu::Benchmarks; - } - ui.separator(); - if ui - .add_sized( - [width, height], - SelectableLabel::new( - self.state.status.submenu == Submenu::P2pool, - "P2Pool", - ), - ) - .on_hover_text(STATUS_SUBMENU_P2POOL) - .clicked() - { - self.state.status.submenu = Submenu::P2pool; - } - ui.separator(); - if ui - .add_sized( - [width, height], - SelectableLabel::new( - self.state.status.submenu == Submenu::Processes, - "Processes", - ), - ) - .on_hover_text(STATUS_SUBMENU_PROCESSES) - .clicked() - { - self.state.status.submenu = Submenu::Processes; - } - }); + self.status_submenu(ui, height); } Tab::Gupax => { - ui.group(|ui| { - let width = (ui.available_width() / 2.0) - 10.5; - if ui - .add_sized( - [width, height], - SelectableLabel::new(!self.state.gupax.simple, "Advanced"), - ) - .on_hover_text(GUPAX_ADVANCED) - .clicked() - { - self.state.gupax.simple = false; - } - ui.separator(); - if ui - .add_sized( - [width, height], - SelectableLabel::new(self.state.gupax.simple, "Simple"), - ) - .on_hover_text(GUPAX_SIMPLE) - .clicked() - { - self.state.gupax.simple = true; - } - }); + self.gupax_submenu(ui, height); } Tab::P2pool => { - ui.group(|ui| { - let width = width / 1.5; - if ui - .add_sized( - [width, height], - SelectableLabel::new(!self.state.p2pool.simple, "Advanced"), - ) - .on_hover_text(P2POOL_ADVANCED) - .clicked() - { - self.state.p2pool.simple = false; - } - ui.separator(); - if ui - .add_sized( - [width, height], - SelectableLabel::new(self.state.p2pool.simple, "Simple"), - ) - .on_hover_text(P2POOL_SIMPLE) - .clicked() - { - self.state.p2pool.simple = true; - } - }); - ui.group(|ui| { - let width = (ui.available_width() / 3.0) - 5.0; - if p2pool_is_waiting { - ui.add_enabled_ui(false, |ui| { - ui.add_sized([width, height], Button::new("⟲")) - .on_disabled_hover_text(P2POOL_MIDDLE); - ui.add_sized([width, height], Button::new("⏹")) - .on_disabled_hover_text(P2POOL_MIDDLE); - ui.add_sized([width, height], Button::new("▶")) - .on_disabled_hover_text(P2POOL_MIDDLE); - }); - } else if p2pool_is_alive { - if key.is_up() && !wants_input - || ui - .add_sized([width, height], Button::new("⟲")) - .on_hover_text("Restart P2Pool") - .clicked() - { - let _ = lock!(self.og).update_absolute_path(); - let _ = self.state.update_absolute_path(); - Helper::restart_p2pool( - &self.helper, - &self.state.p2pool, - &self.state.gupax.absolute_p2pool_path, - self.gather_backup_hosts(), - ); - } - if key.is_down() && !wants_input - || ui - .add_sized([width, height], Button::new("⏹")) - .on_hover_text("Stop P2Pool") - .clicked() - { - Helper::stop_p2pool(&self.helper); - } - ui.add_enabled_ui(false, |ui| { - ui.add_sized([width, height], Button::new("▶")) - .on_disabled_hover_text("Start P2Pool"); - }); - } else { - ui.add_enabled_ui(false, |ui| { - ui.add_sized([width, height], Button::new("⟲")) - .on_disabled_hover_text("Restart P2Pool"); - ui.add_sized([width, height], Button::new("⏹")) - .on_disabled_hover_text("Stop P2Pool"); - }); - // Check if address is okay before allowing to start. - let mut text = String::new(); - let mut ui_enabled = true; - if !Regexes::addr_ok(&self.state.p2pool.address) { - ui_enabled = false; - text = format!("Error: {}", P2POOL_ADDRESS); - } else if !Gupax::path_is_file(&self.state.gupax.p2pool_path) { - ui_enabled = false; - text = format!("Error: {}", P2POOL_PATH_NOT_FILE); - } else if !crate::components::update::check_p2pool_path( - &self.state.gupax.p2pool_path, - ) { - ui_enabled = false; - text = format!("Error: {}", P2POOL_PATH_NOT_VALID); - } - ui.set_enabled(ui_enabled); - let color = if ui_enabled { GREEN } else { RED }; - if (ui_enabled && key.is_up() && !wants_input) - || ui - .add_sized( - [width, height], - Button::new(RichText::new("▶").color(color)), - ) - .on_hover_text("Start P2Pool") - .on_disabled_hover_text(text) - .clicked() - { - let _ = lock!(self.og).update_absolute_path(); - let _ = self.state.update_absolute_path(); - Helper::start_p2pool( - &self.helper, - &self.state.p2pool, - &self.state.gupax.absolute_p2pool_path, - self.gather_backup_hosts(), - ); - } - } - }); + self.p2pool_submenu(ui, size); + self.p2pool_run_actions( + ui, + height, + p2pool_is_waiting, + p2pool_is_alive, + wants_input, + key, + ); } Tab::Xmrig => { - ui.group(|ui| { - let width = width / 1.5; - if ui - .add_sized( - [width, height], - SelectableLabel::new(!self.state.xmrig.simple, "Advanced"), - ) - .on_hover_text(XMRIG_ADVANCED) - .clicked() - { - self.state.xmrig.simple = false; - } - ui.separator(); - if ui - .add_sized( - [width, height], - SelectableLabel::new(self.state.xmrig.simple, "Simple"), - ) - .on_hover_text(XMRIG_SIMPLE) - .clicked() - { - self.state.xmrig.simple = true; - } - }); - ui.group(|ui| { - let width = (ui.available_width() / 3.0) - 5.0; - if xmrig_is_waiting { - ui.add_enabled_ui(false, |ui| { - ui.add_sized([width, height], Button::new("⟲")) - .on_disabled_hover_text(XMRIG_MIDDLE); - ui.add_sized([width, height], Button::new("⏹")) - .on_disabled_hover_text(XMRIG_MIDDLE); - ui.add_sized([width, height], Button::new("▶")) - .on_disabled_hover_text(XMRIG_MIDDLE); - }); - } else if xmrig_is_alive { - if key.is_up() && !wants_input - || ui - .add_sized([width, height], Button::new("⟲")) - .on_hover_text("Restart XMRig") - .clicked() - { - let _ = lock!(self.og).update_absolute_path(); - let _ = self.state.update_absolute_path(); - if cfg!(windows) { - Helper::restart_xmrig( - &self.helper, - &self.state.xmrig, - &self.state.gupax.absolute_xmrig_path, - Arc::clone(&self.sudo), - ); - } else { - lock!(self.sudo).signal = ProcessSignal::Restart; - self.error_state.ask_sudo(&self.sudo); - } - } - if key.is_down() && !wants_input - || ui - .add_sized([width, height], Button::new("⏹")) - .on_hover_text("Stop XMRig") - .clicked() - { - if cfg!(target_os = "macos") { - lock!(self.sudo).signal = ProcessSignal::Stop; - self.error_state.ask_sudo(&self.sudo); - } else { - Helper::stop_xmrig(&self.helper); - } - } - ui.add_enabled_ui(false, |ui| { - ui.add_sized([width, height], Button::new("▶")) - .on_disabled_hover_text("Start XMRig"); - }); - } else { - ui.add_enabled_ui(false, |ui| { - ui.add_sized([width, height], Button::new("⟲")) - .on_disabled_hover_text("Restart XMRig"); - ui.add_sized([width, height], Button::new("⏹")) - .on_disabled_hover_text("Stop XMRig"); - }); - let mut text = String::new(); - let mut ui_enabled = true; - if !Gupax::path_is_file(&self.state.gupax.xmrig_path) { - ui_enabled = false; - text = format!("Error: {}", XMRIG_PATH_NOT_FILE); - } else if !crate::components::update::check_xmrig_path( - &self.state.gupax.xmrig_path, - ) { - ui_enabled = false; - text = format!("Error: {}", XMRIG_PATH_NOT_VALID); - } - ui.set_enabled(ui_enabled); - let color = if ui_enabled { GREEN } else { RED }; - if (ui_enabled && key.is_up() && !wants_input) - || ui - .add_sized( - [width, height], - Button::new(RichText::new("▶").color(color)), - ) - .on_hover_text("Start XMRig") - .on_disabled_hover_text(text) - .clicked() - { - let _ = lock!(self.og).update_absolute_path(); - let _ = self.state.update_absolute_path(); - if cfg!(windows) { - Helper::start_xmrig( - &self.helper, - &self.state.xmrig, - &self.state.gupax.absolute_xmrig_path, - Arc::clone(&self.sudo), - ); - } else if cfg!(unix) { - lock!(self.sudo).signal = ProcessSignal::Start; - self.error_state.ask_sudo(&self.sudo); - } - } - } - }); + self.xmrig_submenu(ui, size); + self.xmrig_run_actions( + ui, + height, + xmrig_is_waiting, + xmrig_is_alive, + key, + wants_input, + ); } - _ => (), + Tab::Xvb => self.xvb_run_actions( + ui, + height, + xvb_is_waiting, + xvb_is_alive, + key, + wants_input, + ), + Tab::About => {} } }); }); }); } + fn save_reset_ui(&mut self, ui: &mut Ui, size: Vec2, key: &KeyPressed, wants_input: bool) { + ui.group(|ui| { + ui.set_enabled(self.diff); + let width = size.x / 2.0; + let size = vec2(width, size.y); + if key.is_r() && !wants_input && self.diff + || ui + .add_sized(size, Button::new("Reset")) + .on_hover_text("Reset changes") + .clicked() + { + let og = lock!(self.og).clone(); + self.state.status = og.status; + self.state.gupax = og.gupax; + self.state.p2pool = og.p2pool; + self.state.xmrig = og.xmrig; + self.state.xvb = og.xvb; + self.node_vec = self.og_node_vec.clone(); + self.pool_vec = self.og_pool_vec.clone(); + } + if key.is_s() && !wants_input && self.diff + || ui + .add_sized(size, Button::new("Save")) + .on_hover_text("Save changes") + .clicked() + { + match State::save(&mut self.state, &self.state_path) { + Ok(_) => { + let mut og = lock!(self.og); + og.status = self.state.status.clone(); + og.gupax = self.state.gupax.clone(); + og.p2pool = self.state.p2pool.clone(); + og.xmrig = self.state.xmrig.clone(); + og.xvb = self.state.xvb.clone(); + } + Err(e) => { + self.error_state.set( + format!("State file: {}", e), + ErrorFerris::Error, + ErrorButtons::Okay, + ); + } + }; + match Node::save(&self.node_vec, &self.node_path) { + Ok(_) => self.og_node_vec = self.node_vec.clone(), + Err(e) => self.error_state.set( + format!("Node list: {}", e), + ErrorFerris::Error, + ErrorButtons::Okay, + ), + }; + match Pool::save(&self.pool_vec, &self.pool_path) { + Ok(_) => self.og_pool_vec = self.pool_vec.clone(), + Err(e) => self.error_state.set( + format!("Pool list: {}", e), + ErrorFerris::Error, + ErrorButtons::Okay, + ), + }; + } + }); + } + fn status_submenu(&mut self, ui: &mut Ui, height: f32) { + ui.group(|ui| { + let width = (ui.available_width() / 3.0) - 14.0; + let size = vec2(width, height); + if ui + .add_sized( + size, + SelectableLabel::new( + self.state.status.submenu == Submenu::Benchmarks, + "Benchmarks", + ), + ) + .on_hover_text(STATUS_SUBMENU_HASHRATE) + .clicked() + { + self.state.status.submenu = Submenu::Benchmarks; + } + ui.separator(); + if ui + .add_sized( + size, + SelectableLabel::new(self.state.status.submenu == Submenu::P2pool, "P2Pool"), + ) + .on_hover_text(STATUS_SUBMENU_P2POOL) + .clicked() + { + self.state.status.submenu = Submenu::P2pool; + } + ui.separator(); + if ui + .add_sized( + size, + SelectableLabel::new( + self.state.status.submenu == Submenu::Processes, + "Processes", + ), + ) + .on_hover_text(STATUS_SUBMENU_PROCESSES) + .clicked() + { + self.state.status.submenu = Submenu::Processes; + } + }); + } + fn gupax_submenu(&mut self, ui: &mut Ui, height: f32) { + ui.group(|ui| { + let width = (ui.available_width() / 2.0) - 10.5; + let size = vec2(width, height); + if ui + .add_sized( + size, + SelectableLabel::new(!self.state.gupax.simple, "Advanced"), + ) + .on_hover_text(GUPAX_ADVANCED) + .clicked() + { + self.state.gupax.simple = false; + } + ui.separator(); + if ui + .add_sized( + size, + SelectableLabel::new(self.state.gupax.simple, "Simple"), + ) + .on_hover_text(GUPAX_SIMPLE) + .clicked() + { + self.state.gupax.simple = true; + } + }); + } + fn p2pool_submenu(&mut self, ui: &mut Ui, size: Vec2) { + ui.group(|ui| { + let width = size.x / 1.5; + let size = vec2(width, size.y); + if ui + .add_sized( + size, + SelectableLabel::new(!self.state.p2pool.simple, "Advanced"), + ) + .on_hover_text(P2POOL_ADVANCED) + .clicked() + { + self.state.p2pool.simple = false; + } + ui.separator(); + if ui + .add_sized( + size, + SelectableLabel::new(self.state.p2pool.simple, "Simple"), + ) + .on_hover_text(P2POOL_SIMPLE) + .clicked() + { + self.state.p2pool.simple = true; + } + }); + } + fn p2pool_run_actions( + &mut self, + ui: &mut Ui, + height: f32, + p2pool_is_waiting: bool, + p2pool_is_alive: bool, + wants_input: bool, + key: &KeyPressed, + ) { + ui.group(|ui| { + let width = (ui.available_width() / 3.0) - 5.0; + let size = vec2(width, height); + if p2pool_is_waiting { + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("⟲")) + .on_disabled_hover_text(P2POOL_MIDDLE); + ui.add_sized(size, Button::new("⏹")) + .on_disabled_hover_text(P2POOL_MIDDLE); + ui.add_sized(size, Button::new("▶")) + .on_disabled_hover_text(P2POOL_MIDDLE); + }); + } else if p2pool_is_alive { + if key.is_up() && !wants_input + || ui + .add_sized(size, Button::new("⟲")) + .on_hover_text("Restart P2Pool") + .clicked() + { + let _ = lock!(self.og).update_absolute_path(); + let _ = self.state.update_absolute_path(); + Helper::restart_p2pool( + &self.helper, + &self.state.p2pool, + &self.state.gupax.absolute_p2pool_path, + self.gather_backup_hosts(), + ); + } + if key.is_down() && !wants_input + || ui + .add_sized(size, Button::new("⏹")) + .on_hover_text("Stop P2Pool") + .clicked() + { + Helper::stop_p2pool(&self.helper); + } + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("▶")) + .on_disabled_hover_text("Start P2Pool"); + }); + } else { + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("⟲")) + .on_disabled_hover_text("Restart P2Pool"); + ui.add_sized(size, Button::new("⏹")) + .on_disabled_hover_text("Stop P2Pool"); + }); + // Check if address is okay before allowing to start. + let mut text = String::new(); + let mut ui_enabled = true; + if !Regexes::addr_ok(&self.state.p2pool.address) { + ui_enabled = false; + text = format!("Error: {}", P2POOL_ADDRESS); + } else if !Gupax::path_is_file(&self.state.gupax.p2pool_path) { + ui_enabled = false; + text = format!("Error: {}", P2POOL_PATH_NOT_FILE); + } else if !crate::components::update::check_p2pool_path( + &self.state.gupax.p2pool_path, + ) { + ui_enabled = false; + text = format!("Error: {}", P2POOL_PATH_NOT_VALID); + } + ui.set_enabled(ui_enabled); + let color = if ui_enabled { GREEN } else { RED }; + if (ui_enabled && key.is_up() && !wants_input) + || ui + .add_sized(size, Button::new(RichText::new("▶").color(color))) + .on_hover_text("Start P2Pool") + .on_disabled_hover_text(text) + .clicked() + { + let _ = lock!(self.og).update_absolute_path(); + let _ = self.state.update_absolute_path(); + Helper::start_p2pool( + &self.helper, + &self.state.p2pool, + &self.state.gupax.absolute_p2pool_path, + self.gather_backup_hosts(), + ); + } + } + }); + } + fn xmrig_submenu(&mut self, ui: &mut Ui, size: Vec2) { + ui.group(|ui| { + let width = size.x / 1.5; + let size = vec2(width, size.y); + if ui + .add_sized( + size, + SelectableLabel::new(!self.state.xmrig.simple, "Advanced"), + ) + .on_hover_text(XMRIG_ADVANCED) + .clicked() + { + self.state.xmrig.simple = false; + } + ui.separator(); + if ui + .add_sized( + size, + SelectableLabel::new(self.state.xmrig.simple, "Simple"), + ) + .on_hover_text(XMRIG_SIMPLE) + .clicked() + { + self.state.xmrig.simple = true; + } + }); + } + fn xmrig_run_actions( + &mut self, + ui: &mut Ui, + height: f32, + xmrig_is_waiting: bool, + xmrig_is_alive: bool, + key: &KeyPressed, + wants_input: bool, + ) { + ui.group(|ui| { + let width = (ui.available_width() / 3.0) - 5.0; + let size = vec2(width, height); + if xmrig_is_waiting { + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("⟲")) + .on_disabled_hover_text(XMRIG_MIDDLE); + ui.add_sized(size, Button::new("⏹")) + .on_disabled_hover_text(XMRIG_MIDDLE); + ui.add_sized(size, Button::new("▶")) + .on_disabled_hover_text(XMRIG_MIDDLE); + }); + } else if xmrig_is_alive { + if key.is_up() && !wants_input + || ui + .add_sized(size, Button::new("⟲")) + .on_hover_text("Restart XMRig") + .clicked() + { + let _ = lock!(self.og).update_absolute_path(); + let _ = self.state.update_absolute_path(); + if cfg!(windows) { + Helper::restart_xmrig( + &self.helper, + &self.state.xmrig, + &self.state.gupax.absolute_xmrig_path, + Arc::clone(&self.sudo), + ); + } else { + lock!(self.sudo).signal = ProcessSignal::Restart; + self.error_state.ask_sudo(&self.sudo); + } + } + if key.is_down() && !wants_input + || ui + .add_sized(size, Button::new("⏹")) + .on_hover_text("Stop XMRig") + .clicked() + { + if cfg!(target_os = "macos") { + lock!(self.sudo).signal = ProcessSignal::Stop; + self.error_state.ask_sudo(&self.sudo); + } else { + Helper::stop_xmrig(&self.helper); + } + } + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("▶")) + .on_disabled_hover_text("Start XMRig"); + }); + } else { + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("⟲")) + .on_disabled_hover_text("Restart XMRig"); + ui.add_sized(size, Button::new("⏹")) + .on_disabled_hover_text("Stop XMRig"); + }); + let mut text = String::new(); + let mut ui_enabled = true; + if !Gupax::path_is_file(&self.state.gupax.xmrig_path) { + ui_enabled = false; + text = format!("Error: {}", XMRIG_PATH_NOT_FILE); + } else if !crate::components::update::check_xmrig_path(&self.state.gupax.xmrig_path) + { + ui_enabled = false; + text = format!("Error: {}", XMRIG_PATH_NOT_VALID); + } + ui.set_enabled(ui_enabled); + let color = if ui_enabled { GREEN } else { RED }; + if (ui_enabled && key.is_up() && !wants_input) + || ui + .add_sized(size, Button::new(RichText::new("▶").color(color))) + .on_hover_text("Start XMRig") + .on_disabled_hover_text(text) + .clicked() + { + let _ = lock!(self.og).update_absolute_path(); + let _ = self.state.update_absolute_path(); + if cfg!(windows) { + Helper::start_xmrig( + &self.helper, + &self.state.xmrig, + &self.state.gupax.absolute_xmrig_path, + Arc::clone(&self.sudo), + ); + } else if cfg!(unix) { + lock!(self.sudo).signal = ProcessSignal::Start; + self.error_state.ask_sudo(&self.sudo); + } + } + } + }); + } + fn xvb_run_actions( + &mut self, + ui: &mut Ui, + height: f32, + xvb_is_waiting: bool, + xvb_is_alive: bool, + key: &KeyPressed, + wants_input: bool, + ) { + ui.group(|ui| { + let width = (ui.available_width() / 3.0) - 5.0; + let size = vec2(width, height); + if xvb_is_waiting { + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("⟲")) + .on_disabled_hover_text(XVB_MIDDLE); + ui.add_sized(size, Button::new("⏹")) + .on_disabled_hover_text(XVB_MIDDLE); + ui.add_sized(size, Button::new("▶")) + .on_disabled_hover_text(XVB_MIDDLE); + }); + } else if xvb_is_alive { + if key.is_up() && !wants_input + || ui + .add_sized(size, Button::new("⟲")) + .on_hover_text("Restart Xvb") + .clicked() + { + Helper::restart_xvb(&self.helper, &self.state.xvb, &self.state.p2pool); + } + if key.is_down() && !wants_input + || ui + .add_sized(size, Button::new("⏹")) + .on_hover_text("Stop Xvb") + .clicked() + { + Helper::stop_xvb(&self.helper); + } + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("▶")) + .on_disabled_hover_text("Start Xvb"); + }); + } else { + ui.add_enabled_ui(false, |ui| { + ui.add_sized(size, Button::new("⟲")) + .on_disabled_hover_text("Restart Xvb"); + ui.add_sized(size, Button::new("⏹")) + .on_disabled_hover_text("Stop Xvb"); + }); + let ui_enabled = + !self.state.p2pool.address.is_empty() && !self.state.xvb.token.is_empty(); + ui.set_enabled(ui_enabled); + let color = if ui_enabled { GREEN } else { RED }; + if (ui_enabled && key.is_up() && !wants_input) + || ui + .add_sized(size, Button::new(RichText::new("▶").color(color))) + .on_hover_text("Start Xvb") + .on_disabled_hover_text(XVB_NOT_CONFIGURED) + .clicked() + { + Helper::start_xvb(&self.helper, &self.state.xvb, &self.state.p2pool); + } + } + }); + } +} + +fn status_p2pool(state: ProcessState, ui: &mut Ui, size: Vec2) { + match state { + Alive => ui + .add_sized(size, Label::new(RichText::new("P2Pool ⏺").color(GREEN))) + .on_hover_text(P2POOL_ALIVE), + Dead => ui + .add_sized(size, Label::new(RichText::new("P2Pool ⏺").color(GRAY))) + .on_hover_text(P2POOL_DEAD), + Failed => ui + .add_sized(size, Label::new(RichText::new("P2Pool ⏺").color(RED))) + .on_hover_text(P2POOL_FAILED), + Syncing => ui + .add_sized(size, Label::new(RichText::new("P2Pool ⏺").color(ORANGE))) + .on_hover_text(P2POOL_SYNCING), + Middle | Waiting | NotMining => ui + .add_sized(size, Label::new(RichText::new("P2Pool ⏺").color(YELLOW))) + .on_hover_text(P2POOL_MIDDLE), + }; +} + +fn status_xmrig(state: ProcessState, ui: &mut Ui, size: Vec2) { + match state { + Alive => ui + .add_sized(size, Label::new(RichText::new("XMRig ⏺").color(GREEN))) + .on_hover_text(XMRIG_ALIVE), + Dead => ui + .add_sized(size, Label::new(RichText::new("XMRig ⏺").color(GRAY))) + .on_hover_text(XMRIG_DEAD), + Failed => ui + .add_sized(size, Label::new(RichText::new("XMRig ⏺").color(RED))) + .on_hover_text(XMRIG_FAILED), + NotMining => ui + .add_sized(size, Label::new(RichText::new("XMRig ⏺").color(ORANGE))) + .on_hover_text(XMRIG_NOT_MINING), + Middle | Waiting | Syncing => ui + .add_sized(size, Label::new(RichText::new("XMRig ⏺").color(YELLOW))) + .on_hover_text(XMRIG_MIDDLE), + }; +} +fn status_xvb(state: ProcessState, ui: &mut Ui, size: Vec2) { + match state { + Alive => ui + .add_sized(size, Label::new(RichText::new("XvB ⏺").color(GREEN))) + .on_hover_text(XVB_ALIVE), + Dead => ui + .add_sized(size, Label::new(RichText::new("XvB ⏺").color(GRAY))) + .on_hover_text(XVB_DEAD), + Failed => ui + .add_sized(size, Label::new(RichText::new("XvB ⏺").color(RED))) + .on_hover_text(XVB_FAILED), + Middle | Waiting | NotMining | Syncing => ui + .add_sized(size, Label::new(RichText::new("XvB ⏺").color(YELLOW))) + .on_hover_text(XVB_MIDDLE), + }; } diff --git a/src/app/panels/middle/gupax.rs b/src/app/panels/middle/gupax.rs index f82f73aa..c1743f0a 100644 --- a/src/app/panels/middle/gupax.rs +++ b/src/app/panels/middle/gupax.rs @@ -22,8 +22,7 @@ impl Gupax { file_window: &Arc>, error_state: &mut ErrorState, restart: &Arc>, - width: f32, - height: f32, + size: Vec2, _frame: &mut eframe::Frame, _ctx: &egui::Context, ui: &mut egui::Ui, @@ -32,16 +31,16 @@ impl Gupax { debug!("Gupax Tab | Rendering [Update] button + progress bar"); ui.group(|ui| { let button = if self.simple { - height / 5.0 + size.y / 5.0 } else { - height / 15.0 + size.y / 15.0 }; let height = if self.simple { - height / 5.0 + size.y / 5.0 } else { - height / 10.0 + size.y / 10.0 }; - let width = width - SPACE; + let width = size.x - SPACE; let updating = *lock2!(update, updating); ui.vertical(|ui| { // If [Gupax] is being built for a Linux distro, @@ -68,60 +67,53 @@ impl Gupax { let msg = format!("{}\n{}{}", *lock2!(update, msg), prog, "%"); ui.add_sized([width, height * 1.4], Label::new(RichText::new(msg))); let height = height / 2.0; + let size = vec2(width, height); if updating { - ui.add_sized([width, height], Spinner::new().size(height)); + ui.add_sized(size, Spinner::new().size(height)); } else { - ui.add_sized([width, height], Label::new("...")); + ui.add_sized(size, Label::new("...")); } - ui.add_sized( - [width, height], - ProgressBar::new(lock2!(update, prog).round() / 100.0), - ); + ui.add_sized(size, ProgressBar::new(lock2!(update, prog).round() / 100.0)); }); }); debug!("Gupax Tab | Rendering bool buttons"); ui.horizontal(|ui| { ui.group(|ui| { - let width = (width - SPACE * 12.0) / 6.0; + let width = (size.x - SPACE * 12.0) / 6.0; let height = if self.simple { - height / 10.0 + size.y / 10.0 } else { - height / 15.0 + size.y / 15.0 }; + let size = vec2(width, height); ui.style_mut().override_text_style = Some(egui::TextStyle::Small); ui.add_sized( - [width, height], + size, Checkbox::new(&mut self.update_via_tor, "Update via Tor"), ) .on_hover_text(GUPAX_UPDATE_VIA_TOR); ui.separator(); - ui.add_sized( - [width, height], - Checkbox::new(&mut self.auto_update, "Auto-Update"), - ) - .on_hover_text(GUPAX_AUTO_UPDATE); + ui.add_sized(size, Checkbox::new(&mut self.auto_update, "Auto-Update")) + .on_hover_text(GUPAX_AUTO_UPDATE); ui.separator(); - ui.add_sized( - [width, height], - Checkbox::new(&mut self.auto_p2pool, "Auto-P2Pool"), - ) - .on_hover_text(GUPAX_AUTO_P2POOL); + ui.add_sized(size, Checkbox::new(&mut self.auto_p2pool, "Auto-P2Pool")) + .on_hover_text(GUPAX_AUTO_P2POOL); ui.separator(); - ui.add_sized( - [width, height], - Checkbox::new(&mut self.auto_xmrig, "Auto-XMRig"), - ) - .on_hover_text(GUPAX_AUTO_XMRIG); + ui.add_sized(size, Checkbox::new(&mut self.auto_xmrig, "Auto-XMRig")) + .on_hover_text(GUPAX_AUTO_XMRIG); + ui.separator(); + ui.add_sized(size, Checkbox::new(&mut self.auto_xvb, "Auto-XvB")) + .on_hover_text(GUPAX_AUTO_XVB); ui.separator(); ui.add_sized( - [width, height], + size, Checkbox::new(&mut self.ask_before_quit, "Ask before quit"), ) .on_hover_text(GUPAX_ASK_BEFORE_QUIT); ui.separator(); ui.add_sized( - [width, height], + size, Checkbox::new(&mut self.save_before_quit, "Save before quit"), ) .on_hover_text(GUPAX_SAVE_BEFORE_QUIT); @@ -134,7 +126,7 @@ impl Gupax { debug!("Gupax Tab | Rendering P2Pool/XMRig path selection"); // P2Pool/XMRig binary path selection - let height = height / 28.0; + let height = size.y / 28.0; let text_edit = (ui.available_width() / 10.0) - SPACE; ui.group(|ui| { ui.add_sized( @@ -238,7 +230,8 @@ impl Gupax { // Saved [Tab] debug!("Gupax Tab | Rendering [Tab] selector"); ui.group(|ui| { - let width = (width / 5.0) - (SPACE * 1.93); + let width = (size.x / 5.0) - (SPACE * 1.93); + let size = vec2(width, height); ui.add_sized( [ui.available_width(), height / 2.0], Label::new(RichText::new("Default Tab").underline().color(LIGHT_GRAY)), @@ -247,10 +240,7 @@ impl Gupax { ui.separator(); ui.horizontal(|ui| { if ui - .add_sized( - [width, height], - SelectableLabel::new(self.tab == Tab::About, "About"), - ) + .add_sized(size, SelectableLabel::new(self.tab == Tab::About, "About")) .on_hover_text(GUPAX_TAB_ABOUT) .clicked() { @@ -259,7 +249,7 @@ impl Gupax { ui.separator(); if ui .add_sized( - [width, height], + size, SelectableLabel::new(self.tab == Tab::Status, "Status"), ) .on_hover_text(GUPAX_TAB_STATUS) @@ -269,10 +259,7 @@ impl Gupax { } ui.separator(); if ui - .add_sized( - [width, height], - SelectableLabel::new(self.tab == Tab::Gupax, "Gupax"), - ) + .add_sized(size, SelectableLabel::new(self.tab == Tab::Gupax, "Gupax")) .on_hover_text(GUPAX_TAB_GUPAX) .clicked() { @@ -281,7 +268,7 @@ impl Gupax { ui.separator(); if ui .add_sized( - [width, height], + size, SelectableLabel::new(self.tab == Tab::P2pool, "P2Pool"), ) .on_hover_text(GUPAX_TAB_P2POOL) @@ -291,10 +278,7 @@ impl Gupax { } ui.separator(); if ui - .add_sized( - [width, height], - SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig"), - ) + .add_sized(size, SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig")) .on_hover_text(GUPAX_TAB_XMRIG) .clicked() { @@ -317,7 +301,7 @@ impl Gupax { .on_hover_text(GUPAX_ADJUST); ui.separator(); ui.vertical(|ui| { - let width = width / 10.0; + let width = size.x / 10.0; ui.spacing_mut().icon_width = width / 25.0; ui.spacing_mut().slider_width = width * 7.6; match self.ratio { @@ -334,17 +318,18 @@ impl Gupax { } } let height = height / 3.5; + let size = vec2(width, height); ui.horizontal(|ui| { ui.set_enabled(self.ratio != Ratio::Height); ui.add_sized( - [width, height], + size, Label::new(format!( " Width [{}-{}]:", APP_MIN_WIDTH as u16, APP_MAX_WIDTH as u16 )), ); ui.add_sized( - [width, height], + size, Slider::new( &mut self.selected_width, APP_MIN_WIDTH as u16..=APP_MAX_WIDTH as u16, @@ -355,14 +340,14 @@ impl Gupax { ui.horizontal(|ui| { ui.set_enabled(self.ratio != Ratio::Width); ui.add_sized( - [width, height], + size, Label::new(format!( "Height [{}-{}]:", APP_MIN_HEIGHT as u16, APP_MAX_HEIGHT as u16 )), ); ui.add_sized( - [width, height], + size, Slider::new( &mut self.selected_height, APP_MIN_HEIGHT as u16..=APP_MAX_HEIGHT as u16, @@ -372,11 +357,11 @@ impl Gupax { }); ui.horizontal(|ui| { ui.add_sized( - [width, height], + size, Label::new(format!("Scaling [{APP_MIN_SCALE}..{APP_MAX_SCALE}]:")), ); ui.add_sized( - [width, height], + size, Slider::new(&mut self.selected_scale, APP_MIN_SCALE..=APP_MAX_SCALE) .step_by(0.1), ) @@ -388,10 +373,11 @@ impl Gupax { // Width/Height locks ui.horizontal(|ui| { use Ratio::*; - let width = (width / 4.0) - (SPACE * 1.5); + let width = (size.x / 4.0) - (SPACE * 1.5); + let size = vec2(width, height); if ui .add_sized( - [width, height], + size, SelectableLabel::new(self.ratio == Width, "Lock to width"), ) .on_hover_text(GUPAX_LOCK_WIDTH) @@ -402,7 +388,7 @@ impl Gupax { ui.separator(); if ui .add_sized( - [width, height], + size, SelectableLabel::new(self.ratio == Height, "Lock to height"), ) .on_hover_text(GUPAX_LOCK_HEIGHT) @@ -412,17 +398,14 @@ impl Gupax { } ui.separator(); if ui - .add_sized( - [width, height], - SelectableLabel::new(self.ratio == None, "No lock"), - ) + .add_sized(size, SelectableLabel::new(self.ratio == None, "No lock")) .on_hover_text(GUPAX_NO_LOCK) .clicked() { self.ratio = None; } if ui - .add_sized([width, height], Button::new("Set")) + .add_sized(size, Button::new("Set")) .on_hover_text(GUPAX_SET) .clicked() { diff --git a/src/app/panels/middle/mod.rs b/src/app/panels/middle/mod.rs index f881d11d..c08a61c9 100644 --- a/src/app/panels/middle/mod.rs +++ b/src/app/panels/middle/mod.rs @@ -24,8 +24,8 @@ impl crate::app::App { debug!("App | Rendering CENTRAL_PANEL (tab contents)"); CentralPanel::default().show(ctx, |ui| { // This sets the Ui dimensions after Top/Bottom are filled - self.width = ui.available_width(); - self.height = ui.available_height(); + self.size.x = ui.available_width(); + self.size.y = ui.available_height(); ui.style_mut().override_text_style = Some(TextStyle::Body); match self.tab { Tab::About => { @@ -85,8 +85,8 @@ path_xmr: {:#?}\n self.now.elapsed().as_secs_f32(), self.state.gupax.selected_width, self.state.gupax.selected_height, - self.width, - self.height, + self.size.x, + self.size.y, OS_NAME, self.max_threads, self.pid, @@ -117,24 +117,25 @@ path_xmr: {:#?}\n ); self.error_state.set(debug_info, ErrorFerris::Cute, ErrorButtons::Debug); } - let width = self.width; - let height = self.height/30.0; - let max_height = self.height; + let width = self.size.x; + let height = self.size.y/30.0; + let max_height = self.size.y; + let size = vec2(width, height); ui.add_space(10.0); ui.vertical_centered(|ui| { ui.set_max_height(max_height); // Display [Gupax] banner let link_width = width/14.0; ui.add_sized(Vec2::new(width, height*3.0), Image::from_bytes("bytes://banner.png", BYTES_BANNER)); - ui.add_sized([width, height], Label::new("is a GUI for mining")); + ui.add_sized(size, Label::new("is a GUI for mining")); ui.add_sized([link_width, height], Hyperlink::from_label_and_url("[Monero]", "https://www.github.com/monero-project/monero")); - ui.add_sized([width, height], Label::new("on")); + ui.add_sized(size, Label::new("on")); ui.add_sized([link_width, height], Hyperlink::from_label_and_url("[P2Pool]", "https://www.github.com/SChernykh/p2pool")); - ui.add_sized([width, height], Label::new("using")); + ui.add_sized(size, Label::new("using")); ui.add_sized([link_width, height], Hyperlink::from_label_and_url("[XMRig]", "https://www.github.com/xmrig/xmrig")); ui.add_space(SPACE*2.0); - ui.add_sized([width, height], Label::new(KEYBOARD_SHORTCUTS)); + ui.add_sized(size, Label::new(KEYBOARD_SHORTCUTS)); ui.add_space(SPACE*2.0); if cfg!(debug_assertions) { ui.label(format!("Gupax is running in debug mode - {}", self.now.elapsed().as_secs_f64())); } @@ -143,23 +144,23 @@ path_xmr: {:#?}\n } Tab::Status => { debug!("App | Entering [Status] Tab"); - crate::disk::state::Status::show(&mut self.state.status, &self.pub_sys, &self.p2pool_api, &self.xmrig_api, &self.xvb_api,&self.p2pool_img, &self.xmrig_img, p2pool_is_alive, xmrig_is_alive, self.max_threads, &self.gupax_p2pool_api, &self.benchmarks, self.width, self.height, ctx, ui); + crate::disk::state::Status::show(&mut self.state.status, &self.pub_sys, &self.p2pool_api, &self.xmrig_api, &self.xvb_api,&self.p2pool_img, &self.xmrig_img, p2pool_is_alive, xmrig_is_alive, self.max_threads, &self.gupax_p2pool_api, &self.benchmarks, self.size, ctx, ui); } Tab::Gupax => { debug!("App | Entering [Gupax] Tab"); - crate::disk::state::Gupax::show(&mut self.state.gupax, &self.og, &self.state_path, &self.update, &self.file_window, &mut self.error_state, &self.restart, self.width, self.height, frame, ctx, ui); + crate::disk::state::Gupax::show(&mut self.state.gupax, &self.og, &self.state_path, &self.update, &self.file_window, &mut self.error_state, &self.restart, self.size, frame, ctx, ui); } Tab::P2pool => { debug!("App | Entering [P2Pool] Tab"); - crate::disk::state::P2pool::show(&mut self.state.p2pool, &mut self.node_vec, &self.og, &self.ping, &self.p2pool, &self.p2pool_api, &mut self.p2pool_stdin, self.width, self.height, ctx, ui); + crate::disk::state::P2pool::show(&mut self.state.p2pool, &mut self.node_vec, &self.og, &self.ping, &self.p2pool, &self.p2pool_api, &mut self.p2pool_stdin, self.size, ctx, ui); } Tab::Xmrig => { debug!("App | Entering [XMRig] Tab"); - crate::disk::state::Xmrig::show(&mut self.state.xmrig, &mut self.pool_vec, &self.xmrig, &self.xmrig_api, &mut self.xmrig_stdin, self.width, self.height, ctx, ui); + crate::disk::state::Xmrig::show(&mut self.state.xmrig, &mut self.pool_vec, &self.xmrig, &self.xmrig_api, &mut self.xmrig_stdin, self.size, ctx, ui); } Tab::Xvb => { debug!("App | Entering [XvB] Tab"); - crate::disk::state::Xvb::show(self.width, self.height, ctx, ui); + crate::disk::state::Xvb::show(self.size, ctx, ui); } } }); diff --git a/src/app/panels/middle/p2pool/mod.rs b/src/app/panels/middle/p2pool/mod.rs index bef4b1fd..bd004c3b 100644 --- a/src/app/panels/middle/p2pool/mod.rs +++ b/src/app/panels/middle/p2pool/mod.rs @@ -18,7 +18,7 @@ use crate::helper::p2pool::PubP2poolApi; // You should have received a copy of the GNU General Public License // along with this program. If not, see . use crate::{components::node::*, constants::*, helper::*, macros::*, utils::regex::Regexes}; -use egui::{Color32, Label, RichText, TextEdit, TextStyle::*}; +use egui::{Color32, Label, RichText, TextEdit, TextStyle::*, Vec2}; use log::*; use std::sync::{Arc, Mutex}; @@ -37,18 +37,19 @@ impl P2pool { process: &Arc>, api: &Arc>, buffer: &mut String, - width: f32, - height: f32, + size: Vec2, _ctx: &egui::Context, ui: &mut egui::Ui, ) { - let text_edit = height / 25.0; + let height = size.y; + let width = size.x; + let text_edit = size.y / 25.0; //---------------------------------------------------------------------------------------------------- [Simple] Console debug!("P2Pool Tab | Rendering [Console]"); ui.group(|ui| { if self.simple { - let height = height / 2.8; - let width = width - SPACE; + let height = size.y / 2.8; + let width = size.x - SPACE; egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into())); egui::ScrollArea::vertical() diff --git a/src/app/panels/middle/status/benchmarks.rs b/src/app/panels/middle/status/benchmarks.rs index 622eae78..e423fad5 100644 --- a/src/app/panels/middle/status/benchmarks.rs +++ b/src/app/panels/middle/status/benchmarks.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex}; use crate::{ app::Benchmark, disk::state::Status, helper::xmrig::PubXmrigApi, utils::human::HumanNumber, }; -use egui::{Hyperlink, ProgressBar, Spinner}; +use egui::{Hyperlink, ProgressBar, Spinner, Vec2}; use crate::utils::macros::lock; @@ -13,22 +13,23 @@ use log::*; impl Status { pub(super) fn benchmarks( &mut self, - width: f32, - height: f32, + size: Vec2, ui: &mut egui::Ui, benchmarks: &[Benchmark], xmrig_alive: bool, xmrig_api: &Arc>, ) { debug!("Status Tab | Rendering [Benchmarks]"); - let text = height / 20.0; + let text = size.x / 20.0; let double = text * 2.0; - let log = height / 3.0; + let log = size.y / 3.0; + let width = size.x; + let height = size.y; // [0], The user's CPU (most likely). let cpu = &benchmarks[0]; ui.horizontal(|ui| { - let width = (width / 2.0) - (SPACE * 1.666); + let width = (size.x / 2.0) - (SPACE * 1.666); let min_height = log; ui.group(|ui| { ui.vertical(|ui| { diff --git a/src/app/panels/middle/status/mod.rs b/src/app/panels/middle/status/mod.rs index e499f2c2..335a19eb 100644 --- a/src/app/panels/middle/status/mod.rs +++ b/src/app/panels/middle/status/mod.rs @@ -15,6 +15,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use egui::Vec2; + use crate::{ app::Benchmark, disk::{gupax_p2pool_api::GupaxP2poolApi, state::Status, status::*}, @@ -47,8 +49,7 @@ impl Status { max_threads: usize, gupax_p2pool_api: &Arc>, benchmarks: &[Benchmark], - width: f32, - height: f32, + size: Vec2, _ctx: &egui::Context, ui: &mut egui::Ui, ) { @@ -56,8 +57,7 @@ impl Status { if self.submenu == Submenu::Processes { self.processes( sys, - width, - height, + size, ui, p2pool_alive, p2pool_api, @@ -70,17 +70,10 @@ impl Status { ); //---------------------------------------------------------------------------------------------------- [P2Pool] } else if self.submenu == Submenu::P2pool { - self.p2pool( - width, - height, - ui, - gupax_p2pool_api, - p2pool_alive, - p2pool_api, - ); + self.p2pool(size, ui, gupax_p2pool_api, p2pool_alive, p2pool_api); //---------------------------------------------------------------------------------------------------- [Benchmarks] } else if self.submenu == Submenu::Benchmarks { - self.benchmarks(width, height, ui, benchmarks, xmrig_alive, xmrig_api) + self.benchmarks(size, ui, benchmarks, xmrig_alive, xmrig_api) } } } diff --git a/src/app/panels/middle/status/p2pool.rs b/src/app/panels/middle/status/p2pool.rs index 4cffe851..afc2f681 100644 --- a/src/app/panels/middle/status/p2pool.rs +++ b/src/app/panels/middle/status/p2pool.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, Mutex}; -use egui::{Label, RichText, SelectableLabel, Slider, TextEdit}; +use egui::{Label, RichText, SelectableLabel, Slider, TextEdit, Vec2}; use crate::{ disk::{ @@ -15,14 +15,15 @@ use crate::{ impl Status { pub fn p2pool( &mut self, - width: f32, - height: f32, + size: Vec2, ui: &mut egui::Ui, gupax_p2pool_api: &Arc>, p2pool_alive: bool, p2pool_api: &Arc>, ) { let api = lock!(gupax_p2pool_api); + let height = size.y; + let width = size.x; let text = height / 25.0; let log = height / 2.8; // Payout Text + PayoutView buttons diff --git a/src/app/panels/middle/status/processes.rs b/src/app/panels/middle/status/processes.rs index 484e047b..53918c60 100644 --- a/src/app/panels/middle/status/processes.rs +++ b/src/app/panels/middle/status/processes.rs @@ -1,4 +1,4 @@ -use egui::{ScrollArea, Ui}; +use egui::{ScrollArea, Ui, Vec2}; use std::sync::{Arc, Mutex}; use crate::disk::state::Status; @@ -17,8 +17,7 @@ impl Status { pub(super) fn processes( &mut self, sys: &Arc>, - width: f32, - height: f32, + size: Vec2, ui: &mut egui::Ui, p2pool_alive: bool, p2pool_api: &Arc>, @@ -29,9 +28,9 @@ impl Status { xvb_api: &Arc>, max_threads: usize, ) { - let width = (width / 4.0) - (SPACE * 1.7500); - let min_height = height - SPACE; - let height = height / 25.0; + let width = (size.x / 4.0) - (SPACE * 1.7500); + let min_height = size.y - SPACE; + let height = size.y / 25.0; ui.horizontal(|ui| { // [Gupax] gupax(ui, min_height, width, height, sys); @@ -261,6 +260,7 @@ fn p2pool( }) }); } +#[allow(clippy::too_many_arguments)] fn xmrig( ui: &mut Ui, min_height: f32, @@ -432,14 +432,14 @@ fn xvb(ui: &mut Ui, min_height: f32, width: f32, height: f32, xvb_api: &Arc>, api: &Arc>, buffer: &mut String, - width: f32, - height: f32, + size: Vec2, _ctx: &egui::Context, ui: &mut egui::Ui, ) { - let text_edit = height / 25.0; + let text_edit = size.y / 25.0; //---------------------------------------------------------------------------------------------------- [Simple] Console debug!("XMRig Tab | Rendering [Console]"); ui.group(|ui| { if self.simple { - let height = height / 1.5; - let width = width - SPACE; + let height = size.y / 1.5; + let width = size.x - SPACE; + let size = vec2(width, height); egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into())); egui::ScrollArea::vertical() @@ -59,15 +60,16 @@ impl Xmrig { .auto_shrink([false; 2]) .show_viewport(ui, |ui, _| { ui.add_sized( - [width, height], + size, TextEdit::multiline(&mut lock!(api).output.as_str()), ); }); }); //---------------------------------------------------------------------------------------------------- [Advanced] Console } else { - let height = height / 2.8; - let width = width - SPACE; + let height = size.y / 2.8; + let width = size.x - SPACE; + let size = vec2(width, height); egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| { ui.style_mut().override_text_style = Some(Name("MonospaceSmall".into())); egui::ScrollArea::vertical() @@ -77,7 +79,7 @@ impl Xmrig { .auto_shrink([false; 2]) .show_viewport(ui, |ui, _| { ui.add_sized( - [width, height], + size, TextEdit::multiline(&mut lock!(api).output.as_str()), ); }); @@ -109,7 +111,7 @@ impl Xmrig { debug!("XMRig Tab | Rendering [Arguments]"); ui.group(|ui| { ui.horizontal(|ui| { - let width = (width / 10.0) - SPACE; + let width = (size.x / 10.0) - SPACE; ui.add_sized([width, text_edit], Label::new("Command arguments:")); ui.add_sized( [ui.available_width(), text_edit], @@ -126,7 +128,7 @@ impl Xmrig { //---------------------------------------------------------------------------------------------------- Address debug!("XMRig Tab | Rendering [Address]"); ui.group(|ui| { - let width = width - SPACE; + let width = size.x - SPACE; ui.spacing_mut().text_edit_width = (width) - (SPACE * 3.0); let text; let color; @@ -160,7 +162,7 @@ impl Xmrig { } debug!("XMRig Tab | Rendering [Threads]"); ui.vertical(|ui| { - let width = width / 10.0; + let width = size.x / 10.0; let text_width = width * 2.4; ui.spacing_mut().slider_width = width * 6.5; ui.spacing_mut().icon_width = width / 25.0; @@ -471,22 +473,17 @@ impl Xmrig { ui.horizontal(|ui| { let width = (ui.available_width() / 2.0) - 11.0; let height = text_edit * 2.0; + let size = vec2(width, height); // let mut style = (*ctx.style()).clone(); // style.spacing.icon_width_inner = width / 8.0; // style.spacing.icon_width = width / 6.0; // style.spacing.icon_spacing = 20.0; // ctx.set_style(style); - ui.add_sized( - [width, height], - Checkbox::new(&mut self.tls, "TLS Connection"), - ) - .on_hover_text(XMRIG_TLS); + ui.add_sized(size, Checkbox::new(&mut self.tls, "TLS Connection")) + .on_hover_text(XMRIG_TLS); ui.separator(); - ui.add_sized( - [width, height], - Checkbox::new(&mut self.keepalive, "Keepalive"), - ) - .on_hover_text(XMRIG_KEEPALIVE); + ui.add_sized(size, Checkbox::new(&mut self.keepalive, "Keepalive")) + .on_hover_text(XMRIG_KEEPALIVE); }); }); }); diff --git a/src/app/panels/middle/xvb.rs b/src/app/panels/middle/xvb.rs index 52446279..f0709ef5 100644 --- a/src/app/panels/middle/xvb.rs +++ b/src/app/panels/middle/xvb.rs @@ -1,12 +1,12 @@ -use egui::{Hyperlink, Image}; +use egui::{Hyperlink, Image, Vec2}; use crate::constants::{BYTES_XVB, SPACE}; impl crate::disk::state::Xvb { #[inline(always)] // called once - pub fn show(width: f32, height: f32, _ctx: &egui::Context, ui: &mut egui::Ui) { - let website_height = height / 10.0; - let width = width - SPACE; + pub fn show(size: Vec2, _ctx: &egui::Context, ui: &mut egui::Ui) { + let website_height = size.y / 10.0; + let width = size.x - SPACE; // ui.add_sized( // [width, website_height], // Hyperlink::from_label_and_url("XMRvsBeast", "https://xmrvsbeast.com"), diff --git a/src/app/panels/quit_error.rs b/src/app/panels/quit_error.rs index cef526f7..4ea8250a 100644 --- a/src/app/panels/quit_error.rs +++ b/src/app/panels/quit_error.rs @@ -23,8 +23,8 @@ impl crate::app::App { CentralPanel::default().show(ctx, |ui| { ui.vertical_centered(|ui| { // Set width/height/font - let width = self.width; - let height = self.height / 4.0; + let width = self.size.x; + let height = self.size.y / 4.0; ui.style_mut().override_text_style = Some(Name("MonospaceLarge".into())); // Display ferris diff --git a/src/app/panels/top.rs b/src/app/panels/top.rs index a38144fb..24832d48 100644 --- a/src/app/panels/top.rs +++ b/src/app/panels/top.rs @@ -8,8 +8,8 @@ impl crate::app::App { pub fn top_panel(&mut self, ctx: &egui::Context) { debug!("App | Rendering TOP tabs"); TopBottomPanel::top("top").show(ctx, |ui| { - let width = (self.width - (SPACE * 11.0)) / 6.0; - let height = self.height / 15.0; + let width = (self.size.x - (SPACE * 11.0)) / 6.0; + let height = self.size.y / 15.0; ui.add_space(4.0); ui.horizontal(|ui| { ui.style_mut().override_text_style = Some(Name("Tab".into())); diff --git a/src/app/resize.rs b/src/app/resize.rs index ec714534..8af4c7dd 100644 --- a/src/app/resize.rs +++ b/src/app/resize.rs @@ -50,9 +50,9 @@ impl App { ctx.request_repaint(); info!( "App | Resizing frame to match new internal resolution: [{}x{}]", - self.width, self.height + self.size.x, self.size.y ); - init_text_styles(ctx, self.width, self.state.gupax.selected_scale); + init_text_styles(ctx, self.size.x, self.state.gupax.selected_scale); self.resizing = false; } }); diff --git a/src/disk/state.rs b/src/disk/state.rs index 4e684b85..53ec1d39 100644 --- a/src/disk/state.rs +++ b/src/disk/state.rs @@ -1,3 +1,6 @@ +use anyhow::{bail, Result}; +use hyper::StatusCode; + use super::*; use crate::{components::node::RemoteNode, disk::status::*}; //---------------------------------------------------------------------------------------------------- [State] Impl @@ -175,6 +178,7 @@ pub struct Gupax { pub auto_update: bool, pub auto_p2pool: bool, pub auto_xmrig: bool, + pub auto_xvb: bool, // pub auto_monero: bool, pub ask_before_quit: bool, pub save_before_quit: bool, @@ -240,8 +244,15 @@ pub struct Xmrig { #[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize, Default)] pub struct Xvb { - pub token_confirmed: String, - pub token_inserted: String, + pub token: String, + pub node: XvbNode, +} + +#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize, Default)] +pub enum XvbNode { + NorthAmerica, + #[default] + Europe, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -272,6 +283,7 @@ impl Default for Gupax { auto_update: true, auto_p2pool: false, auto_xmrig: false, + auto_xvb: true, ask_before_quit: true, save_before_quit: true, update_via_tor: true, @@ -314,6 +326,36 @@ impl Default for P2pool { } } } + +impl Xvb { + pub async fn is_token_exist(address: String, token: String) -> Result<()> { + let client: hyper::Client = + hyper::Client::builder().build(hyper::client::HttpConnector::new()); + if let Ok(request) = hyper::Request::builder() + .method("GET") + .uri(format!( + "{}/cgi-bin/p2pool_bonus_history_api.cgi?address={}&token={}", + XVB_URL, address, token + )) + .body(hyper::Body::empty()) + { + if let Ok(resp) = client.request(request).await { + match resp.status() { + StatusCode::OK => Ok(()), + StatusCode::UNPROCESSABLE_ENTITY => { + bail!("the token is invalid for this xmr address.") + } + _ => bail!("The status of the response is not expected"), + } + } else { + bail!("error from response") + } + } else { + bail!("request could not be build") + } + } +} + impl Xmrig { fn with_threads(max_threads: usize, current_threads: usize) -> Self { let xmrig = Self::default(); diff --git a/src/disk/tests.rs b/src/disk/tests.rs index 6b6ef839..f0f06afc 100644 --- a/src/disk/tests.rs +++ b/src/disk/tests.rs @@ -31,6 +31,7 @@ mod test { auto_update = true auto_p2pool = false auto_xmrig = false + auto_xvb = false ask_before_quit = true save_before_quit = true update_via_tor = true @@ -97,8 +98,8 @@ mod test { selected_port = "3333" [xvb] - token_confirmed = "" - token_inserted = "" + token = "" + node = "Europe" [version] gupax = "v1.3.0" p2pool = "v2.5" diff --git a/src/helper/mod.rs b/src/helper/mod.rs index 078fc077..bc77633b 100644 --- a/src/helper/mod.rs +++ b/src/helper/mod.rs @@ -72,6 +72,7 @@ pub struct Helper { pub pub_sys: Arc>, // The public API for [sysinfo] that the [Status] tab reads from pub p2pool: Arc>, // P2Pool process state pub xmrig: Arc>, // XMRig process state + pub xvb: Arc>, // XvB process state pub gui_api_p2pool: Arc>, // P2Pool API state (for GUI thread) pub gui_api_xmrig: Arc>, // XMRig API state (for GUI thread) pub gui_api_xvb: Arc>, // XMRig API state (for GUI thread) @@ -246,6 +247,7 @@ impl Default for ProcessSignal { pub enum ProcessName { P2pool, Xmrig, + Xvb, } impl std::fmt::Display for ProcessState { @@ -263,6 +265,7 @@ impl std::fmt::Display for ProcessName { match *self { ProcessName::P2pool => write!(f, "P2Pool"), ProcessName::Xmrig => write!(f, "XMRig"), + ProcessName::Xvb => write!(f, "XvB"), } } } @@ -276,6 +279,7 @@ impl Helper { pub_sys: Arc>, p2pool: Arc>, xmrig: Arc>, + xvb: Arc>, gui_api_p2pool: Arc>, gui_api_xmrig: Arc>, gui_api_xvb: Arc>, @@ -293,6 +297,7 @@ impl Helper { // These are created when initializing [App], since it needs a handle to it as well p2pool, xmrig, + xvb, gui_api_p2pool, gui_api_xmrig, gui_api_xvb, diff --git a/src/helper/xvb.rs b/src/helper/xvb.rs index e9a4a261..5c8afcbe 100644 --- a/src/helper/xvb.rs +++ b/src/helper/xvb.rs @@ -1,26 +1,98 @@ use derive_more::Display; use hyper::client::HttpConnector; use hyper_tls::HttpsConnector; -use log::{debug, info, warn}; +use log::{debug, error, info, warn}; use serde::Deserialize; +use std::fmt::Write; use std::{ sync::{Arc, Mutex}, thread, time::Instant, }; -use crate::utils::{constants::XVB_URL_PUBLIC_API, macros::lock}; +use crate::{ + disk::state::Xvb, + helper::{ProcessSignal, ProcessState}, + utils::{ + constants::{HORI_CONSOLE, XVB_URL_PUBLIC_API}, + human::HumanTime, + macros::{lock, lock2, sleep}, + }, +}; -use super::Helper; +use super::{Helper, Process}; impl Helper { - pub fn start_xvb(helper: &Arc>) { + // Just sets some signals for the watchdog thread to pick up on. + pub fn stop_xvb(helper: &Arc>) { + info!("XvB | Attempting to stop..."); + lock2!(helper, p2pool).signal = ProcessSignal::Stop; + lock2!(helper, p2pool).state = ProcessState::Middle; + } + pub fn restart_xvb( + helper: &Arc>, + state_xvb: &crate::disk::state::Xvb, + state_p2pool: &crate::disk::state::P2pool, + ) { + info!("XvB | Attempting to restart..."); + lock2!(helper, p2pool).signal = ProcessSignal::Restart; + lock2!(helper, p2pool).state = ProcessState::Middle; + let helper = helper.clone(); + let state_xvb = state_xvb.clone(); + let state_p2pool = state_p2pool.clone(); + // This thread lives to wait, start xmrig then die. + thread::spawn(move || { + while lock2!(helper, xvb).state != ProcessState::Waiting { + warn!("XvB | Want to restart but process is still alive, waiting..."); + sleep!(1000); + } + // Ok, process is not alive, start the new one! + info!("XvB | Old process seems dead, starting new one!"); + Self::start_xvb(&helper, &state_xvb, &state_p2pool); + }); + info!("XMRig | Restart ... OK"); + } + pub fn start_xvb( + helper: &Arc>, + state_xvb: &crate::disk::state::Xvb, + state_p2pool: &crate::disk::state::P2pool, + ) { + lock2!(helper, xvb).state = ProcessState::Middle; + let https = HttpsConnector::new(); let client = hyper::Client::builder().build(https); let gui_api = Arc::clone(&lock!(helper).gui_api_xvb); let pub_api = Arc::clone(&lock!(helper).pub_api_xvb); + let process = Arc::clone(&lock!(helper).xvb); + let state_xvb = state_xvb.clone(); + let state_p2pool = state_p2pool.clone(); + // two first verifications should be done before starting the process and giving the possibility to start it. + // verify if address is inserted + // if state_p2pool.address.is_empty() { + // warn!("Xvb | Start ... Failed because payout address is not inserted in P2pool tab"); + // lock2!(helper, xvb).state = ProcessState::Failed; + // return; + // } + // verify if token exist + // if state_xvb.token.is_empty() { + // warn!("Xvb | Start ... Failed because valid token is not inserted in XvB tab"); + // lock2!(helper, xvb).state = ProcessState::Failed; + // return; + // } + // verify if token and address are existent on XvB server + let rt = tokio::runtime::Runtime::new().unwrap(); + if !rt.block_on(async move { + Xvb::is_token_exist(state_p2pool.address, state_xvb.token) + .await + .is_ok() + }) { + warn!("Xvb | Start ... Failed because token and associated address are not existent on XvB server"); + lock2!(helper, xvb).state = ProcessState::Failed; + return; + } + thread::spawn(move || { - Self::spawn_xvb_watchdog(client, gui_api, pub_api); + Self::spawn_xvb_watchdog(client, gui_api, pub_api, process); }); } #[tokio::main] @@ -28,16 +100,31 @@ impl Helper { client: hyper::Client>, gui_api: Arc>, pub_api: Arc>, + process: Arc>, ) { info!("XvB started"); + + // 2. Set process state + debug!("XvB | Setting process state..."); + { + let mut lock = lock!(process); + lock.state = ProcessState::Middle; + lock.signal = ProcessSignal::None; + lock.start = Instant::now(); + } // Reset stats before loop *lock!(pub_api) = PubXvbApi::new(); *lock!(gui_api) = PubXvbApi::new(); + let start = lock!(process).start; info!("XvB | Entering watchdog mode... woof!"); loop { // Set timer let now = Instant::now(); + // check signal + if signal_interrupt(process.clone(), start, gui_api.clone()) { + break; + } debug!("XvB Watchdog | ----------- Start of loop -----------"); // Send an HTTP API request debug!("XvB Watchdog | Attempting HTTP API request..."); @@ -57,7 +144,7 @@ impl Helper { // XvB Status do not need to be refreshed like others because combine_with_gui do not refresh if no data is changed. let elapsed = now.elapsed().as_secs(); if elapsed < 59 { - let sleep = (60 - elapsed) as u64; + let sleep = 60 - elapsed; debug!("XvB Watchdog | END OF LOOP - Sleeping for [{}]s...", sleep); std::thread::sleep(std::time::Duration::from_secs(sleep)) } else { @@ -70,6 +157,10 @@ impl Helper { use serde_this_or_that::as_u64; #[derive(Debug, Clone, Default, Deserialize)] pub struct PubXvbApi { + #[serde(skip)] + pub output: String, + #[serde(skip)] + pub uptime: HumanTime, pub time_remain: u32, // remaining time of round in minutes pub bonus_hr: f64, pub donate_hr: f64, // donated hr from all donors @@ -139,6 +230,57 @@ impl PubXvbApi { Ok(serde_json::from_slice::(&body)?) } } + +fn signal_interrupt( + process: Arc>, + start: Instant, + gui_api: Arc>, +) -> bool { + // Check SIGNAL + // check if STOP or RESTART Signal is given. + // if STOP, will put Signal to None, if Restart to Wait + // in either case, will break from loop. + if lock!(process).signal == ProcessSignal::Stop { + debug!("P2Pool Watchdog | Stop SIGNAL caught"); + // Wait to get the exit status + let uptime = HumanTime::into_human(start.elapsed()); + info!("Xvb Watchdog | Stopped ... Uptime was: [{}]", uptime); + // insert the signal into output of XvB + // This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it. + if let Err(e) = writeln!( + lock!(gui_api).output, + "{}\nXvb stopped | Uptime: [{}] | \n{}\n\n\n\n", + HORI_CONSOLE, + uptime, + HORI_CONSOLE + ) { + error!("XvB Watchdog | GUI Uptime/Exit status write failed: {}", e); + } + debug!("XvB Watchdog | Stop SIGNAL done, breaking"); + lock!(process).signal = ProcessSignal::None; + return true; + // Check RESTART + } else if lock!(process).signal == ProcessSignal::Restart { + debug!("XvB Watchdog | Restart SIGNAL caught"); + let uptime = HumanTime::into_human(start.elapsed()); + info!("XvB Watchdog | Stopped ... Uptime was: [{}]", uptime); + // insert the signal into output of XvB + // This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it. + if let Err(e) = writeln!( + lock!(gui_api).output, + "{}\nXvb stopped | Uptime: [{}] | \n{}\n\n\n\n", + HORI_CONSOLE, + uptime, + HORI_CONSOLE + ) { + error!("XvB Watchdog | GUI Uptime/Exit status write failed: {}", e); + } + debug!("XvB Watchdog | Restart SIGNAL done, breaking"); + lock!(process).state = ProcessState::Waiting; + return true; + } + false +} //---------------------------------------------------------------------------------------------------- TEST #[cfg(test)] mod test { diff --git a/src/inits.rs b/src/inits.rs index c7adc5f5..ec71af90 100644 --- a/src/inits.rs +++ b/src/inits.rs @@ -206,7 +206,11 @@ pub fn init_auto(app: &mut App) { } else { info!("Skipping auto-xmrig..."); } - // [XvB] - // For now, the XvB client of the API for receiving public information is always enabled. - Helper::start_xvb(&app.helper); + // [Auto-XvB] + if app.state.gupax.auto_xvb { + Helper::start_xvb(&app.helper, &app.state.xvb, &app.state.p2pool); + } else { + info!("Skipping auto-xvb..."); + + } } diff --git a/src/utils/constants.rs b/src/utils/constants.rs index 266779b0..c14996bd 100644 --- a/src/utils/constants.rs +++ b/src/utils/constants.rs @@ -101,6 +101,13 @@ pub const XMRIG_FAILED: &str = "XMRig is offline and failed when exiting"; pub const XMRIG_MIDDLE: &str = "XMRig is in the middle of (re)starting/stopping"; pub const XMRIG_NOT_MINING: &str = "XMRig is online, but not mining to any pool"; +pub const XVB_ALIVE: &str = + "XvB process is configured and distributing hashrate, XvB node is online"; +pub const XVB_DEAD: &str = "XvB process is offline"; +pub const XVB_FAILED: &str = "XvB process is misconfigured or the XvB node is offline"; +pub const XVB_MIDDLE: &str = "XvB is in the middle of (re)starting/stopping"; +pub const XVB_NOT_CONFIGURED: &str = "You need to insert an existent token before starting XvB"; + // This is the typical space added when using // [ui.separator()] or [ui.group()] // Used for subtracting the width/height so @@ -266,6 +273,7 @@ pub const GUPAX_ASK_BEFORE_QUIT: &str = "Ask before quitting Gupax"; pub const GUPAX_SAVE_BEFORE_QUIT: &str = "Automatically save any changed settings before quitting"; pub const GUPAX_AUTO_P2POOL: &str = "Automatically start P2Pool on Gupax startup. If you are using [P2Pool Simple], this will NOT wait for your [Auto-Ping] to finish, it will start P2Pool on the pool you already have selected. This option will fail if your P2Pool settings aren't valid!"; pub const GUPAX_AUTO_XMRIG: &str = "Automatically start XMRig on Gupax startup. This option will fail if your XMRig settings aren't valid!"; +pub const GUPAX_AUTO_XVB: &str = "Automatically start XvB on Gupax startup. This option will fail if your XvB settings aren't valid!"; pub const GUPAX_ADJUST: &str = "Adjust and set the width/height of the Gupax window"; pub const GUPAX_WIDTH: &str = "Set the width of the Gupax window"; pub const GUPAX_HEIGHT: &str = "Set the height of the Gupax window";