connect functions with [State/state.rs]

This commit is contained in:
hinto-janaiyo 2022-10-15 15:15:27 -04:00
parent dafb8fc18e
commit ed7ddeeda1
No known key found for this signature in database
GPG key ID: D7483F6CA27D1B1D
7 changed files with 273 additions and 361 deletions

1
Cargo.lock generated
View file

@ -1085,6 +1085,7 @@ dependencies = [
"monero", "monero",
"num-format", "num-format",
"num_cpus", "num_cpus",
"rand",
"regex", "regex",
"reqwest", "reqwest",
"serde", "serde",

View file

@ -12,8 +12,8 @@
| `main.rs` | Struct/enum/impl for `App/Tab/State`, init functions, main function | `main.rs` | Struct/enum/impl for `App/Tab/State`, init functions, main function
| `node.rs` | Struct/impl for Community Nodes | `node.rs` | Struct/impl for Community Nodes
| `p2pool.rs` | Struct/impl for `P2Pool` tab | `p2pool.rs` | Struct/impl for `P2Pool` tab
| `state.rs` | Struct/impl for `gupax.toml`, the disk state
| `status.rs` | Struct/impl for `Status` tab | `status.rs` | Struct/impl for `Status` tab
| `toml.rs` | Struct/impl for `gupax.toml`, the disk state
| `xmrig.rs` | Struct/impl for `XMRig` tab | `xmrig.rs` | Struct/impl for `XMRig` tab
## Bootstrap ## Bootstrap
@ -42,7 +42,7 @@ This is how Gupax works internally when starting up, divided into 3 sections.
- Kill processes, kill connections, exit - Kill processes, kill connections, exit
## State ## State
Internal state is saved in the "OS data folder" as `gupax.toml`, using the [TOML](https://github.com/toml-lang/toml) format. If the version can't be parsed (not in the `vX.X.X` or `vX.X` format), the auto-updater will be skipped. [If not found, a default `gupax.toml` file will be created with `Toml::default`.](https://github.com/hinto-janaiyo/gupax/blob/main/src/toml.rs) Gupax will `panic!` if `gupax.toml` has IO or parsing issues. Internal state is saved in the "OS data folder" as `gupax.toml`, using the [TOML](https://github.com/toml-lang/toml) format. If the version can't be parsed (not in the `vX.X.X` or `vX.X` format), the auto-updater will be skipped. [If not found, a default `gupax.toml` file will be created with `State::default`.](https://github.com/hinto-janaiyo/gupax/blob/main/src/state.rs) Gupax will `panic!` if `gupax.toml` has IO or parsing issues.
| OS | Data Folder | Example | | OS | Data Folder | Example |
|----------|----------------------------------------- |-----------------------------------------------------------| |----------|----------------------------------------- |-----------------------------------------------------------|

View file

@ -16,135 +16,98 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
use eframe::{egui,NativeOptions};
use egui::{Vec2,Pos2};
use std::process::exit;
use std::thread;
use egui::color::Color32;
use egui::Stroke;
use egui::FontId;
use egui::FontFamily::Proportional;
use egui::TextStyle::{Body,Button,Heading,Monospace,Name,Small};
use egui::RichText;
use egui::Label;
use regex::Regex;
use egui_extras::RetainedImage;
use log::*;
use env_logger::Builder;
use env_logger::WriteStyle;
use std::io::Write;
use std::time::Instant;
use std::sync::{Arc,Mutex};
//---------------------------------------------------------------------------------------------------- Imports
// egui/eframe
use egui::TextStyle::*;
use egui::color::Color32;
use egui::FontFamily::Proportional;
use egui::{FontId,Label,RichText,Stroke,Vec2,Pos2};
use egui_extras::RetainedImage;
use eframe::{egui,NativeOptions};
// Logging
use log::*;
use env_logger::{Builder,WriteStyle};
// std
use std::io::Write;
use std::process::exit;
use std::sync::{Arc,Mutex};
use std::thread;
use std::time::Instant;
// Modules
mod constants; mod constants;
mod node; mod node;
mod toml; mod state;
mod about; mod about;
mod status; mod status;
mod gupax; mod gupax;
mod p2pool; mod p2pool;
mod xmrig; mod xmrig;
use {constants::*,node::*,crate::toml::*,about::*,status::*,gupax::*,p2pool::*,xmrig::*}; use {constants::*,node::*,state::*,about::*,status::*,gupax::*,p2pool::*,xmrig::*};
// The state of the outer [App]. //---------------------------------------------------------------------------------------------------- Struct + Impl
// See the [State] struct for the // The state of the outer main [App].
// actual inner state of the settings. // See the [State] struct in [state.rs] for the
// actual inner state of the tab settings.
pub struct App { pub struct App {
// Misc state
tab: Tab, // What tab are we on?
quit: bool, // Did user click quit button?
quit_confirm: bool, // Did user confirm to quit?
ping: bool, // Did user click the ping button?
ping_prog: Arc<Mutex<bool>>, // Are we in the progress of pinging?
node: Arc<Mutex<NodeStruct>>, // Data on community nodes
// State:
// og = Old state to compare against
// state = Working state (current settings)
// Instead of comparing [og == state] every frame,
// the [diff] bool will be the signal for [Reset/Save].
og: State,
state: State,
diff: bool,
// Static stuff
now: Instant,
resolution: Vec2,
os: &'static str,
version: String, version: String,
name_version: String, name_version: String,
tab: Tab,
changed: bool,
os: &'static str,
current_threads: u16,
max_threads: u16,
resolution: Vec2,
banner: RetainedImage, banner: RetainedImage,
// TEMPORARY FIXME
p2pool: bool, p2pool: bool,
xmrig: bool, xmrig: bool,
state: State,
og: State,
allowed_to_close: bool,
show_confirmation_dialog: bool,
now: Instant,
ping: bool,
ping_prog: Arc<Mutex<bool>>,
node: Arc<Mutex<NodeStruct>>,
} }
impl App { impl App {
fn new(cc: &eframe::CreationContext<'_>) -> Self { fn new(cc: &eframe::CreationContext<'_>) -> Self {
let version = String::from("v0.0.1");
let name_version = String::from("Gupax v0.0.1");
let tab = Tab::default();
let max_threads = num_cpus::get().try_into().unwrap();
let current_threads: u16;
let changed = false;
let os = OS;
if max_threads != 1 {
current_threads = max_threads / 2
} else {
current_threads = 1
}
let resolution = cc.integration_info.window_info.size;
init_text_styles(&cc.egui_ctx, resolution[0] as f32);
let banner = match RetainedImage::from_image_bytes("banner.png", BYTES_BANNER) {
Ok(banner) => { info!("Banner loading ... OK"); banner },
Err(err) => { error!("{}", err); panic!("{}", err); },
};
let mut state = State::new();
let mut og = State::new();
info!("Frame resolution ... {:#?}", resolution);
Self { Self {
version, tab: Tab::default(),
name_version, quit: false,
tab, quit_confirm: false,
current_threads,
max_threads,
changed,
resolution,
os,
banner,
p2pool: false,
xmrig: false,
state,
og,
allowed_to_close: false,
show_confirmation_dialog: false,
now: Instant::now(),
node: Arc::new(Mutex::new(NodeStruct::default())),
ping: false, ping: false,
ping_prog: Arc::new(Mutex::new(false)), ping_prog: Arc::new(Mutex::new(false)),
node: Arc::new(Mutex::new(NodeStruct::default())),
og: State::default(),
state: State::default(),
diff: false,
now: Instant::now(),
resolution: cc.integration_info.window_info.size,
os: OS,
version: "v0.0.1".to_string(),
name_version: "Gupax v0.0.1".to_string(),
banner: RetainedImage::from_image_bytes("banner.png", BYTES_BANNER).expect("oops"),
// TEMPORARY FIXME
p2pool: false,
xmrig: false,
} }
} }
} }
// Inner state holding all //---------------------------------------------------------------------------------------------------- Enum + Impl
// mutable tab structs.
#[derive(Clone, Debug, Eq, PartialEq)]
struct State {
gupax: Gupax,
p2pool: P2pool,
xmrig: Xmrig,
}
impl State {
fn new() -> Self {
Self {
gupax: Gupax::new(),
p2pool: P2pool::new(),
xmrig: Xmrig::new(),
}
}
fn save(new: State) -> Self {
Self {
gupax: new.gupax,
p2pool: new.p2pool,
xmrig: new.xmrig,
}
}
}
// The tabs inside [App]. // The tabs inside [App].
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
enum Tab { enum Tab {
@ -154,12 +117,14 @@ enum Tab {
P2pool, P2pool,
Xmrig, Xmrig,
} }
impl Default for Tab { impl Default for Tab {
fn default() -> Self { fn default() -> Self {
Self::About Self::About
} }
} }
//---------------------------------------------------------------------------------------------------- Init functions
fn init_text_styles(ctx: &egui::Context, width: f32) { fn init_text_styles(ctx: &egui::Context, width: f32) {
let scale = width / 26.666; let scale = width / 26.666;
let mut style = (*ctx.style()).clone(); let mut style = (*ctx.style()).clone();
@ -230,23 +195,7 @@ fn init_options() -> NativeOptions {
options options
} }
fn main() { //---------------------------------------------------------------------------------------------------- [App] frame for [Panic] situations
init_logger();
let toml = match Toml::get() {
Ok(toml) => toml,
Err(err) => {
error!("{}", err);
let error_msg = err.to_string();
let options = Panic::options();
eframe::run_native("Gupax", options, Box::new(|cc| Box::new(Panic::new(cc, error_msg))),);
exit(1);
},
};
let options = init_options();
eframe::run_native("Gupax", options, Box::new(|cc| Box::new(App::new(cc))),);
}
// [App] frame for Panic situations.
struct Panic { error_msg: String, } struct Panic { error_msg: String, }
impl Panic { impl Panic {
fn options() -> NativeOptions { fn options() -> NativeOptions {
@ -288,15 +237,35 @@ impl eframe::App for Panic {
} }
} }
//---------------------------------------------------------------------------------------------------- Main [App] frame
fn main() {
init_logger();
// let toml = match State::get() {
// Ok(toml) => toml,
// Err(err) => {
// error!("{}", err);
// let error_msg = err.to_string();
// let options = Panic::options();
// eframe::run_native("Gupax", options, Box::new(|cc| Box::new(Panic::new(cc, error_msg))),);
// exit(1);
// },
// };
let state = State::default();
let options = init_options();
eframe::run_native("Gupax", options, Box::new(|cc| Box::new(App::new(cc))),);
}
impl eframe::App for App { impl eframe::App for App {
fn on_close_event(&mut self) -> bool { fn on_close_event(&mut self) -> bool {
// self.show_confirmation_dialog = true; self.quit = true;
self.ping = true; self.quit_confirm
self.allowed_to_close
} }
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
init_text_styles(ctx, 1280.0);
// Close confirmation. // Close confirmation.
if self.show_confirmation_dialog { if self.quit {
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
let width = ui.available_width(); let width = ui.available_width();
let width = width - 10.0; let width = width - 10.0;
@ -306,10 +275,10 @@ impl eframe::App for App {
ui.group(|ui| { ui.group(|ui| {
if ui.add_sized([width, height/10.0], egui::Button::new("Yes")).clicked() { if ui.add_sized([width, height/10.0], egui::Button::new("Yes")).clicked() {
info!("Quit confirmation = yes ... goodbye!"); info!("Quit confirmation = yes ... goodbye!");
exit(0); self.quit_confirm = true;
frame.close();
} else if ui.add_sized([width, height/10.0], egui::Button::new("No")).clicked() { } else if ui.add_sized([width, height/10.0], egui::Button::new("No")).clicked() {
info!("Quit confirmation = no ... returning!"); self.quit = false;
self.show_confirmation_dialog = false;
} }
}); });
}); });
@ -329,25 +298,25 @@ impl eframe::App for App {
}); });
} }
if *self.ping_prog.lock().unwrap() { // if *self.ping_prog.lock().unwrap() {
egui::CentralPanel::default().show(ctx, |ui| { // egui::CentralPanel::default().show(ctx, |ui| {
let width = ui.available_width(); // let width = ui.available_width();
let width = width - 10.0; // let width = width - 10.0;
let height = ui.available_height(); // let height = ui.available_height();
init_text_styles(ctx, width); // init_text_styles(ctx, width);
ui.add_sized([width, height/2.0], Label::new(format!("In progress: {}", *self.ping_prog.lock().unwrap()))); // ui.add_sized([width, height/2.0], Label::new(format!("In progress: {}", *self.ping_prog.lock().unwrap())));
ui.group(|ui| { // ui.group(|ui| {
if ui.add_sized([width, height/10.0], egui::Button::new("Yes")).clicked() { // if ui.add_sized([width, height/10.0], egui::Button::new("Yes")).clicked() {
info!("Quit confirmation = yes ... goodbye!"); // info!("Quit confirmation = yes ... goodbye!");
exit(0); // exit(0);
} else if ui.add_sized([width, height/10.0], egui::Button::new("No")).clicked() { // } else if ui.add_sized([width, height/10.0], egui::Button::new("No")).clicked() {
info!("Quit confirmation = no ... returning!"); // info!("Quit confirmation = no ... returning!");
self.show_confirmation_dialog = false; // self.show_confirmation_dialog = false;
} // }
}); // });
}); // });
return // return
} // }
// Top: Tabs // Top: Tabs
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
@ -390,6 +359,9 @@ impl eframe::App for App {
ui.add_sized([width, height], Label::new(self.os)); ui.add_sized([width, height], Label::new(self.os));
ui.separator(); ui.separator();
ui.add_sized([width/1.5, height], Label::new("P2Pool")); ui.add_sized([width/1.5, height], Label::new("P2Pool"));
// TODO
// self.p2pool + self.xmrig
// This is for process online/offline status
if self.p2pool == true { if self.p2pool == true {
ui.add_sized([width/4.0, height], Label::new(RichText::new("").color(Color32::from_rgb(100, 230, 100)))); ui.add_sized([width/4.0, height], Label::new(RichText::new("").color(Color32::from_rgb(100, 230, 100))));
} else { } else {
@ -469,7 +441,8 @@ impl eframe::App for App {
Status::show(self, ctx, ui); Status::show(self, ctx, ui);
} }
Tab::Gupax => { Tab::Gupax => {
Gupax::show(&mut self.state.gupax, ctx, ui); // Gupax::show(self.state.gupax, ctx, ui);
exit(0);
} }
Tab::P2pool => { Tab::P2pool => {
P2pool::show(&mut self.state.p2pool, ctx, ui); P2pool::show(&mut self.state.p2pool, ctx, ui);

View file

@ -55,12 +55,18 @@ pub struct Data {
pub ip: String, pub ip: String,
} }
#[derive(Copy,Clone,Debug,Deserialize,Serialize)] #[derive(Copy,Clone,Eq,PartialEq,Debug,Deserialize,Serialize)]
pub enum NodeEnum { pub enum NodeEnum {
C3pool, Cake, CakeEu, CakeUk, CakeUs, Monerujo, Rino, C3pool, Cake, CakeEu, CakeUk, CakeUs, Monerujo, Rino,
Selsta, Seth, SupportXmr, SupportXmrIr, XmrVsBeast, Selsta, Seth, SupportXmr, SupportXmrIr, XmrVsBeast,
} }
impl std::fmt::Display for NodeEnum {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:#?}", self)
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct PingResult { pub struct PingResult {
pub node: NodeStruct, pub node: NodeStruct,
@ -146,7 +152,7 @@ impl NodeStruct {
}; };
let mut timeout = false; let mut timeout = false;
let mut mid = Duration::new(0, 0); let mut mid = Duration::new(0, 0);
let max = rng::thread_rng().gen_range(1..5); let max = rand::thread_rng().gen_range(1..5);
for i in 1..=max { for i in 1..=max {
let client = reqwest::blocking::ClientBuilder::new(); let client = reqwest::blocking::ClientBuilder::new();
let client = reqwest::blocking::ClientBuilder::timeout(client, timeout_sec); let client = reqwest::blocking::ClientBuilder::timeout(client, timeout_sec);
@ -156,7 +162,7 @@ impl NodeStruct {
match client.post(http).json(&get_info).send() { match client.post(http).json(&get_info).send() {
Ok(r) => mid += now.elapsed(), Ok(r) => mid += now.elapsed(),
Err(err) => { Err(err) => {
error!("Timeout on [{}: {}] (over 5 seconds)", id, ip); error!("Timeout on [{:#?}: {}] (over 5 seconds)", id, ip);
mid += timeout_sec; mid += timeout_sec;
timeout = true; timeout = true;
}, },

View file

@ -16,77 +16,25 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::App; use crate::App;
use monero::util::address::Address;
use std::str::FromStr;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use crate::constants::*; use crate::constants::*;
use crate::state::P2pool;
use crate::node::NodeEnum;
use crate::node::{RINO,SETH,SELSTA};
#[derive(Clone, Debug, Eq, PartialEq)] // pub simple: bool,
pub enum Node { // pub mini: bool,
Rino, // pub out_peers: u8,
Seth, // pub in_peers: u8,
Selsta, // pub log_level: u8,
} // pub node: crate::node::NodeEnum,
// pub monerod: String,
impl Node {
pub fn ip(self) -> String {
match self {
Node::Rino => String::from("node.community.rino.io:18081"),
Node::Seth => String::from("node.sethforprivacy.com:18089"),
Node::Selsta => String::from("selsta1.featherwallet.net:18081"),
}
}
pub fn name(self) -> String {
match self {
Node::Rino => String::from("Rino"),
Node::Seth => String::from("Seth"),
Node::Selsta => String::from("Selsta"),
}
}
}
// Main data structure for the P2Pool tab
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct P2pool {
pub version: String,
pub sha256: String,
pub manual: bool,
pub mini: bool,
pub rpc: String,
pub zmq: String,
// pub rpc: u16, // pub rpc: u16,
// pub zmq: u16, // pub zmq: u16,
pub out_peers: u16, // pub address: String,
pub in_peers: u16,
pub log: u8,
pub monerod: String,
// pub monerod: std::net::SocketAddr,
pub community: Node,
pub address: String,
// pub address: monero::util::address::Address,
}
impl P2pool { impl P2pool {
pub fn new() -> Self { pub fn show(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) {
Self {
version: String::from("v2.4"),
sha256: String::from("asdf"),
manual: false,
mini: true,
rpc: String::from(""),
zmq: String::from(""),
out_peers: 10,
in_peers: 10,
log: 3,
// monerod: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 18081),
monerod: String::from(""),
address: String::from(""),
community: Node::Rino,
// address: Address::from_str("44hintoFpuo3ugKfcqJvh5BmrsTRpnTasJmetKC4VXCt6QDtbHVuixdTtsm6Ptp7Y8haXnJ6j8Gj2dra8CKy5ewz7Vi9CYW").unwrap(),
}
}
pub fn show(state: &mut P2pool, ctx: &egui::Context, ui: &mut egui::Ui) {
let height = ui.available_height() / 10.0; let height = ui.available_height() / 10.0;
let mut width = ui.available_width() - 50.0; let mut width = ui.available_width() - 50.0;
ui.group(|ui| { ui.group(|ui| {
@ -100,61 +48,61 @@ impl P2pool {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.group(|ui| { ui.vertical(|ui| { ui.group(|ui| { ui.vertical(|ui| {
ui.group(|ui| { ui.horizontal(|ui| { ui.group(|ui| { ui.horizontal(|ui| {
if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(state.mini == false, "P2Pool Main")).on_hover_text(P2POOL_MAIN).clicked() { state.mini = false; }; if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(self.mini == false, "P2Pool Main")).on_hover_text(P2POOL_MAIN).clicked() { self.mini = false; };
if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(state.mini == true, "P2Pool Mini")).on_hover_text(P2POOL_MINI).clicked() { state.mini = true; }; if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(self.mini == true, "P2Pool Mini")).on_hover_text(P2POOL_MINI).clicked() { self.mini = true; };
})}); })});
let width = (width/4.0); let width = width/4.0;
style.spacing.slider_width = width*1.25; style.spacing.slider_width = width*1.25;
ctx.set_style(style); ctx.set_style(style);
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add_sized([width/8.0, height/5.0], egui::Label::new("Out peers [10-450]:")); ui.add_sized([width/8.0, height/5.0], egui::Label::new("Out peers [10-450]:"));
ui.add_sized([width, height/5.0], egui::Slider::new(&mut state.out_peers, 10..=450)).on_hover_text(P2POOL_OUT); ui.add_sized([width, height/5.0], egui::Slider::new(&mut self.out_peers, 10..=450)).on_hover_text(P2POOL_OUT);
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add_sized([width/8.0, height/5.0], egui::Label::new(" In peers [10-450]:")); ui.add_sized([width/8.0, height/5.0], egui::Label::new(" In peers [10-450]:"));
ui.add_sized([width, height/5.0], egui::Slider::new(&mut state.in_peers, 10..=450)).on_hover_text(P2POOL_IN); ui.add_sized([width, height/5.0], egui::Slider::new(&mut self.in_peers, 10..=450)).on_hover_text(P2POOL_IN);
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add_sized([width/8.0, height/5.0], egui::Label::new(" Log level [0-6]:")); ui.add_sized([width/8.0, height/5.0], egui::Label::new(" Log level [0-6]:"));
ui.add_sized([width, height/5.0], egui::Slider::new(&mut state.log, 0..=6)).on_hover_text(P2POOL_LOG); ui.add_sized([width, height/5.0], egui::Slider::new(&mut self.log_level, 0..=6)).on_hover_text(P2POOL_LOG);
}); });
})}); })});
ui.group(|ui| { ui.vertical(|ui| { ui.group(|ui| { ui.vertical(|ui| {
ui.group(|ui| { ui.horizontal(|ui| { ui.group(|ui| { ui.horizontal(|ui| {
if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(state.manual == false, "Community Monero Node")).on_hover_text(P2POOL_COMMUNITY).clicked() { state.manual = false; }; if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(self.simple == false, "Community Monero Node")).on_hover_text(P2POOL_COMMUNITY).clicked() { self.simple = false; };
if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(state.manual == true, "Manual Monero Node")).on_hover_text(P2POOL_MANUAL).clicked() { state.manual = true; }; if ui.add_sized([width/4.0, height/5.0], egui::SelectableLabel::new(self.simple == true, "Manual Monero Node")).on_hover_text(P2POOL_MANUAL).clicked() { self.simple = true; };
})}); })});
ui.add_space(8.0); ui.add_space(8.0);
ui.horizontal(|ui| { ui.horizontal(|ui| {
// ui.add_sized([width/8.0, height/5.0], // ui.add_sized([width/8.0, height/5.0],
egui::ComboBox::from_label(Node::name(state.community.clone())).selected_text(Node::ip(state.community.clone())).show_ui(ui, |ui| { egui::ComboBox::from_label(self.node.to_string()).selected_text(RINO).show_ui(ui, |ui| {
ui.selectable_value(&mut state.community, Node::Rino, Node::ip(Node::Rino)); ui.selectable_value(&mut self.node, NodeEnum::Rino, RINO);
ui.selectable_value(&mut state.community, Node::Seth, Node::ip(Node::Seth)); ui.selectable_value(&mut self.node, NodeEnum::Seth, SETH);
ui.selectable_value(&mut state.community, Node::Selsta, Node::ip(Node::Selsta)); ui.selectable_value(&mut self.node, NodeEnum::Selsta, SELSTA);
}); });
// ); // );
}); });
if state.manual == false { ui.set_enabled(false); } if self.simple == false { ui.set_enabled(false); }
let width = (width/4.0); let width = (width/4.0);
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add_sized([width/8.0, height/7.8], egui::Label::new("Monero Node IP:")); ui.add_sized([width/8.0, height/7.8], egui::Label::new("Monero Node IP:"));
ui.spacing_mut().text_edit_width = ui.available_width() - 35.0; ui.spacing_mut().text_edit_width = ui.available_width() - 35.0;
ui.text_edit_singleline(&mut state.monerod); ui.text_edit_singleline(&mut self.monerod);
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add_sized([width/8.0, height/7.8], egui::Label::new("Monero Node RPC Port:")); ui.add_sized([width/8.0, height/7.8], egui::Label::new("Monero Node RPC Port:"));
ui.spacing_mut().text_edit_width = ui.available_width() - 35.0; ui.spacing_mut().text_edit_width = ui.available_width() - 35.0;
ui.text_edit_singleline(&mut state.rpc); ui.text_edit_singleline(&mut self.rpc.to_string());
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add_sized([width/8.0, height/7.8], egui::Label::new("Monero Node ZMQ Port:")); ui.add_sized([width/8.0, height/7.8], egui::Label::new("Monero Node ZMQ Port:"));
ui.spacing_mut().text_edit_width = ui.available_width() - 35.0; ui.spacing_mut().text_edit_width = ui.available_width() - 35.0;
ui.text_edit_singleline(&mut state.zmq); ui.text_edit_singleline(&mut self.zmq.to_string());
}); });
})}); })});
@ -164,7 +112,7 @@ impl P2pool {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.spacing_mut().text_edit_width = ui.available_width(); ui.spacing_mut().text_edit_width = ui.available_width();
ui.label("Address:"); ui.label("Address:");
ui.text_edit_singleline(&mut state.address); ui.text_edit_singleline(&mut self.address);
})}); })});
} }
} }

