Status Submenu: (de)serialize logs in our own format

Instead of saving P2Pool payout logs as they are, they are now
(de)serialized in the same [Display] format, e.g:

<DATE> <TIME>            | <12_DOT_FLOAT> XMR | Block <BLOCK>
2022-09-31 12:53:52.8683 | 0.166122683521 XMR | Block 2,713,512

The parsing functions were updated to be able to read both raw
log lines and the new above format.
This commit is contained in:
hinto-janaiyo 2022-12-31 13:47:41 -05:00
parent e8751842ce
commit a3b6521415
No known key found for this signature in database
GPG key ID: B1C5A64B80691E45
7 changed files with 127 additions and 79 deletions

24
Cargo.lock generated
View file

@ -449,9 +449,9 @@ dependencies = [
[[package]]
name = "calloop"
version = "0.10.4"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19457a0da465234abd76134a5c2a910c14bd3c5558463e4396ab9a37a328e465"
checksum = "1a59225be45a478d772ce015d9743e49e92798ece9e34eda9a6aa2a6a7f40192"
dependencies = [
"log",
"nix 0.25.1",
@ -1841,9 +1841,9 @@ dependencies = [
[[package]]
name = "half"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad6a9459c9c30b177b925162351f97e7d967c7ea8bab3b8352805327daf45554"
checksum = "6c467d36af040b7b2681f5fddd27427f6da8d3d072f575a265e181d2f8e8d157"
dependencies = [
"crunchy",
]
@ -2665,9 +2665,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.16.0"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
[[package]]
name = "opaque-debug"
@ -3420,18 +3420,18 @@ checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
[[package]]
name = "serde"
version = "1.0.151"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.151"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
@ -3832,9 +3832,9 @@ dependencies = [
[[package]]
name = "sysinfo"
version = "0.27.1"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccb297c0afb439440834b4bcf02c5c9da8ec2e808e70f36b0d8e815ff403bd24"
checksum = "17351d0e9eb8841897b14e9669378f3c69fb57779cc04f8ca9a9d512edfb2563"
dependencies = [
"cfg-if",
"core-foundation-sys",

View file

@ -605,6 +605,7 @@ pub struct GupaxP2poolApi {
impl Default for GupaxP2poolApi { fn default() -> Self { Self::new() } }
impl GupaxP2poolApi {
//---------------------------------------------------------------------------------------------------- Init, these pretty much only get called once
pub fn new() -> Self {
Self {
log: String::new(),
@ -659,34 +660,6 @@ impl GupaxP2poolApi {
Ok(())
}
pub fn update_log_rev(&mut self) {
let mut log_rev = String::with_capacity(self.log.len());
for line in self.log.lines().rev() {
log_rev.push_str(line);
log_rev.push_str("\n");
}
self.log_rev = log_rev;
}
pub fn append_head_log_rev(&mut self, line: &str) {
self.log_rev = format!("{}\n{}", line, self.log_rev);
}
pub fn update_payout_low(&mut self) {
self.payout_ord.sort_payout_low_to_high();
self.payout_low = self.payout_ord.to_string();
}
pub fn update_payout_high(&mut self) {
self.payout_ord.sort_payout_high_to_low();
self.payout_high = self.payout_ord.to_string();
}
pub fn update_payout_strings(&mut self) {
self.update_payout_low();
self.update_payout_high();
}
pub fn read_all_files_and_update(&mut self) -> Result<(), TomlError> {
let payout_u64 = match read_to_string(File::Payout, &self.path_payout)?.trim().parse::<u64>() {
Ok(o) => o,
@ -711,10 +684,48 @@ impl GupaxP2poolApi {
Ok(())
}
// Takes the log line and (date, atomic_unit, block) and updates [self] and the [PayoutOrd]
pub fn add_payout(&mut self, log_payout_line: &str, date: String, atomic_unit: AtomicUnit, block: HumanNumber) {
self.log.push_str(log_payout_line);
self.append_head_log_rev(log_payout_line);
//---------------------------------------------------------------------------------------------------- Live, functions that actually update/write live stats
pub fn update_log_rev(&mut self) {
let mut log_rev = String::with_capacity(self.log.len());
for line in self.log.lines().rev() {
log_rev.push_str(line);
log_rev.push_str("\n");
}
self.log_rev = log_rev;
}
pub fn format_payout(date: &str, atomic_unit: &AtomicUnit, block: &HumanNumber) -> String {
format!("{} | {} XMR | Block {}", date, atomic_unit, block)
}
pub fn append_log(&mut self, formatted_log_line: &str) {
self.log.push_str(formatted_log_line);
self.log.push_str("\n");
}
pub fn append_head_log_rev(&mut self, formatted_log_line: &str) {
self.log_rev = format!("{}\n{}", formatted_log_line, self.log_rev);
}
pub fn update_payout_low(&mut self) {
self.payout_ord.sort_payout_low_to_high();
self.payout_low = self.payout_ord.to_string();
}
pub fn update_payout_high(&mut self) {
self.payout_ord.sort_payout_high_to_low();
self.payout_high = self.payout_ord.to_string();
}
pub fn update_payout_strings(&mut self) {
self.update_payout_low();
self.update_payout_high();
}
// Takes the (date, atomic_unit, block) and updates [self] and the [PayoutOrd]
pub fn add_payout(&mut self, formatted_log_line: &str, date: String, atomic_unit: AtomicUnit, block: HumanNumber) {
self.append_log(formatted_log_line);
self.append_head_log_rev(formatted_log_line);
self.payout_u64 += 1;
self.payout = HumanNumber::from_u64(self.payout_u64);
self.xmr = self.xmr.add_self(atomic_unit);
@ -722,20 +733,20 @@ impl GupaxP2poolApi {
self.update_payout_strings();
}
pub fn write_to_all_files(&self) -> Result<(), TomlError> {
pub fn write_to_all_files(&self, formatted_log_line: &str) -> Result<(), TomlError> {
Self::disk_overwrite(&self.payout_u64.to_string(), &self.path_payout)?;
Self::disk_overwrite(&self.xmr.to_string(), &self.path_xmr)?;
Self::disk_append(&self.log, &self.path_log)?;
Self::disk_append(formatted_log_line, &self.path_log)?;
Ok(())
}
pub fn disk_append(string: &str, path: &PathBuf) -> Result<(), TomlError> {
pub fn disk_append(formatted_log_line: &str, path: &PathBuf) -> Result<(), TomlError> {
use std::io::Write;
let mut file = match fs::OpenOptions::new().append(true).create(true).open(path) {
Ok(f) => f,
Err(e) => { error!("GupaxP2poolApi | Append [{}] ... FAIL: {}", path.display(), e); return Err(TomlError::Io(e)) },
};
match writeln!(file, "{}", string) {
match writeln!(file, "{}", formatted_log_line) {
Ok(_) => { debug!("GupaxP2poolApi | Append [{}] ... OK", path.display()); Ok(()) },
Err(e) => { error!("GupaxP2poolApi | Append [{}] ... FAIL: {}", path.display(), e); Err(TomlError::Io(e)) },
}
@ -1384,6 +1395,8 @@ mod test {
#[test]
fn create_and_serde_gupax_p2pool_api() {
use crate::disk::GupaxP2poolApi;
use crate::regex::P2poolRegex;
use crate::xmr::PayoutOrd;
use crate::xmr::AtomicUnit;
// Get API dir, fill paths.
@ -1398,7 +1411,9 @@ mod test {
api.log = "NOTICE 2022-01-27 01:30:23.1377 P2Pool You received a payout of 0.000000000001 XMR in block 2642816".to_string();
api.payout_u64 = 1;
api.xmr = AtomicUnit::from_u64(2);
GupaxP2poolApi::write_to_all_files(&api).unwrap();
let (date, atomic_unit, block) = PayoutOrd::parse_raw_payout_line(&api.log, &P2poolRegex::new());
let formatted_log_line = GupaxP2poolApi::format_payout(&date, &atomic_unit, &block);
GupaxP2poolApi::write_to_all_files(&api, &formatted_log_line).unwrap();
println!("AFTER WRITE: {:#?}", api);
// Read
@ -1409,7 +1424,7 @@ mod test {
assert_eq!(api.payout_u64, 1);
assert_eq!(api.xmr.to_u64(), 2);
assert!(!api.payout_ord.is_empty());
assert!(api.log.contains("2022-01-27 01:30:23.1377 P2Pool You received a payout of 0.000000000001 XMR in block 2642816\n"));
assert!(api.log.contains("2022-01-27 01:30:23.1377 | 0.000000000001 XMR | Block 2,642,816"));
}
#[test]

View file

@ -267,9 +267,10 @@ impl Helper {
// println!("{}", line); // For debugging.
if regex.payout.is_match(&line) {
debug!("P2Pool PTY | Found payout, attempting write: {}", line);
let (date, atomic_unit, block) = PayoutOrd::parse_line(&line, &regex);
GupaxP2poolApi::add_payout(&mut lock!(gupax_p2pool_api), &line, date, atomic_unit, block);
if let Err(e) = GupaxP2poolApi::write_to_all_files(&lock!(gupax_p2pool_api)) { error!("P2Pool PTY GupaxP2poolApi | Write error: {}", e); }
let (date, atomic_unit, block) = PayoutOrd::parse_raw_payout_line(&line, &regex);
let formatted_log_line = GupaxP2poolApi::format_payout(&date, &atomic_unit, &block);
GupaxP2poolApi::add_payout(&mut lock!(gupax_p2pool_api), &formatted_log_line, date, atomic_unit, block);
if let Err(e) = GupaxP2poolApi::write_to_all_files(&lock!(gupax_p2pool_api), &formatted_log_line) { error!("P2Pool PTY GupaxP2poolApi | Write error: {}", e); }
}
if let Err(e) = writeln!(lock!(output_parse), "{}", line) { error!("P2Pool PTY Parse | Output error: {}", e); }
if let Err(e) = writeln!(lock!(output_pub), "{}", line) { error!("P2Pool PTY Pub | Output error: {}", e); }

View file

@ -113,6 +113,9 @@ impl HumanNumber {
pub fn unknown() -> Self {
Self("???".to_string())
}
pub fn from_str(s: &str) -> Self {
Self(s.to_string())
}
pub fn to_percent(f: f32) -> Self {
if f < 0.01 {
Self("0%".to_string())

View file

@ -74,6 +74,7 @@ pub struct P2poolRegex {
pub payout_float: regex::Regex,
pub block: regex::Regex,
pub block_int: regex::Regex,
pub block_comma: regex::Regex,
}
impl P2poolRegex {
@ -84,6 +85,7 @@ impl P2poolRegex {
payout_float: regex::Regex::new("[0-9].[0-9]{12}").unwrap(), // Assumes 12 digits after the dot.
block: regex::Regex::new("block [0-9]{7}").unwrap(), // Monero blocks will be 7 digits for... the next 10,379 years
block_int: regex::Regex::new("[0-9]{7}").unwrap(),
block_comma: regex::Regex::new("[0-9],[0-9]{3},[0-9]{3}").unwrap(),
}
}
}
@ -115,10 +117,12 @@ mod test {
use regex::Regex;
let r = crate::P2poolRegex::new();
let text = "NOTICE 2022-11-11 11:11:11.1111 P2Pool You received a payout of 0.111111111111 XMR in block 1111111";
let text2 = "2022-11-11 11:11:11.1111 | 0.111111111111 XMR | Block 1,111,111";
assert_eq!(r.payout.find(text).unwrap().as_str(), "payout of 0.111111111111 XMR");
assert_eq!(r.payout_float.find(text).unwrap().as_str(), "0.111111111111");
assert_eq!(r.date.find(text).unwrap().as_str(), "2022-11-11 11:11:11.1111");
assert_eq!(r.block.find(text).unwrap().as_str(), "block 1111111");
assert_eq!(r.block_int.find(text).unwrap().as_str(), "1111111");
assert_eq!(r.block_comma.find(text2).unwrap().as_str(), "1,111,111");
}
}

View file

@ -165,8 +165,8 @@ pub fn show(&mut self, sys: &Arc<Mutex<Sys>>, p2pool_api: &Arc<Mutex<PubP2poolAp
egui::Frame::none().fill(DARK_GRAY).show(ui, |ui| {
egui::ScrollArea::vertical().stick_to_bottom(self.payout_view == PayoutView::Oldest).max_width(width).max_height(log).auto_shrink([false; 2]).show_viewport(ui, |ui, _| {
match self.payout_view {
PayoutView::Latest => ui.add_sized([width, log], TextEdit::multiline(&mut api.log.as_str())),
PayoutView::Oldest => ui.add_sized([width, log], TextEdit::multiline(&mut api.log_rev.as_str())),
PayoutView::Latest => ui.add_sized([width, log], TextEdit::multiline(&mut api.log_rev.as_str())),
PayoutView::Oldest => ui.add_sized([width, log], TextEdit::multiline(&mut api.log.as_str())),
PayoutView::Biggest => ui.add_sized([width, log], TextEdit::multiline(&mut api.payout_high.as_str())),
PayoutView::Smallest => ui.add_sized([width, log], TextEdit::multiline(&mut api.payout_low.as_str())),
};
@ -191,14 +191,15 @@ pub fn show(&mut self, sys: &Arc<Mutex<Sys>>, p2pool_api: &Arc<Mutex<PubP2poolAp
ui.separator();
if ui.add_sized([button, text], SelectableLabel::new(self.hash_metric == Hash::Giga, "Giga")).on_hover_text(STATUS_SUBMENU_GIGA).clicked() { self.hash_metric = Hash::Giga; }
ui.separator();
ui.spacing_mut().slider_width = button*12.5;
ui.spacing_mut().slider_width = button*11.5;
ui.add_sized([button*14.0, text], Slider::new(&mut self.hashrate, 1.0..=1_000.0));
})});
let api = lock!(p2pool_api);
// Actual stats
ui.set_enabled(p2pool_alive);
let text = height / 25.0;
let width = (width/3.0)-(SPACE*1.666);
let min_height = ui.available_height()/1.25;
let api = lock!(p2pool_api);
ui.horizontal(|ui| {
ui.group(|ui| { ui.vertical(|ui| {
ui.set_min_height(min_height);
@ -206,19 +207,19 @@ pub fn show(&mut self, sys: &Arc<Mutex<Sys>>, p2pool_api: &Arc<Mutex<PubP2poolAp
let hashrate = Hash::convert_to_hash(self.hashrate, self.hash_metric) as u64;
let p2pool_share_mean = PubP2poolApi::calculate_share_or_block_time(hashrate, api.p2pool_difficulty_u64);
let solo_block_mean = PubP2poolApi::calculate_share_or_block_time(hashrate, api.monero_difficulty_u64);
ui.add_sized([width, text], Label::new(RichText::new("P2Pool Block Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN);
ui.add_sized([width, text], Label::new(api.p2pool_block_mean.to_string()));
ui.add_sized([width, text], Label::new(RichText::new("Your P2Pool Hashrate").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_HASHRATE);
ui.add_sized([width, text], Label::new(format!("{} H/s", HumanNumber::from_u64(hashrate))));
ui.add_sized([width, text], Label::new(RichText::new("P2Pool Block Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN);
ui.add_sized([width, text], Label::new(api.p2pool_block_mean.to_string()));
ui.add_sized([width, text], Label::new(RichText::new("Your P2Pool Share Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN);
ui.add_sized([width, text], Label::new(p2pool_share_mean.to_string()));
ui.add_sized([width, text], Label::new(RichText::new("Your Solo Block Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN);
ui.add_sized([width, text], Label::new(solo_block_mean.to_string()));
} else {
ui.add_sized([width, text], Label::new(RichText::new("P2Pool Block Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN);
ui.add_sized([width, text], Label::new(api.p2pool_block_mean.to_string()));
ui.add_sized([width, text], Label::new(RichText::new("Your P2Pool Hashrate").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_YOUR_P2POOL_HASHRATE);
ui.add_sized([width, text], Label::new(format!("{} H/s", api.hashrate_1h)));
ui.add_sized([width, text], Label::new(RichText::new("P2Pool Block Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_BLOCK_MEAN);
ui.add_sized([width, text], Label::new(api.p2pool_block_mean.to_string()));
ui.add_sized([width, text], Label::new(RichText::new("Your P2Pool Share Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_P2POOL_SHARE_MEAN);
ui.add_sized([width, text], Label::new(api.p2pool_share_mean.to_string()));
ui.add_sized([width, text], Label::new(RichText::new("Your Solo Block Mean").underline().color(BONE))).on_hover_text(STATUS_SUBMENU_SOLO_BLOCK_MEAN);

View file

@ -140,7 +140,8 @@ impl PayoutOrd {
self.0.is_empty()
}
pub fn parse_line(line: &str, regex: &P2poolRegex) -> (String, AtomicUnit, HumanNumber) {
// Expected input: "NOTICE 2022-01-27 01:30:23.1377 P2Pool You received a payout of 0.000000000001 XMR in block 2642816"
pub fn parse_raw_payout_line(line: &str, regex: &P2poolRegex) -> (String, AtomicUnit, HumanNumber) {
// Date
let date = match regex.date.find(line) {
Some(date) => date.as_str().to_string(),
@ -154,9 +155,11 @@ impl PayoutOrd {
Err(e) => { error!("P2Pool | AtomicUnit parse error: [{}] on [{}]", e, line); AtomicUnit::new() },
}
} else {
error!("P2Pool | AtomicUnit parse error: [{}]", line);
AtomicUnit::new()
}
} else {
error!("P2Pool | AtomicUnit parse error: [{}]", line);
AtomicUnit::new()
};
// Block
@ -167,31 +170,57 @@ impl PayoutOrd {
Err(e) => { error!("P2Pool | Block parse error: [{}] on [{}]", e, line); HumanNumber::unknown() },
}
} else {
error!("P2Pool | Block parse error: [{}]", line);
HumanNumber::unknown()
}
} else {
error!("P2Pool | Block parse error: [{}]", line);
HumanNumber::unknown()
};
(date, atomic_unit, block)
}
// Takes in input of ONLY P2Pool payout logs and
// converts it into a usable [PayoutOrd]
// It expects log lines like this:
// "NOTICE 2022-04-11 00:20:17.2571 P2Pool You received a payout of 0.001371623621 XMR in block 2562511"
// Expected input: "2022-01-27 01:30:23.1377 | 0.000000000001 XMR | Block 2,642,816"
pub fn parse_formatted_payout_line(line: &str, regex: &P2poolRegex) -> (String, AtomicUnit, HumanNumber) {
// Date
let date = match regex.date.find(line) {
Some(date) => date.as_str().to_string(),
None => { error!("P2Pool | Date parse error: [{}]", line); "????-??-?? ??:??:??.????".to_string() },
};
// AtomicUnit
let atomic_unit = if let Some(word) = regex.payout_float.find(line) {
match word.as_str().parse::<f64>() {
Ok(au) => AtomicUnit::from_f64(au),
Err(e) => { error!("P2Pool | AtomicUnit parse error: [{}] on [{}]", e, line); AtomicUnit::new() },
}
} else {
error!("P2Pool | AtomicUnit parse error: [{}]", line);
AtomicUnit::new()
};
// Block
let block = match regex.block_comma.find(line) {
Some(b) => HumanNumber::from_str(b.as_str()),
None => { error!("P2Pool | Block parse error: [{}]", line); HumanNumber::unknown() },
};
(date, atomic_unit, block)
}
// Takes in input of ONLY P2Pool payout logs and converts it into a usable [PayoutOrd]
// It expects formatted log lines like this: "2022-04-11 00:20:17.2571 | 0.001371623621 XMR | Block 2,562,511"
// For efficiency reasons, I'd like to know the byte size
// we should allocate for the vector so we aren't adding every loop.
// Given a log [str], the equation for how many bytes the final vec will be is:
// (BYTES_OF_DATE + BYTES OF XMR + BYTES OF BLOCK) * amount_of_lines
// (BYTES_OF_DATE + BYTES OF XMR + BYTES OF BLOCK) + (SPACES, PIPES, MISC WORDS) * amount_of_lines
// The first three are more or less constants (monero block 10m is in 10,379 years...): [23, 14, 7] (sum: 44)
// Add 16 more bytes for wrapper type overhead and it's an even [60] bytes per line.
// Spaces, pipes, commas and words (XMR, Block): [19]
// Add 7 more bytes for wrapper type overhead and it's an even [70] bytes per line.
pub fn update_from_payout_log(&mut self, log: &str) {
let regex = P2poolRegex::new();
let amount_of_lines = log.lines().count();
let mut vec: Vec<(String, AtomicUnit, HumanNumber)> = Vec::with_capacity(60 * amount_of_lines);
let mut vec: Vec<(String, AtomicUnit, HumanNumber)> = Vec::with_capacity(70 * amount_of_lines);
for line in log.lines() {
debug!("PayoutOrg | Parsing line: [{}]", line);
vec.push(Self::parse_line(line, &regex));
vec.push(Self::parse_formatted_payout_line(line, &regex));
}
*self = Self(vec);
}
@ -259,20 +288,15 @@ mod test {
fn update_p2pool_payout_log() {
use crate::xmr::PayoutOrd;
let log =
r#"NOTICE 2021-12-21 01:01:01.1111 P2Pool You received a payout of 0.001000000000 XMR in block 1234567
NOTICE 2021-12-21 02:01:01.1111 P2Pool You received a payout of 0.002000000000 XMR in block 2345678
NOTICE 2021-12-21 03:01:01.1111 P2Pool You received a payout of 0.003000000000 XMR in block 3456789
r#"2021-12-21 01:01:01.1111 | 0.001000000000 XMR | Block 1,234,567
2021-12-21 02:01:01.1111 | 0.002000000000 XMR | Block 2,345,678
2021-12-21 03:01:01.1111 | 0.003000000000 XMR | Block 3,456,789
"#;
let mut payout_ord = PayoutOrd::new();
println!("BEFORE: {}", payout_ord);
PayoutOrd::update_from_payout_log(&mut payout_ord, log);
println!("AFTER: {}", payout_ord);
let should_be =
r#"2021-12-21 01:01:01.1111 | 0.001000000000 XMR | Block 1,234,567
2021-12-21 02:01:01.1111 | 0.002000000000 XMR | Block 2,345,678
2021-12-21 03:01:01.1111 | 0.003000000000 XMR | Block 3,456,789
"#;
assert_eq!(payout_ord.to_string(), should_be)
assert_eq!(payout_ord.to_string(), log);
}
#[test]