mirror of
https://github.com/Cyrix126/gupaxx.git
synced 2024-11-16 15:27:46 +00:00
xmrig/status: implement API hyper/tokio call; add [Gupax] stats
This commit is contained in:
parent
81ec266e0a
commit
1e2b8f7803
8 changed files with 270 additions and 126 deletions
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -32,6 +32,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"static_vcruntime",
|
||||
"sudo",
|
||||
"sysinfo",
|
||||
"tar",
|
||||
"tls-api",
|
||||
"tls-api-native-tls",
|
||||
|
@ -2504,6 +2505,15 @@ dependencies = [
|
|||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.3"
|
||||
|
@ -3820,6 +3830,20 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d08ba83d6dde63d053e42d7230f0dc7f8d8efeb8d30d3681580d158156461ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"ntapi",
|
||||
"once_cell",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "6.0.3"
|
||||
|
|
|
@ -46,6 +46,7 @@ regex = { version = "1.6.0", default-features = false, features = ["perf"] }
|
|||
rfd = "0.10.0"
|
||||
serde = { version = "1.0.145", features = ["rc", "derive"] }
|
||||
serde_json = "1.0"
|
||||
sysinfo = { version = "0.27.0", default-features = false }
|
||||
tls-api = "0.9.0"
|
||||
tls-api-native-tls = "0.9.0"
|
||||
tokio = { version = "1.21.2", features = ["rt", "time", "macros", "process"] }
|
||||
|
|
|
@ -69,6 +69,7 @@ pub const RED: egui::Color32 = egui::Color32::from_rgb(230, 50, 50);
|
|||
pub const GREEN: egui::Color32 = egui::Color32::from_rgb(100, 230, 100);
|
||||
pub const YELLOW: egui::Color32 = egui::Color32::from_rgb(230, 230, 100);
|
||||
pub const BRIGHT_YELLOW: egui::Color32 = egui::Color32::from_rgb(250, 250, 100);
|
||||
pub const BONE: egui::Color32 = egui::Color32::from_rgb(190, 190, 190); // In between LIGHT_GRAY <-> GRAY
|
||||
pub const WHITE: egui::Color32 = egui::Color32::WHITE;
|
||||
pub const GRAY: egui::Color32 = egui::Color32::GRAY;
|
||||
pub const LIGHT_GRAY: egui::Color32 = egui::Color32::LIGHT_GRAY;
|
||||
|
@ -110,6 +111,14 @@ pub const OS: &str = "🐧 Linux";
|
|||
pub const OS_NAME: &str = "Linux";
|
||||
|
||||
// Tooltips
|
||||
// Status
|
||||
pub const STATUS_GUPAX_UPTIME: &str = "How long Gupax has been online";
|
||||
pub const STATUS_GUPAX_CPU_USAGE: &str = "How much CPU Gupax is currently using. This accounts for all your threads (it is out of 100%)";
|
||||
pub const STATUS_GUPAX_MEMORY_USAGE: &str = "How much memory Gupax is currently using in Megabytes";
|
||||
pub const STATUS_GUPAX_SYSTEM_CPU_USAGE: &str = "How much CPU your entire system is currently using. This accounts for all your threads (it is out of 100%)";
|
||||
pub const STATUS_GUPAX_SYSTEM_MEMORY: &str = "How much memory your entire system has (including swap) and is currently using in Gigabytes";
|
||||
pub const STATUS_GUPAX_SYSTEM_CPU_MODEL: &str = "The detected model of your system's CPU and its current frequency";
|
||||
|
||||
// Gupax
|
||||
pub const GUPAX_UPDATE: &str = "Check for updates on Gupax, P2Pool, and XMRig via GitHub's API and upgrade automatically";
|
||||
pub const GUPAX_AUTO_UPDATE: &str = "Automatically check for updates at startup";
|
||||
|
|
|
@ -142,8 +142,8 @@ impl State {
|
|||
simple: true,
|
||||
auto_update: true,
|
||||
auto_node: true,
|
||||
auto_p2pool: true,
|
||||
auto_xmrig: true,
|
||||
auto_p2pool: false,
|
||||
auto_xmrig: false,
|
||||
ask_before_quit: true,
|
||||
save_before_quit: true,
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
|
|
|
@ -230,7 +230,7 @@ impl Gupax {
|
|||
})});
|
||||
// Saved [Tab]
|
||||
ui.group(|ui| {
|
||||
let height = ui.available_height()/2.0;
|
||||
let height = ui.available_height()/1.85;
|
||||
let width = (width/5.0)-(SPACE*1.93);
|
||||
ui.horizontal(|ui| {
|
||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::About, "About")).on_hover_text(GUPAX_TAB_ABOUT).clicked() { self.tab = Tab::About; }
|
||||
|
|
181
src/helper.rs
181
src/helper.rs
|
@ -46,6 +46,7 @@ use crate::{
|
|||
constants::*,
|
||||
SudoState,
|
||||
};
|
||||
use sysinfo::SystemExt;
|
||||
use serde::{Serialize,Deserialize};
|
||||
use num_format::{Buffer,Locale};
|
||||
use log::*;
|
||||
|
@ -67,7 +68,8 @@ const GUI_OUTPUT_LEEWAY: usize = MAX_GUI_OUTPUT_BYTES - 1000;
|
|||
// A meta struct holding all the data that gets processed in this thread
|
||||
pub struct Helper {
|
||||
pub instant: Instant, // Gupax start as an [Instant]
|
||||
pub human_time: HumanTime, // Gupax uptime formatting for humans
|
||||
pub uptime: HumanTime, // Gupax uptime formatting for humans
|
||||
pub pub_sys: Arc<Mutex<Sys>>, // The public API for [sysinfo] that the [Status] tab reads from
|
||||
pub p2pool: Arc<Mutex<Process>>, // P2Pool process state
|
||||
pub xmrig: Arc<Mutex<Process>>, // XMRig process state
|
||||
pub gui_api_p2pool: Arc<Mutex<PubP2poolApi>>, // P2Pool API state (for GUI thread)
|
||||
|
@ -88,6 +90,30 @@ pub struct Helper {
|
|||
// it's the helpers job to lock everything, and move the watchdog [Pub*Api]s
|
||||
// on a 1-second interval into the [GUI]'s [Pub*Api] struct, atomically.
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Sys {
|
||||
pub gupax_uptime: String,
|
||||
pub gupax_cpu_usage: String,
|
||||
pub gupax_memory_used_mb: String,
|
||||
pub system_cpu_model: String,
|
||||
pub system_memory: String,
|
||||
pub system_cpu_usage: String,
|
||||
}
|
||||
|
||||
impl Sys {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
gupax_uptime: "0 seconds".to_string(),
|
||||
gupax_cpu_usage: "???%".to_string(),
|
||||
gupax_memory_used_mb: "??? megabytes".to_string(),
|
||||
system_cpu_usage: "???%".to_string(),
|
||||
system_memory: "???GB / ???GB".to_string(),
|
||||
system_cpu_model: "???".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [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.
|
||||
|
@ -191,10 +217,11 @@ use tokio::io::{BufReader,AsyncBufReadExt};
|
|||
|
||||
impl Helper {
|
||||
//---------------------------------------------------------------------------------------------------- General Functions
|
||||
pub fn new(instant: std::time::Instant, p2pool: Arc<Mutex<Process>>, xmrig: Arc<Mutex<Process>>, gui_api_p2pool: Arc<Mutex<PubP2poolApi>>, gui_api_xmrig: Arc<Mutex<PubXmrigApi>>, img_p2pool: Arc<Mutex<ImgP2pool>>, img_xmrig: Arc<Mutex<ImgXmrig>>) -> Self {
|
||||
pub fn new(instant: std::time::Instant, pub_sys: Arc<Mutex<Sys>>, p2pool: Arc<Mutex<Process>>, xmrig: Arc<Mutex<Process>>, gui_api_p2pool: Arc<Mutex<PubP2poolApi>>, gui_api_xmrig: Arc<Mutex<PubXmrigApi>>, img_p2pool: Arc<Mutex<ImgP2pool>>, img_xmrig: Arc<Mutex<ImgXmrig>>) -> Self {
|
||||
Self {
|
||||
instant,
|
||||
human_time: HumanTime::into_human(instant.elapsed()),
|
||||
pub_sys,
|
||||
uptime: HumanTime::into_human(instant.elapsed()),
|
||||
priv_api_p2pool: Arc::new(Mutex::new(PrivP2poolApi::new())),
|
||||
priv_api_xmrig: Arc::new(Mutex::new(PrivXmrigApi::new())),
|
||||
pub_api_p2pool: Arc::new(Mutex::new(PubP2poolApi::new())),
|
||||
|
@ -255,22 +282,6 @@ impl Helper {
|
|||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- P2Pool specific
|
||||
// Read P2Pool's API file.
|
||||
fn read_p2pool_api(path: &std::path::PathBuf) -> Result<String, std::io::Error> {
|
||||
match std::fs::read_to_string(path) {
|
||||
Ok(s) => Ok(s),
|
||||
Err(e) => { warn!("P2Pool API | [{}] read error: {}", path.display(), e); Err(e) },
|
||||
}
|
||||
}
|
||||
|
||||
// Deserialize the above [String] into a [PrivP2poolApi]
|
||||
fn str_to_priv_p2pool_api(string: &str) -> Result<PrivP2poolApi, serde_json::Error> {
|
||||
match serde_json::from_str::<PrivP2poolApi>(string) {
|
||||
Ok(a) => Ok(a),
|
||||
Err(e) => { warn!("P2Pool API | Could not deserialize API data: {}", e); Err(e) },
|
||||
}
|
||||
}
|
||||
|
||||
// Just sets some signals for the watchdog thread to pick up on.
|
||||
pub fn stop_p2pool(helper: &Arc<Mutex<Self>>) {
|
||||
info!("P2Pool | Attempting to stop...");
|
||||
|
@ -513,9 +524,9 @@ impl Helper {
|
|||
PubP2poolApi::update_from_output(&pub_api, &output_full, &output_buf, start.elapsed(), ®ex);
|
||||
|
||||
// Read API file into string
|
||||
if let Ok(string) = Self::read_p2pool_api(&path) {
|
||||
if let Ok(string) = PrivP2poolApi::read_p2pool_api(&path) {
|
||||
// Deserialize
|
||||
if let Ok(s) = Self::str_to_priv_p2pool_api(&string) {
|
||||
if let Ok(s) = PrivP2poolApi::str_to_priv_p2pool_api(&string) {
|
||||
// Update the structs.
|
||||
PubP2poolApi::update_from_priv(&pub_api, &priv_api);
|
||||
}
|
||||
|
@ -603,7 +614,7 @@ impl Helper {
|
|||
pub fn start_xmrig(helper: &Arc<Mutex<Self>>, state: &crate::disk::Xmrig, path: &std::path::PathBuf, sudo: Arc<Mutex<SudoState>>) {
|
||||
helper.lock().unwrap().xmrig.lock().unwrap().state = ProcessState::Middle;
|
||||
|
||||
let args = Self::build_xmrig_args_and_mutate_img(helper, state, path);
|
||||
let (args, api_ip_port) = Self::build_xmrig_args_and_mutate_img(helper, state, path);
|
||||
|
||||
// Print arguments & user settings to console
|
||||
crate::disk::print_dash(&format!("XMRig | Launch arguments: {:#?}", args));
|
||||
|
@ -616,15 +627,16 @@ impl Helper {
|
|||
let priv_api = Arc::clone(&helper.lock().unwrap().priv_api_xmrig);
|
||||
let path = path.clone();
|
||||
thread::spawn(move || {
|
||||
Self::spawn_xmrig_watchdog(process, gui_api, pub_api, priv_api, args, path, sudo);
|
||||
Self::spawn_xmrig_watchdog(process, gui_api, pub_api, priv_api, args, path, sudo, api_ip_port);
|
||||
});
|
||||
}
|
||||
|
||||
// Takes in some [State/Xmrig] and parses it to build the actual command arguments.
|
||||
// Returns the [Vec] of actual arguments, and mutates the [ImgXmrig] for the main GUI thread
|
||||
// It returns a value... and mutates a deeply nested passed argument... this is some pretty bad code...
|
||||
pub fn build_xmrig_args_and_mutate_img(helper: &Arc<Mutex<Self>>, state: &crate::disk::Xmrig, path: &std::path::PathBuf) -> Vec<String> {
|
||||
pub fn build_xmrig_args_and_mutate_img(helper: &Arc<Mutex<Self>>, state: &crate::disk::Xmrig, path: &std::path::PathBuf) -> (Vec<String>, String) {
|
||||
let mut args = Vec::with_capacity(500);
|
||||
let mut api_ip_port = String::with_capacity(15);
|
||||
let path = path.clone();
|
||||
// The actual binary we're executing is [sudo], technically
|
||||
// the XMRig path is just an argument to sudo, so add it.
|
||||
|
@ -651,6 +663,7 @@ impl Helper {
|
|||
threads: state.current_threads.to_string(),
|
||||
url: "127.0.0.1:3333 (Local P2Pool)".to_string(),
|
||||
};
|
||||
api_ip_port = "127.0.0.1:18088".to_string();
|
||||
|
||||
// [Advanced]
|
||||
} else {
|
||||
|
@ -689,9 +702,10 @@ impl Helper {
|
|||
url,
|
||||
threads: state.current_threads.to_string(),
|
||||
};
|
||||
api_ip_port = format!("{}:{}", api_ip, api_port);
|
||||
}
|
||||
}
|
||||
args
|
||||
(args, api_ip_port)
|
||||
}
|
||||
|
||||
// We actually spawn [sudo] on Unix, with XMRig being the argument.
|
||||
|
@ -712,8 +726,10 @@ impl Helper {
|
|||
cmd
|
||||
}
|
||||
|
||||
// The P2Pool watchdog. Spawns 1 OS thread for reading a PTY (STDOUT+STDERR), and combines the [Child] with a PTY so STDIN actually works.
|
||||
fn spawn_xmrig_watchdog(process: Arc<Mutex<Process>>, gui_api: Arc<Mutex<PubXmrigApi>>, pub_api: Arc<Mutex<PubXmrigApi>>, priv_api: Arc<Mutex<PrivXmrigApi>>, args: Vec<String>, mut path: std::path::PathBuf, sudo: Arc<Mutex<SudoState>>) {
|
||||
// The XMRig watchdog. Spawns 1 OS thread for reading a PTY (STDOUT+STDERR), and combines the [Child] with a PTY so STDIN actually works.
|
||||
// This isn't actually async, a tokio runtime is unfortunately needed because [Hyper] is an async library (HTTP API calls)
|
||||
#[tokio::main]
|
||||
async fn spawn_xmrig_watchdog(process: Arc<Mutex<Process>>, gui_api: Arc<Mutex<PubXmrigApi>>, pub_api: Arc<Mutex<PubXmrigApi>>, priv_api: Arc<Mutex<PrivXmrigApi>>, args: Vec<String>, mut path: std::path::PathBuf, sudo: Arc<Mutex<SudoState>>, api_ip_port: String) {
|
||||
// 1a. Create PTY
|
||||
let pty = portable_pty::native_pty_system();
|
||||
let mut pair = pty.openpty(portable_pty::PtySize {
|
||||
|
@ -758,9 +774,7 @@ impl Helper {
|
|||
let output_full = Arc::clone(&process.lock().unwrap().output_full);
|
||||
let output_buf = Arc::clone(&process.lock().unwrap().output_buf);
|
||||
|
||||
// path.pop();
|
||||
// path.push(P2POOL_API_PATH);
|
||||
// let regex = P2poolRegex::new();
|
||||
let client: hyper::Client<hyper::client::HttpConnector> = hyper::Client::builder().build(hyper::client::HttpConnector::new());
|
||||
let start = process.lock().unwrap().start;
|
||||
|
||||
// 5. Loop as watchdog
|
||||
|
@ -838,15 +852,11 @@ impl Helper {
|
|||
|
||||
// Always update from output
|
||||
PubXmrigApi::update_from_output(&pub_api, &output_buf, start.elapsed());
|
||||
//
|
||||
// // Read API file into string
|
||||
// if let Ok(string) = Self::read_xmrig_api(&path) {
|
||||
// // Deserialize
|
||||
// if let Ok(s) = Self::str_to_priv_xmrig_api(&string) {
|
||||
// // Update the structs.
|
||||
// PubP2poolApi::update_from_priv(&pub_api, &priv_api);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Send an HTTP API request
|
||||
if let Ok(priv_api) = PrivXmrigApi::request_xmrig_api(client.clone(), &api_ip_port).await {
|
||||
PubXmrigApi::from_priv(&mut pub_api.lock().unwrap(), priv_api);
|
||||
}
|
||||
|
||||
// Check if logs need resetting
|
||||
Self::check_reset_output_full(&output_full, ProcessName::Xmrig);
|
||||
|
@ -863,9 +873,44 @@ impl Helper {
|
|||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- The "helper"
|
||||
fn update_pub_sys_from_sysinfo(sysinfo: &sysinfo::System, pub_sys: &mut Sys, pid: &sysinfo::Pid, helper: &Helper, max_threads: usize) {
|
||||
use sysinfo::{CpuExt,ProcessExt,NetworkExt,NetworksExt};
|
||||
let gupax_uptime = helper.uptime.to_string();
|
||||
let cpu = &sysinfo.cpus()[0];
|
||||
let gupax_cpu_usage = format!("{:.2}%", sysinfo.process(*pid).unwrap().cpu_usage()/(max_threads as f32));
|
||||
let gupax_memory_used_mb = HumanNumber::from_u64(sysinfo.process(*pid).unwrap().memory()/1_000_000);
|
||||
let gupax_memory_used_mb = format!("{} megabytes", gupax_memory_used_mb);
|
||||
let system_cpu_model = format!("{} ({}MHz)", cpu.brand(), cpu.frequency());
|
||||
let system_memory = {
|
||||
let used = (sysinfo.used_memory() as f64)/1_000_000_000.0;
|
||||
let total = (sysinfo.total_memory() as f64)/1_000_000_000.0;
|
||||
format!("{:.3} GB / {:.3} GB", used, total)
|
||||
};
|
||||
let system_cpu_usage = {
|
||||
let mut total: f32 = 0.0;
|
||||
for cpu in sysinfo.cpus() {
|
||||
total += cpu.cpu_usage();
|
||||
}
|
||||
format!("{:.2}%", total/(max_threads as f32))
|
||||
};
|
||||
*pub_sys = Sys {
|
||||
gupax_uptime,
|
||||
gupax_cpu_usage,
|
||||
gupax_memory_used_mb,
|
||||
system_cpu_usage,
|
||||
system_memory,
|
||||
system_cpu_model,
|
||||
..*pub_sys
|
||||
};
|
||||
}
|
||||
|
||||
// The "helper" thread. Syncs data between threads here and the GUI.
|
||||
pub fn spawn_helper(helper: &Arc<Mutex<Self>>) {
|
||||
pub fn spawn_helper(helper: &Arc<Mutex<Self>>, mut sysinfo: sysinfo::System, pid: sysinfo::Pid, max_threads: usize) {
|
||||
let mut helper = Arc::clone(helper);
|
||||
let mut pub_sys = Arc::clone(&helper.lock().unwrap().pub_sys);
|
||||
let sysinfo_cpu = sysinfo::CpuRefreshKind::everything();
|
||||
let sysinfo_processes = sysinfo::ProcessRefreshKind::new().with_cpu();
|
||||
|
||||
thread::spawn(move || {
|
||||
info!("Helper | Hello from helper thread! Entering loop where I will spend the rest of my days...");
|
||||
// Begin loop
|
||||
|
@ -875,31 +920,37 @@ impl Helper {
|
|||
|
||||
// 2. Lock... EVERYTHING!
|
||||
let mut lock = helper.lock().unwrap();
|
||||
// Calculate Gupax's uptime always.
|
||||
lock.uptime = HumanTime::into_human(lock.instant.elapsed());
|
||||
let mut gui_api_p2pool = lock.gui_api_p2pool.lock().unwrap();
|
||||
let mut gui_api_xmrig = lock.gui_api_xmrig.lock().unwrap();
|
||||
let mut pub_api_p2pool = lock.pub_api_p2pool.lock().unwrap();
|
||||
let mut pub_api_xmrig = lock.pub_api_xmrig.lock().unwrap();
|
||||
let p2pool = lock.p2pool.lock().unwrap();
|
||||
let xmrig = lock.xmrig.lock().unwrap();
|
||||
// Calculate Gupax's uptime always.
|
||||
let human_time = HumanTime::into_human(lock.instant.elapsed());
|
||||
let mut lock_pub_sys = pub_sys.lock().unwrap();
|
||||
// If [P2Pool] is alive...
|
||||
if p2pool.is_alive() { PubP2poolApi::combine_gui_pub_api(&mut gui_api_p2pool, &mut pub_api_p2pool); }
|
||||
// If [XMRig] is alive...
|
||||
if xmrig.is_alive() { PubXmrigApi::combine_gui_pub_api(&mut gui_api_xmrig, &mut pub_api_xmrig); }
|
||||
|
||||
// 2. Drop... (almost) EVERYTHING... IN REVERSE!
|
||||
// 2. Selectively refresh [sysinfo] for only what we need (better performance).
|
||||
sysinfo.refresh_cpu_specifics(sysinfo_cpu);
|
||||
sysinfo.refresh_processes_specifics(sysinfo_processes);
|
||||
sysinfo.refresh_memory();
|
||||
Self::update_pub_sys_from_sysinfo(&sysinfo, &mut lock_pub_sys, &pid, &lock, max_threads);
|
||||
|
||||
// 3. Drop... (almost) EVERYTHING... IN REVERSE!
|
||||
drop(lock_pub_sys);
|
||||
drop(xmrig);
|
||||
drop(p2pool);
|
||||
drop(pub_api_xmrig);
|
||||
drop(pub_api_p2pool);
|
||||
drop(gui_api_xmrig);
|
||||
drop(gui_api_p2pool);
|
||||
// Update the time... then drop :)
|
||||
lock.human_time = human_time;
|
||||
drop(lock);
|
||||
|
||||
// 3. Calculate if we should sleep or not.
|
||||
// 4. Calculate if we should sleep or not.
|
||||
// If we should sleep, how long?
|
||||
let elapsed = start.elapsed().as_millis();
|
||||
if elapsed < 1000 {
|
||||
|
@ -908,7 +959,7 @@ impl Helper {
|
|||
std::thread::sleep(std::time::Duration::from_millis((1000-elapsed) as u64));
|
||||
}
|
||||
|
||||
// 4. End loop
|
||||
// 5. End loop
|
||||
}
|
||||
|
||||
// 5. Something has gone terribly wrong if the helper exited this loop.
|
||||
|
@ -1220,7 +1271,7 @@ impl PubP2poolApi {
|
|||
let buf = std::mem::take(&mut pub_api.output);
|
||||
*gui_api = Self {
|
||||
output,
|
||||
..std::mem::take(&mut *pub_api)
|
||||
..std::mem::take(pub_api)
|
||||
};
|
||||
if !buf.is_empty() { gui_api.output.push_str(&buf); }
|
||||
}
|
||||
|
@ -1327,6 +1378,22 @@ impl PrivP2poolApi {
|
|||
connections: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Read P2Pool's API file to a [String].
|
||||
fn read_p2pool_api(path: &std::path::PathBuf) -> Result<String, std::io::Error> {
|
||||
match std::fs::read_to_string(path) {
|
||||
Ok(s) => Ok(s),
|
||||
Err(e) => { warn!("P2Pool API | [{}] read error: {}", path.display(), e); Err(e) },
|
||||
}
|
||||
}
|
||||
|
||||
// Deserialize the above [String] into a [PrivP2poolApi]
|
||||
fn str_to_priv_p2pool_api(string: &str) -> Result<Self, serde_json::Error> {
|
||||
match serde_json::from_str::<Self>(string) {
|
||||
Ok(a) => Ok(a),
|
||||
Err(e) => { warn!("P2Pool API | Could not deserialize API data: {}", e); Err(e) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- [ImgXmrig]
|
||||
|
@ -1385,7 +1452,7 @@ impl PubXmrigApi {
|
|||
let buf = std::mem::take(&mut pub_api.output);
|
||||
*gui_api = Self {
|
||||
output,
|
||||
..std::mem::take(&mut *pub_api)
|
||||
..std::mem::take(pub_api)
|
||||
};
|
||||
if !buf.is_empty() { gui_api.output.push_str(&buf); }
|
||||
}
|
||||
|
@ -1403,9 +1470,8 @@ impl PubXmrigApi {
|
|||
}
|
||||
|
||||
// Formats raw private data into ready-to-print human readable version.
|
||||
fn from_priv(private: PrivXmrigApi, output: String) -> Self {
|
||||
Self {
|
||||
output: output.clone(),
|
||||
fn from_priv(public: &mut Self, private: PrivXmrigApi) {
|
||||
*public = Self {
|
||||
uptime: HumanTime::new(),
|
||||
worker_id: private.worker_id,
|
||||
resources: HumanNumber::from_load(private.resources.load_average),
|
||||
|
@ -1414,6 +1480,7 @@ impl PubXmrigApi {
|
|||
diff: HumanNumber::from_u128(private.connection.diff),
|
||||
accepted: HumanNumber::from_u128(private.connection.accepted),
|
||||
rejected: HumanNumber::from_u128(private.connection.rejected),
|
||||
..std::mem::take(public)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1440,6 +1507,16 @@ impl PrivXmrigApi {
|
|||
hashrate: Hashrate::new(),
|
||||
}
|
||||
}
|
||||
// Send an HTTP request to XMRig's API, serialize it into [Self] and return it
|
||||
async fn request_xmrig_api(client: hyper::Client<hyper::client::HttpConnector>, api_ip_port: &str) -> Result<Self, anyhow::Error> {
|
||||
let request = hyper::Request::builder()
|
||||
.method("GET")
|
||||
.uri("http://".to_string() + api_ip_port + "/1/summary")
|
||||
.body(hyper::Body::empty())?;
|
||||
let mut response = tokio::time::timeout(std::time::Duration::from_millis(500), client.request(request)).await?;
|
||||
let body = hyper::body::to_bytes(response?.body_mut()).await?;
|
||||
Ok(serde_json::from_slice::<Self>(&body)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
|
|
72
src/main.rs
72
src/main.rs
|
@ -49,6 +49,15 @@ use std::{
|
|||
time::Instant,
|
||||
path::PathBuf,
|
||||
};
|
||||
// Sysinfo (this controls which info we get)
|
||||
use sysinfo::{
|
||||
NetworkExt,
|
||||
CpuExt,
|
||||
ProcessExt,
|
||||
System,
|
||||
SystemExt,
|
||||
PidExt,
|
||||
};
|
||||
// Modules
|
||||
mod ferris;
|
||||
mod constants;
|
||||
|
@ -112,6 +121,7 @@ pub struct App {
|
|||
// This holds everything related to the data processed by the "helper thread".
|
||||
// This includes the "helper" threads public P2Pool/XMRig's API.
|
||||
helper: Arc<Mutex<Helper>>, // [Helper] state, mostly for Gupax uptime
|
||||
pub_sys: Arc<Mutex<Sys>>, // [Sys] state, read by [Status], mutated by [Helper]
|
||||
p2pool: Arc<Mutex<Process>>, // [P2Pool] process state
|
||||
xmrig: Arc<Mutex<Process>>, // [XMRig] process state
|
||||
p2pool_api: Arc<Mutex<PubP2poolApi>>, // Public ready-to-print P2Pool API made by the "helper" thread
|
||||
|
@ -126,6 +136,8 @@ pub struct App {
|
|||
// State from [--flags]
|
||||
no_startup: bool,
|
||||
// Static stuff
|
||||
pid: sysinfo::Pid, // Gupax's PID
|
||||
max_threads: usize, // Max amount of detected system threads
|
||||
now: Instant, // Internal timer
|
||||
exe: String, // Path for [Gupax] binary
|
||||
dir: String, // Directory [Gupax] binary is in
|
||||
|
@ -160,6 +172,17 @@ impl App {
|
|||
let xmrig_api = Arc::new(Mutex::new(PubXmrigApi::new()));
|
||||
let p2pool_img = Arc::new(Mutex::new(ImgP2pool::new()));
|
||||
let xmrig_img = Arc::new(Mutex::new(ImgXmrig::new()));
|
||||
|
||||
// We give this to the [Helper] thread.
|
||||
let mut sysinfo = sysinfo::System::new_with_specifics(
|
||||
sysinfo::RefreshKind::new()
|
||||
.with_cpu(sysinfo::CpuRefreshKind::everything())
|
||||
.with_processes(sysinfo::ProcessRefreshKind::new().with_cpu())
|
||||
.with_memory());
|
||||
sysinfo.refresh_all();
|
||||
let pid = sysinfo::get_current_pid().unwrap();
|
||||
let pub_sys = Arc::new(Mutex::new(Sys::new()));
|
||||
|
||||
let mut app = Self {
|
||||
tab: Tab::default(),
|
||||
ping: Arc::new(Mutex::new(Ping::new())),
|
||||
|
@ -177,7 +200,7 @@ impl App {
|
|||
restart: Arc::new(Mutex::new(Restart::No)),
|
||||
diff: false,
|
||||
error_state: ErrorState::new(),
|
||||
helper: Arc::new(Mutex::new(Helper::new(now, p2pool.clone(), xmrig.clone(), p2pool_api.clone(), xmrig_api.clone(), p2pool_img.clone(), xmrig_img.clone()))),
|
||||
helper: Arc::new(Mutex::new(Helper::new(now, pub_sys.clone(), p2pool.clone(), xmrig.clone(), p2pool_api.clone(), xmrig_api.clone(), p2pool_img.clone(), xmrig_img.clone()))),
|
||||
p2pool,
|
||||
xmrig,
|
||||
p2pool_api,
|
||||
|
@ -190,6 +213,9 @@ impl App {
|
|||
resizing: false,
|
||||
alpha: 0,
|
||||
no_startup: false,
|
||||
pub_sys,
|
||||
pid,
|
||||
max_threads: num_cpus::get(),
|
||||
now,
|
||||
admin: false,
|
||||
exe: String::new(),
|
||||
|
@ -291,7 +317,7 @@ impl App {
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
let mut og = app.og.lock().unwrap(); // Lock [og]
|
||||
// Handle max threads
|
||||
og.xmrig.max_threads = num_cpus::get();
|
||||
og.xmrig.max_threads = app.max_threads;
|
||||
let current = og.xmrig.current_threads;
|
||||
let max = og.xmrig.max_threads;
|
||||
if current > max {
|
||||
|
@ -340,7 +366,7 @@ impl App {
|
|||
|
||||
// Spawn the "Helper" thread.
|
||||
info!("Helper | Spawning helper thread...");
|
||||
Helper::spawn_helper(&app.helper);
|
||||
Helper::spawn_helper(&app.helper, sysinfo, app.pid, app.max_threads);
|
||||
info!("Helper ... OK");
|
||||
|
||||
// Check for privilege. Should be Admin on [Windows] and NOT root on Unix.
|
||||
|
@ -617,7 +643,7 @@ fn init_auto(app: &mut App) {
|
|||
Helper::start_p2pool(&app.helper, &app.state.p2pool, &app.state.gupax.absolute_p2pool_path);
|
||||
}
|
||||
} else {
|
||||
info!("Skipping auto-xmrig...");
|
||||
info!("Skipping auto-p2pool...");
|
||||
}
|
||||
|
||||
// [Auto-XMRig]
|
||||
|
@ -1044,26 +1070,24 @@ impl eframe::App for App {
|
|||
TopBottomPanel::top("top").show(ctx, |ui| {
|
||||
let width = (self.width - (SPACE*10.0))/5.0;
|
||||
let height = self.height/12.0;
|
||||
ui.group(|ui| {
|
||||
ui.add_space(4.0);
|
||||
ui.horizontal(|ui| {
|
||||
let style = ui.style_mut();
|
||||
style.override_text_style = Some(Name("Tab".into()));
|
||||
style.visuals.widgets.inactive.fg_stroke.color = Color32::from_rgb(100, 100, 100);
|
||||
style.visuals.selection.bg_fill = Color32::from_rgb(255, 120, 120);
|
||||
style.visuals.selection.stroke = Stroke { width: 5.0, color: Color32::from_rgb(255, 255, 255) };
|
||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::About, "About")).clicked() { self.tab = Tab::About; }
|
||||
ui.separator();
|
||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::Status, "Status")).clicked() { self.tab = Tab::Status; }
|
||||
ui.separator();
|
||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::Gupax, "Gupax")).clicked() { self.tab = Tab::Gupax; }
|
||||
ui.separator();
|
||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::P2pool, "P2Pool")).clicked() { self.tab = Tab::P2pool; }
|
||||
ui.separator();
|
||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig")).clicked() { self.tab = Tab::Xmrig; }
|
||||
});
|
||||
ui.add_space(4.0);
|
||||
ui.add_space(4.0);
|
||||
ui.horizontal(|ui| {
|
||||
let style = ui.style_mut();
|
||||
style.override_text_style = Some(Name("Tab".into()));
|
||||
style.visuals.widgets.inactive.fg_stroke.color = Color32::from_rgb(100, 100, 100);
|
||||
style.visuals.selection.bg_fill = Color32::from_rgb(255, 120, 120);
|
||||
style.visuals.selection.stroke = Stroke { width: 5.0, color: Color32::from_rgb(255, 255, 255) };
|
||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::About, "About")).clicked() { self.tab = Tab::About; }
|
||||
ui.separator();
|
||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::Status, "Status")).clicked() { self.tab = Tab::Status; }
|
||||
ui.separator();
|
||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::Gupax, "Gupax")).clicked() { self.tab = Tab::Gupax; }
|
||||
ui.separator();
|
||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::P2pool, "P2Pool")).clicked() { self.tab = Tab::P2pool; }
|
||||
ui.separator();
|
||||
if ui.add_sized([width, height], SelectableLabel::new(self.tab == Tab::Xmrig, "XMRig")).clicked() { self.tab = Tab::Xmrig; }
|
||||
});
|
||||
ui.add_space(4.0);
|
||||
});
|
||||
|
||||
// Bottom: app info + state/process buttons
|
||||
|
@ -1312,7 +1336,7 @@ impl eframe::App for App {
|
|||
});
|
||||
}
|
||||
Tab::Status => {
|
||||
Status::show(self, self.width, self.height, ctx, ui);
|
||||
Status::show(&self.pub_sys, &self.p2pool_api, &self.xmrig_api, &self.p2pool_img, &self.xmrig_img, self.width, self.height, ctx, ui);
|
||||
}
|
||||
Tab::Gupax => {
|
||||
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);
|
||||
|
|
103
src/status.rs
103
src/status.rs
|
@ -15,56 +15,65 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::App;
|
||||
use egui::{containers::*, *};
|
||||
use crate::{
|
||||
Helper,
|
||||
PubP2poolApi,
|
||||
PubXmrigApi,
|
||||
ImgP2pool,
|
||||
ImgXmrig,
|
||||
constants::*,
|
||||
Sys,
|
||||
};
|
||||
use std::sync::{Arc,Mutex};
|
||||
use egui::{
|
||||
containers::*,
|
||||
Label,RichText,TextStyle
|
||||
};
|
||||
|
||||
// Main data structure for the Status tab
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Status {
|
||||
|
||||
}
|
||||
pub struct Status {}
|
||||
|
||||
impl Status {
|
||||
pub fn show(_app: &mut App, _width: f32, _height: f32, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||
let color = if ui.visuals().dark_mode {
|
||||
Color32::from_additive_luminance(196)
|
||||
} else {
|
||||
Color32::from_black_alpha(240)
|
||||
};
|
||||
|
||||
Frame::canvas(ui.style()).show(ui, |ui| {
|
||||
ui.ctx().request_repaint();
|
||||
let time = ui.input().time;
|
||||
|
||||
let desired_size = ui.available_width() * vec2(1.0, 0.3);
|
||||
let (_id, rect) = ui.allocate_space(desired_size);
|
||||
|
||||
let to_screen =
|
||||
emath::RectTransform::from_to(Rect::from_x_y_ranges(0.0..=1.0, -1.0..=1.0), rect);
|
||||
|
||||
let mut shapes = vec![];
|
||||
|
||||
for &mode in &[2, 3, 5] {
|
||||
let mode = mode as f64;
|
||||
let n = 120;
|
||||
let speed = 1.5;
|
||||
|
||||
let points: Vec<Pos2> = (0..=n)
|
||||
.map(|i| {
|
||||
let t = i as f64 / (n as f64);
|
||||
let amp = (time * speed * mode).sin() / mode;
|
||||
let y = amp * (t * std::f64::consts::TAU / 2.0 * mode).sin();
|
||||
to_screen * pos2(t as f32, y as f32)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let thickness = 10.0 / mode as f32;
|
||||
shapes.push(epaint::Shape::line(points, Stroke::new(thickness, color)));
|
||||
}
|
||||
|
||||
ui.painter().extend(shapes);
|
||||
});
|
||||
ui.label("WIP");
|
||||
ui.label("Enjoy these cool lines.");
|
||||
}
|
||||
pub fn show(sys: &Arc<Mutex<Sys>>, p2pool_api: &Arc<Mutex<PubP2poolApi>>, xmrig_api: &Arc<Mutex<PubXmrigApi>>, p2pool_img: &Arc<Mutex<ImgP2pool>>, xmrig_img: &Arc<Mutex<ImgXmrig>>, width: f32, height: f32, ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||
let width = (width/3.0)-(SPACE*1.666);
|
||||
let min_height = height/1.14;
|
||||
let height = height/20.0;
|
||||
ui.horizontal(|ui| {
|
||||
// [Gupax]
|
||||
ui.group(|ui| { ui.vertical(|ui| {
|
||||
ui.set_min_height(min_height);
|
||||
ui.add_sized([width, height*2.0], Label::new(RichText::new("[Gupax]").color(LIGHT_GRAY).text_style(TextStyle::Name("MonospaceLarge".into()))));
|
||||
// Uptime
|
||||
ui.add_sized([width, height], Label::new(RichText::new("Uptime").underline().color(BONE))).on_hover_text(STATUS_GUPAX_UPTIME);
|
||||
ui.add_sized([width, height], Label::new(format!("{}", sys.lock().unwrap().gupax_uptime)));
|
||||
ui.add_sized([width, height], Label::new(RichText::new("Gupax CPU").underline().color(BONE))).on_hover_text(STATUS_GUPAX_CPU_USAGE);
|
||||
ui.add_sized([width, height], Label::new(format!("{}", sys.lock().unwrap().gupax_cpu_usage)));
|
||||
ui.add_sized([width, height], Label::new(RichText::new("Gupax Memory").underline().color(BONE))).on_hover_text(STATUS_GUPAX_MEMORY_USAGE);
|
||||
ui.add_sized([width, height], Label::new(format!("{}", sys.lock().unwrap().gupax_memory_used_mb)));
|
||||
ui.add_sized([width, height], Label::new(RichText::new("System CPU").underline().color(BONE))).on_hover_text(STATUS_GUPAX_SYSTEM_CPU_USAGE);
|
||||
ui.add_sized([width, height], Label::new(format!("{}", sys.lock().unwrap().system_cpu_usage)));
|
||||
ui.add_sized([width, height], Label::new(RichText::new("System Memory").underline().color(BONE))).on_hover_text(STATUS_GUPAX_SYSTEM_MEMORY);
|
||||
ui.add_sized([width, height], Label::new(format!("{}", sys.lock().unwrap().system_memory)));
|
||||
ui.add_sized([width, height], Label::new(RichText::new("System CPU Model").underline().color(BONE))).on_hover_text(STATUS_GUPAX_SYSTEM_CPU_MODEL);
|
||||
ui.add_sized([width, height], Label::new(format!("{}", sys.lock().unwrap().system_cpu_model)));
|
||||
})});
|
||||
// [P2Pool]
|
||||
ui.group(|ui| { ui.vertical(|ui| {
|
||||
ui.set_min_height(min_height);
|
||||
ui.add_sized([width, height*2.0], Label::new(RichText::new("[P2Pool]").color(LIGHT_GRAY).text_style(TextStyle::Name("MonospaceLarge".into()))));
|
||||
// Uptime
|
||||
ui.add_sized([width, height], Label::new(RichText::new("Uptime").underline()));
|
||||
ui.add_sized([width, height], Label::new(format!("{}", p2pool_api.lock().unwrap().uptime)));
|
||||
})});
|
||||
// [XMRig]
|
||||
ui.group(|ui| { ui.vertical(|ui| {
|
||||
ui.set_min_height(min_height);
|
||||
ui.add_sized([width, height*2.0], Label::new(RichText::new("[XMRig]").color(LIGHT_GRAY).text_style(TextStyle::Name("MonospaceLarge".into()))));
|
||||
// Uptime
|
||||
ui.add_sized([width, height], Label::new(RichText::new("Uptime").underline()));
|
||||
ui.add_sized([width, height], Label::new(format!("{}", xmrig_api.lock().unwrap().uptime)));
|
||||
})});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue