Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show information about multiple farms in UI during normal operation (no configuration yet) #55

Merged
merged 1 commit into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 111 additions & 62 deletions src/frontend/running.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
mod farm;

use crate::backend::config::RawConfig;
use crate::backend::farmer::{PlottingKind, PlottingState};
use crate::backend::node::{SyncKind, SyncState};
use crate::backend::{FarmerNotification, NodeNotification};
use crate::frontend::running::farm::{FarmWidget, FarmWidgetInit, FarmWidgetInput};
use gtk::prelude::*;
use relm4::factory::FactoryHashMap;
use relm4::prelude::*;
use subspace_core_primitives::BlockNumber;
use tracing::warn;
Expand All @@ -15,6 +20,7 @@ pub enum RunningInput {
Initialize {
best_block_number: BlockNumber,
initial_plotting_states: Vec<PlottingState>,
raw_config: RawConfig,
},
NodeNotification(NodeNotification),
FarmerNotification(FarmerNotification),
Expand All @@ -37,6 +43,7 @@ struct FarmerState {
pub struct RunningView {
node_state: NodeState,
farmer_state: FarmerState,
farms: FactoryHashMap<usize, FarmWidget>,
}

#[relm4::component(pub)]
Expand All @@ -62,7 +69,6 @@ impl Component for RunningView {
set_label: "Consensus node",
},

// TODO: Match only because `if let Some(x) = y` is not yet supported here: https://github.com/Relm4/Relm4/issues/582
#[transition = "SlideUpDown"]
match model.node_state.sync_state {
SyncState::Unknown => gtk::Box {
Expand All @@ -79,6 +85,8 @@ impl Component for RunningView {
set_spacing: 10,

gtk::Box {
set_spacing: 5,

gtk::Label {
set_halign: gtk::Align::Start,

Expand All @@ -102,7 +110,6 @@ impl Component for RunningView {
},

gtk::Spinner {
set_margin_start: 5,
start: (),
},
},
Expand Down Expand Up @@ -139,86 +146,83 @@ impl Component for RunningView {
// TODO: Render all farms, not just the first one
// TODO: Match only because `if let Some(x) = y` is not yet supported here: https://github.com/Relm4/Relm4/issues/582
#[transition = "SlideUpDown"]
match (
model.farmer_state.piece_cache_sync_progress,
model.farmer_state.plotting_state.get(0).copied().unwrap_or_default(),
model.node_state.sync_state
) {
(progress, _, _) if progress < 100.0 => gtk::Box {
if model.farmer_state.piece_cache_sync_progress < 100.0 {
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 10,

gtk::Box {
gtk::Label {
set_halign: gtk::Align::Start,

#[watch]
set_label: &format!("Piece cache sync {progress:.2}%"),
set_tooltip: "Plotting starts after piece cache sync is complete",
},
set_spacing: 5,
set_tooltip: "Plotting starts after piece cache sync is complete",

gtk::Spinner {
set_margin_start: 5,
start: (),
},
},

gtk::ProgressBar {
#[watch]
set_fraction: progress as f64 / 100.0,
},
},
(_, PlottingState::Plotting { kind, progress, speed }, SyncState::Idle) => gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 10,

gtk::Box {
gtk::Label {
set_halign: gtk::Align::Start,

#[watch]
set_label: &{
let kind = match kind {
PlottingKind::Initial => "Initial plotting, not farming",
PlottingKind::Replotting => "Replotting, farming",
};

format!(
"{} {:.2}%{}",
kind,
progress,
speed
.map(|speed| format!(", {:.2} sectors/h", 3600.0 / speed))
.unwrap_or_default(),
)
},
set_tooltip: "Farming starts after initial plotting is complete",
set_label: &format!(
"Piece cache sync {:.2}%",
model.farmer_state.piece_cache_sync_progress
),
},

gtk::Spinner {
set_margin_start: 5,
start: (),
},
},

gtk::ProgressBar {
#[watch]
set_fraction: progress as f64 / 100.0,
set_fraction: model.farmer_state.piece_cache_sync_progress as f64 / 100.0,
},
},
(_, PlottingState::Idle, SyncState::Idle) => gtk::Box {
gtk::Label {
#[watch]
set_label: "Farming",
}
},
_ => gtk::Box {
gtk::Label {
#[watch]
set_label: "Waiting for node to sync first",
}
},
}
} else {
gtk::Label {
set_halign: gtk::Align::Start,
#[watch]
set_label: &{
if matches!(model.node_state.sync_state, SyncState::Idle) {
let mut statuses = Vec::new();
let plotting = model.farmer_state.plotting_state.iter().any(|plotting_state| {
matches!(plotting_state, PlottingState::Plotting { kind: PlottingKind::Initial, .. })
});
let replotting = model.farmer_state.plotting_state.iter().any(|plotting_state| {
matches!(plotting_state, PlottingState::Plotting { kind: PlottingKind::Replotting, .. })
});
let idle = model.farmer_state.plotting_state.iter().any(|plotting_state| {
matches!(plotting_state, PlottingState::Idle)
});
if plotting {
statuses.push(if statuses.is_empty() {
"Plotting"
} else {
"plotting"
});
}
if matches!(model.node_state.sync_state, SyncState::Idle) && (replotting || idle) {
statuses.push(if statuses.is_empty() {
"Farming"
} else {
"farming"
});
}
if replotting {
statuses.push(if statuses.is_empty() {
"Replotting"
} else {
"replotting"
});
}

statuses.join(", ")
} else {
"Waiting for node to sync first".to_string()
}
},
// TODO: Show summarized state of all farms: Plotting, Replotting, Farming
}
},

model.farms.widget(),
},
}
}
Expand All @@ -228,9 +232,21 @@ impl Component for RunningView {
_root: &Self::Root,
_sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let farms = FactoryHashMap::builder()
.launch(
gtk::Box::builder()
.margin_start(10)
.margin_end(10)
.orientation(gtk::Orientation::Vertical)
.spacing(10)
.build(),
)
.detach();

let model = Self {
node_state: NodeState::default(),
farmer_state: FarmerState::default(),
farms,
};

let widgets = view_output!();
Expand All @@ -249,7 +265,23 @@ impl RunningView {
RunningInput::Initialize {
best_block_number,
initial_plotting_states,
raw_config,
} => {
for (farm_index, (initial_plotting_state, farm)) in initial_plotting_states
.iter()
.copied()
.zip(raw_config.farms().iter().cloned())
.enumerate()
{
self.farms.insert(
farm_index,
FarmWidgetInit {
initial_plotting_state,
farm,
},
);
}

self.node_state = NodeState {
best_block_number,
sync_state: SyncState::default(),
Expand Down Expand Up @@ -282,6 +314,13 @@ impl RunningView {
}
}
}

let old_synced = matches!(self.node_state.sync_state, SyncState::Idle);
let new_synced = matches!(sync_state, SyncState::Idle);
if old_synced != new_synced {
self.farms
.broadcast(FarmWidgetInput::NodeSynced(new_synced));
}
self.node_state.sync_state = sync_state;
}
NodeNotification::BlockImported { number } => {
Expand All @@ -295,6 +334,9 @@ impl RunningView {
},
RunningInput::FarmerNotification(farmer_notification) => match farmer_notification {
FarmerNotification::PlottingStateUpdate { farm_index, state } => {
self.farms
.send(&farm_index, FarmWidgetInput::PlottingStateUpdate(state));

if let Some(plotting_state) =
self.farmer_state.plotting_state.get_mut(farm_index)
{
Expand All @@ -304,6 +346,13 @@ impl RunningView {
}
}
FarmerNotification::PieceCacheSyncProgress { progress } => {
let old_synced = self.farmer_state.piece_cache_sync_progress == 100.0;
let new_synced = progress == 100.0;
if old_synced != new_synced {
self.farms
.broadcast(FarmWidgetInput::PieceCacheSynced(new_synced));
}

self.farmer_state.piece_cache_sync_progress = progress;
}
},
Expand Down
Loading