#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(dead_code)]
use std::sync::Mutex;
use crate::ported::crt::{ColorElements, ColorScheme};
use crate::ported::meter::Meter_humanUnit;
use crate::ported::richstring::{
RichString, RichString_appendAscii, RichString_appendnAscii, RichString_writeAscii,
};
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum MeterRateStatus {
RATESTATUS_DATA,
RATESTATUS_INIT,
RATESTATUS_NODATA,
RATESTATUS_STALE,
}
struct NetworkIOMeterState {
status: MeterRateStatus,
cached_rxb_diff: f64,
cached_rxb_diff_str: String,
cached_rxp_diff: u32,
cached_txb_diff: f64,
cached_txb_diff_str: String,
cached_txp_diff: u32,
}
static NETWORK_IO_METER_STATE: Mutex<NetworkIOMeterState> = Mutex::new(NetworkIOMeterState {
status: MeterRateStatus::RATESTATUS_INIT,
cached_rxb_diff: 0.0,
cached_rxb_diff_str: String::new(),
cached_rxp_diff: 0,
cached_txb_diff: 0.0,
cached_txb_diff_str: String::new(),
cached_txp_diff: 0,
});
#[derive(Default)]
pub struct NetworkIOData {
pub bytesReceived: u64,
pub packetsReceived: u64,
pub bytesTransmitted: u64,
pub packetsTransmitted: u64,
}
const ONE_K: f64 = 1024.0;
static NET_UPDATE_CACHE: Mutex<(u64, u64, u64, u64, u64)> = Mutex::new((0, 0, 0, 0, 0));
pub fn NetworkIOMeter_updateValues(this: &mut crate::ported::meter::Meter) {
let realtime_ms = unsafe { (*this.host).realtimeMs };
let mut c = NET_UPDATE_CACHE.lock().unwrap();
let passed_time_ms = realtime_ms.wrapping_sub(c.0);
let mut has_new_data = false;
let mut data = NetworkIOData::default();
let mut st = NETWORK_IO_METER_STATE.lock().unwrap();
if passed_time_ms > 500 {
has_new_data = crate::ported::linux::platform::Platform_getNetworkIO(&mut data);
st.status = if !has_new_data {
MeterRateStatus::RATESTATUS_NODATA
} else if c.0 == 0 {
MeterRateStatus::RATESTATUS_INIT
} else if passed_time_ms > 30000 {
MeterRateStatus::RATESTATUS_STALE
} else {
MeterRateStatus::RATESTATUS_DATA
};
c.0 = realtime_ms;
}
if has_new_data {
if st.status != MeterRateStatus::RATESTATUS_INIT {
st.cached_rxb_diff = if data.bytesReceived > c.1 {
((1000 * (data.bytesReceived - c.1)) / passed_time_ms) as f64
} else {
0.0
};
st.cached_rxb_diff_str = Meter_humanUnit(st.cached_rxb_diff / ONE_K);
st.cached_rxp_diff = if data.packetsReceived > c.2 {
((1000 * (data.packetsReceived - c.2)) / passed_time_ms) as u32
} else {
0
};
st.cached_txb_diff = if data.bytesTransmitted > c.3 {
((1000 * (data.bytesTransmitted - c.3)) / passed_time_ms) as f64
} else {
0.0
};
st.cached_txb_diff_str = Meter_humanUnit(st.cached_txb_diff / ONE_K);
st.cached_txp_diff = if data.packetsTransmitted > c.4 {
((1000 * (data.packetsTransmitted - c.4)) / passed_time_ms) as u32
} else {
0
};
}
c.1 = data.bytesReceived;
c.2 = data.packetsReceived;
c.3 = data.bytesTransmitted;
c.4 = data.packetsTransmitted;
}
this.values[0] = st.cached_rxb_diff;
this.values[1] = st.cached_txb_diff;
match st.status {
MeterRateStatus::RATESTATUS_NODATA => {
this.txtBuffer = "no data".to_string();
return;
}
MeterRateStatus::RATESTATUS_INIT => {
this.txtBuffer = "init".to_string();
return;
}
MeterRateStatus::RATESTATUS_STALE => {
this.txtBuffer = "stale".to_string();
return;
}
MeterRateStatus::RATESTATUS_DATA => {}
}
this.txtBuffer = format!(
"rx:{}iB/s tx:{}iB/s ({}/{}pps)",
st.cached_rxb_diff_str, st.cached_txb_diff_str, st.cached_rxp_diff, st.cached_txp_diff
);
}
pub fn NetworkIOMeter_display(out: &mut RichString) {
let scheme = ColorScheme::active();
let state = NETWORK_IO_METER_STATE.lock().unwrap();
match state.status {
MeterRateStatus::RATESTATUS_NODATA => {
RichString_writeAscii(
out,
ColorElements::METER_VALUE_ERROR.packed(scheme),
b"no data",
);
return;
}
MeterRateStatus::RATESTATUS_INIT => {
RichString_writeAscii(
out,
ColorElements::METER_VALUE.packed(scheme),
b"initializing...",
);
return;
}
MeterRateStatus::RATESTATUS_STALE => {
RichString_writeAscii(
out,
ColorElements::METER_VALUE_WARN.packed(scheme),
b"stale data",
);
return;
}
MeterRateStatus::RATESTATUS_DATA => {}
}
RichString_writeAscii(out, ColorElements::METER_TEXT.packed(scheme), b"rx: ");
RichString_appendAscii(
out,
ColorElements::METER_VALUE_IOREAD.packed(scheme),
state.cached_rxb_diff_str.as_bytes(),
);
RichString_appendAscii(
out,
ColorElements::METER_VALUE_IOREAD.packed(scheme),
b"iB/s",
);
RichString_appendAscii(out, ColorElements::METER_TEXT.packed(scheme), b" tx: ");
RichString_appendAscii(
out,
ColorElements::METER_VALUE_IOWRITE.packed(scheme),
state.cached_txb_diff_str.as_bytes(),
);
RichString_appendAscii(
out,
ColorElements::METER_VALUE_IOWRITE.packed(scheme),
b"iB/s",
);
RichString_appendAscii(out, ColorElements::METER_TEXT.packed(scheme), b" (");
let buffer = format!("{}", state.cached_rxp_diff);
RichString_appendnAscii(
out,
ColorElements::METER_VALUE_IOREAD.packed(scheme),
buffer.as_bytes(),
buffer.len(),
);
RichString_appendAscii(out, ColorElements::METER_TEXT.packed(scheme), b"/");
let buffer = format!("{}", state.cached_txp_diff);
RichString_appendnAscii(
out,
ColorElements::METER_VALUE_IOWRITE.packed(scheme),
buffer.as_bytes(),
buffer.len(),
);
RichString_appendAscii(out, ColorElements::METER_TEXT.packed(scheme), b" pps)");
}
#[cfg(test)]
mod tests {
use super::*;
static TEST_LOCK: Mutex<()> = Mutex::new(());
fn text(r: &RichString) -> String {
(0..r.chlen as usize).map(|i| r.chptr[i].chars).collect()
}
fn set_state(status: MeterRateStatus, rxb: &str, txb: &str, rxp: u32, txp: u32) {
let mut s = NETWORK_IO_METER_STATE.lock().unwrap();
s.status = status;
s.cached_rxb_diff_str = rxb.to_string();
s.cached_txb_diff_str = txb.to_string();
s.cached_rxp_diff = rxp;
s.cached_txp_diff = txp;
}
#[test]
fn display_status_words() {
let _g = TEST_LOCK.lock().unwrap();
set_state(MeterRateStatus::RATESTATUS_NODATA, "", "", 0, 0);
let mut out = RichString::new();
NetworkIOMeter_display(&mut out);
assert_eq!(text(&out), "no data");
set_state(MeterRateStatus::RATESTATUS_INIT, "", "", 0, 0);
let mut out = RichString::new();
NetworkIOMeter_display(&mut out);
assert_eq!(text(&out), "initializing...");
set_state(MeterRateStatus::RATESTATUS_STALE, "", "", 0, 0);
let mut out = RichString::new();
NetworkIOMeter_display(&mut out);
assert_eq!(text(&out), "stale data");
}
#[test]
fn display_data_line() {
let _g = TEST_LOCK.lock().unwrap();
set_state(MeterRateStatus::RATESTATUS_DATA, "1.23G", "45.6M", 120, 34);
let mut out = RichString::new();
NetworkIOMeter_display(&mut out);
assert_eq!(text(&out), "rx: 1.23GiB/s tx: 45.6MiB/s (120/34 pps)");
}
#[test]
fn update_first_sample_status() {
use crate::ported::linux::linuxmachine::LinuxMachine;
use crate::ported::machine::Machine;
use crate::ported::meter::Meter;
let host = Box::leak(Box::new(LinuxMachine {
super_: Machine {
realtimeMs: 1000,
..Default::default()
},
..Default::default()
}));
let mut m = Meter {
values: vec![0.0; 2],
host: &host.super_ as *const crate::ported::machine::Machine,
..Meter::empty()
};
super::NetworkIOMeter_updateValues(&mut m);
assert!(
m.txtBuffer == "init" || m.txtBuffer == "no data" || m.txtBuffer.starts_with("rx:")
);
}
}