View file

@ -18,7 +18,7 @@
// This handles reading/parsing the state file: [gupax.toml] // This handles reading/parsing the state file: [gupax.toml]
// The TOML format is used. This struct hierarchy directly // The TOML format is used. This struct hierarchy directly
// translates into the TOML parser: // translates into the TOML parser:
// Toml/ // State/
// ├─ Gupax/ // ├─ Gupax/
// │ ├─ ... // │ ├─ ...
// ├─ P2pool/ // ├─ P2pool/
@ -31,13 +31,12 @@
use std::{fs,env}; use std::{fs,env};
use std::fmt::Display; use std::fmt::Display;
use std::path::{Path,PathBuf}; use std::path::{Path,PathBuf};
use std::result::Result;
use serde_derive::{Serialize,Deserialize}; use serde_derive::{Serialize,Deserialize};
use log::*; use log::*;
//---------------------------------------------------------------------------------------------------- Impl //---------------------------------------------------------------------------------------------------- Impl
// Since [State] is already used in [main.rs] to represent impl State {
// working state, [Toml] is used to represent disk state.
impl Toml {
pub fn default() -> Self { pub fn default() -> Self {
use crate::constants::{P2POOL_VERSION,XMRIG_VERSION}; use crate::constants::{P2POOL_VERSION,XMRIG_VERSION};
Self { Self {
@ -64,7 +63,9 @@ impl Toml {
tls: false, tls: false,
nicehash: false, nicehash: false,
keepalive: false, keepalive: false,
threads: 1, hugepages_jit: true,
current_threads: 1,
max_threads: 1,
priority: 2, priority: 2,
pool: "localhost:3333".to_string(), pool: "localhost:3333".to_string(),
address: "".to_string(), address: "".to_string(),
@ -76,7 +77,7 @@ impl Toml {
} }
} }
pub fn get() -> Result<Toml, TomlError> { pub fn get_path() -> Result<PathBuf, TomlError> {
// Get OS data folder // Get OS data folder
// Linux | $XDG_DATA_HOME or $HOME/.local/share | /home/alice/.local/state // Linux | $XDG_DATA_HOME or $HOME/.local/share | /home/alice/.local/state
// macOS | $HOME/Library/Application Support | /Users/Alice/Library/Application Support // macOS | $HOME/Library/Application Support | /Users/Alice/Library/Application Support
@ -89,36 +90,67 @@ impl Toml {
}, },
None => { error!("Couldn't get OS PATH for data"); return Err(TomlError::Path(PATH_ERROR.to_string())) }, None => { error!("Couldn't get OS PATH for data"); return Err(TomlError::Path(PATH_ERROR.to_string())) },
}; };
// Create directory // Create directory
fs::create_dir_all(&path)?; fs::create_dir_all(&path)?;
// Attempt to read file, create default if not found
path.push(FILENAME); path.push(FILENAME);
let file = match fs::read_to_string(&path) { info!("TOML path ... {}", path.display());
Ok(file) => file, Ok(path)
}
// Attempts to read [gupax.toml] or
// attempts to create if not found.
pub fn read_or_create(path: PathBuf) -> Result<String, TomlError> {
// Attempt to read file, create default if not found
match fs::read_to_string(&path) {
Ok(string) => {
info!("TOML read ... OK");
Ok(string)
},
Err(err) => { Err(err) => {
error!("TOML not found, attempting to create default"); error!("TOML not found, attempting to create default");
let default = match toml::ser::to_string(&Toml::default()) { let default = match toml::ser::to_string(&State::default()) {
Ok(o) => { info!("TOML serialization ... OK"); o }, Ok(o) => { info!("TOML serialization ... OK"); o },
Err(e) => { error!("Couldn't serialize default TOML file: {}", e); return Err(TomlError::Serialize(e)) }, Err(e) => { error!("Couldn't serialize default TOML file: {}", e); return Err(TomlError::Serialize(e)) },
}; };
fs::write(&path, default)?; fs::write(&path, &default)?;
info!("TOML write ... OK"); info!("TOML write ... OK");
fs::read_to_string(&path)? Ok(fs::read_to_string(default)?)
}, },
}; }
info!("TOML path ... {}", path.display()); }
info!("TOML read ... OK");
// Attempt to parse, return Result // Attempt to parse from String
match toml::from_str(&file) { pub fn parse(string: String) -> Result<State, TomlError> {
match toml::de::from_str(&string) {
Ok(toml) => { Ok(toml) => {
info!("TOML parse ... OK"); info!("TOML parse ... OK");
eprint!("{}", file); eprint!("{}", string);
Ok(toml) Ok(toml)
}, },
Err(err) => { error!("Couldn't parse TOML file"); Err(TomlError::Parse(err)) }, Err(err) => { error!("Couldn't parse TOML from string"); Err(TomlError::Deserialize(err)) },
}
}
// Last three functions combined
// get_path() -> read_or_create() -> parse()
// pub fn get() -> Result<State, TomlError> {
// let path = Self::path();
// }
// Overwrite disk Toml with memory State (save state)
pub fn overwrite(state: State, path: PathBuf) -> Result<(), TomlError> {
info!("Starting TOML overwrite...");
let string = match toml::ser::to_string(&state) {
Ok(string) => {
info!("TOML parse ... OK");
eprint!("{}", string);
string
},
Err(err) => { error!("Couldn't parse TOML into string"); return Err(TomlError::Serialize(err)) },
};
match fs::write(&path, string) {
Ok(_) => { info!("TOML overwrite ... OK"); Ok(()) },
Err(err) => { error!("Couldn't overwrite TOML file"); return Err(TomlError::Io(err)) },
} }
} }
} }
@ -129,8 +161,8 @@ impl Display for TomlError {
match self { match self {
Io(err) => write!(f, "{} | {}", ERROR, err), Io(err) => write!(f, "{} | {}", ERROR, err),
Path(err) => write!(f, "{} | {}", ERROR, err), Path(err) => write!(f, "{} | {}", ERROR, err),
Parse(err) => write!(f, "{} | {}", ERROR, err),
Serialize(err) => write!(f, "{} | {}", ERROR, err), Serialize(err) => write!(f, "{} | {}", ERROR, err),
Deserialize(err) => write!(f, "{} | {}", ERROR, err),
} }
} }
} }
@ -141,13 +173,6 @@ impl From<std::io::Error> for TomlError {
} }
} }
fn main() {
let state = match Toml::get() {
Ok(state) => { println!("OK"); state },
Err(err) => panic!(),
};
}
//---------------------------------------------------------------------------------------------------- Const //---------------------------------------------------------------------------------------------------- Const
const FILENAME: &'static str = "gupax.toml"; const FILENAME: &'static str = "gupax.toml";
const ERROR: &'static str = "TOML Error"; const ERROR: &'static str = "TOML Error";
@ -176,55 +201,57 @@ const DEFAULT_XMRIG_PATH: &'static str = "xmrig/xmrig";
pub enum TomlError { pub enum TomlError {
Io(std::io::Error), Io(std::io::Error),
Path(String), Path(String),
Parse(toml::de::Error),
Serialize(toml::ser::Error), Serialize(toml::ser::Error),
Deserialize(toml::de::Error),
} }
//---------------------------------------------------------------------------------------------------- Structs //---------------------------------------------------------------------------------------------------- Structs
#[derive(Debug,Deserialize,Serialize)] #[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)]
pub struct Toml { pub struct State {
gupax: Gupax, pub gupax: Gupax,
p2pool: P2pool, pub p2pool: P2pool,
xmrig: Xmrig, pub xmrig: Xmrig,
version: Version, pub version: Version,
} }
#[derive(Debug,Deserialize,Serialize)] #[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)]
struct Gupax { pub struct Gupax {
auto_update: bool, pub auto_update: bool,
ask_before_quit: bool, pub ask_before_quit: bool,
p2pool_path: String, pub p2pool_path: String,
xmrig_path: String, pub xmrig_path: String,
} }
#[derive(Debug,Deserialize,Serialize)] #[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)]
struct P2pool { pub struct P2pool {
simple: bool, pub simple: bool,
mini: bool, pub mini: bool,
out_peers: u8, pub out_peers: u16,
in_peers: u8, pub in_peers: u16,
log_level: u8, pub log_level: u8,
node: crate::node::NodeEnum, pub node: crate::node::NodeEnum,
monerod: String, pub monerod: String,
rpc: u16, pub rpc: u16,
zmq: u16, pub zmq: u16,
address: String, pub address: String,
} }
#[derive(Debug,Deserialize,Serialize)] #[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)]
struct Xmrig { pub struct Xmrig {
simple: bool, pub simple: bool,
tls: bool, pub tls: bool,
nicehash: bool, pub nicehash: bool,
keepalive: bool, pub keepalive: bool,
threads: u16, pub hugepages_jit: bool,
priority: u8, pub max_threads: u16,
pool: String, pub current_threads: u16,
address: String, pub priority: u8,
pub pool: String,
pub address: String,
} }
#[derive(Debug,Deserialize,Serialize)] #[derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize)]
struct Version { pub struct Version {
p2pool: String, pub p2pool: String,
xmrig: String, pub xmrig: String,
} }

