xmrig/status: implement API hyper/tokio call; add [Gupax] stats

This commit is contained in:
hinto-janaiyo 2022-12-11 15:49:01 -05:00
parent 81ec266e0a
commit 1e2b8f7803
No known key found for this signature in database
GPG key ID: B1C5A64B80691E45
8 changed files with 270 additions and 126 deletions

24
Cargo.lock generated
View file

@ -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"

View file

@ -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"] }

View file

@ -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";

View file

@ -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"))]

View file

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

View file

@ -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(), &regex);
// 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)]

View file

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

View file

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