View file

@ -20,54 +20,11 @@ use monero::util::address::Address;
use std::str::FromStr; use std::str::FromStr;
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use num_cpus; use num_cpus;
use crate::State;
use crate::constants::*; use crate::constants::*;
use crate::state::Xmrig;
// Main data structure for the XMRig tab
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Xmrig {
pub version: String,
pub sha256: String,
pub manual: bool,
pub tls: bool,
pub nicehash: bool,
pub keepalive: bool,
pub hugepages_jit: bool,
pub threads: u16,
pub priority: u8,
pub pool: String,
pub address: String,
// pub pool: std::net::SocketAddr,
// pub address: monero::util::address::Address,
pub max_threads: u16,
pub current_threads: u16,
}
impl Xmrig { impl Xmrig {
pub fn new() -> Self { pub fn show(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) {
let max_threads = num_cpus::get().try_into().unwrap();
let current_threads: u16;
if max_threads == 1 { current_threads = 1 } else { current_threads = max_threads/2 }
Self {
version: String::from("v6.18.0"),
sha256: String::from("asdf"),
manual: false,
tls: false,
nicehash: false,
keepalive: false,
hugepages_jit: true,
threads: 16,
priority: 2,
pool: String::from(""),
address: String::from(""),
// pool: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 3333),
// address: Address::from_str("44hintoFpuo3ugKfcqJvh5BmrsTRpnTasJmetKC4VXCt6QDtbHVuixdTtsm6Ptp7Y8haXnJ6j8Gj2dra8CKy5ewz7Vi9CYW").unwrap(),
max_threads,
current_threads,
}
}
pub fn show(state: &mut Xmrig, ctx: &egui::Context, ui: &mut egui::Ui) {
let height = ui.available_height() / 10.0; let height = ui.available_height() / 10.0;
let mut width = ui.available_width() - 10.0; let mut width = ui.available_width() - 10.0;
ui.group(|ui| { ui.group(|ui| {
@ -81,18 +38,18 @@ impl Xmrig {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.group(|ui| { ui.vertical(|ui| { ui.group(|ui| { ui.vertical(|ui| {
ui.group(|ui| { ui.horizontal(|ui| { ui.group(|ui| { ui.horizontal(|ui| {
if ui.add_sized([width/2.0, height/6.0], egui::SelectableLabel::new(state.manual == false, "P2Pool Mode")).on_hover_text(XMRIG_P2POOL).clicked() { state.manual = false; }; if ui.add_sized([width/2.0, height/6.0], egui::SelectableLabel::new(self.simple == false, "P2Pool Mode")).on_hover_text(XMRIG_P2POOL).clicked() { self.simple = false; };
if ui.add_sized([width/2.0, height/6.0], egui::SelectableLabel::new(state.manual == true, "Manual Mode")).on_hover_text(XMRIG_MANUAL).clicked() { state.manual = true; }; if ui.add_sized([width/2.0, height/6.0], egui::SelectableLabel::new(self.simple == true, "Manual Mode")).on_hover_text(XMRIG_MANUAL).clicked() { self.simple = true; };
})}); })});
ui.group(|ui| { ui.horizontal(|ui| { ui.group(|ui| { ui.horizontal(|ui| {
let width = width - 58.0; let width = width - 58.0;
ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut state.tls, "TLS Connection")).on_hover_text(XMRIG_TLS); ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut self.tls, "TLS Connection")).on_hover_text(XMRIG_TLS);
ui.separator(); ui.separator();
ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut state.hugepages_jit, "Hugepages JIT")).on_hover_text(XMRIG_HUGEPAGES_JIT); ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut self.hugepages_jit, "Hugepages JIT")).on_hover_text(XMRIG_HUGEPAGES_JIT);
ui.separator(); ui.separator();
ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut state.nicehash, "Nicehash")).on_hover_text(XMRIG_NICEHASH); ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut self.nicehash, "Nicehash")).on_hover_text(XMRIG_NICEHASH);
ui.separator(); ui.separator();
ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut state.keepalive, "Keepalive")).on_hover_text(XMRIG_KEEPALIVE); ui.add_sized([width/4.0, height/6.0], egui::Checkbox::new(&mut self.keepalive, "Keepalive")).on_hover_text(XMRIG_KEEPALIVE);
})}); })});
})}); })});
}); });
@ -101,28 +58,28 @@ impl Xmrig {
style.spacing.slider_width = ui.available_width()/1.25; style.spacing.slider_width = ui.available_width()/1.25;
ctx.set_style(style); ctx.set_style(style);
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add_sized([width/8.0, height/8.0], egui::Label::new(format!("Threads [1-{}]:", state.max_threads))); ui.add_sized([width/8.0, height/8.0], egui::Label::new(format!("Threads [1-{}]:", self.max_threads)));
ui.add_sized([width, height/8.0], egui::Slider::new(&mut state.current_threads, 1..=state.max_threads)).on_hover_text(XMRIG_THREADS); ui.add_sized([width, height/8.0], egui::Slider::new(&mut self.current_threads, 1..=self.max_threads)).on_hover_text(XMRIG_THREADS);
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add_sized([width/8.0, height/8.0], egui::Label::new("CPU Priority [0-5]:")); ui.add_sized([width/8.0, height/8.0], egui::Label::new("CPU Priority [0-5]:"));
ui.add_sized([width, height/8.0], egui::Slider::new(&mut state.priority, 0..=5)).on_hover_text(XMRIG_PRIORITY); ui.add_sized([width, height/8.0], egui::Slider::new(&mut self.priority, 0..=5)).on_hover_text(XMRIG_PRIORITY);
}); });
// }); // });
// ui.group(|ui| { // ui.group(|ui| {
if state.manual == false { ui.set_enabled(false); } if self.simple == false { ui.set_enabled(false); }
let width = (width/4.0); let width = (width/4.0);
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add_sized([width/8.0, height/8.0], egui::Label::new("Pool IP:")); ui.add_sized([width/8.0, height/8.0], egui::Label::new("Pool IP:"));
ui.spacing_mut().text_edit_width = ui.available_width() - 35.0; ui.spacing_mut().text_edit_width = ui.available_width() - 35.0;
ui.text_edit_singleline(&mut state.pool); ui.text_edit_singleline(&mut self.pool);
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add_sized([width/8.0, height/8.0], egui::Label::new("Address:")); ui.add_sized([width/8.0, height/8.0], egui::Label::new("Address:"));
ui.spacing_mut().text_edit_width = ui.available_width() - 35.0; ui.spacing_mut().text_edit_width = ui.available_width() - 35.0;
ui.text_edit_singleline(&mut state.address); ui.text_edit_singleline(&mut self.address);
}); });
// }); // });
} }