2023-05-04 20:09:26 +03:00
|
|
|
// Copyright 2023 The Grim Developers
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2023-06-23 22:12:30 +03:00
|
|
|
use std::net::{IpAddr, Ipv4Addr, SocketAddrV4, TcpListener};
|
|
|
|
use std::str::FromStr;
|
2023-05-04 20:09:26 +03:00
|
|
|
use std::time::Duration;
|
2023-05-17 00:36:59 +03:00
|
|
|
|
2023-06-26 18:51:19 +03:00
|
|
|
use egui::{Color32, lerp, Rgba, RichText};
|
2023-05-04 20:09:26 +03:00
|
|
|
use egui::style::Margin;
|
|
|
|
use egui_extras::{Size, StripBuilder};
|
|
|
|
use grin_chain::SyncStatus;
|
2023-05-17 00:36:59 +03:00
|
|
|
|
2023-06-03 11:22:51 +03:00
|
|
|
use crate::gui::{Colors, Navigator};
|
2023-06-19 01:29:15 +03:00
|
|
|
use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE};
|
2023-05-04 20:09:26 +03:00
|
|
|
use crate::gui::platform::PlatformCallbacks;
|
2023-06-26 18:51:19 +03:00
|
|
|
use crate::gui::views::{Modal, ModalContainer, View};
|
2023-05-18 03:53:38 +03:00
|
|
|
use crate::gui::views::network_metrics::NetworkMetrics;
|
2023-06-03 11:22:51 +03:00
|
|
|
use crate::gui::views::network_mining::NetworkMining;
|
2023-05-04 20:09:26 +03:00
|
|
|
use crate::gui::views::network_node::NetworkNode;
|
2023-06-17 13:23:31 +03:00
|
|
|
use crate::gui::views::network_settings::NetworkSettings;
|
2023-06-26 18:51:19 +03:00
|
|
|
use crate::gui::views::settings_stratum::StratumServerSetup;
|
2023-05-04 20:09:26 +03:00
|
|
|
use crate::node::Node;
|
2023-06-17 13:23:31 +03:00
|
|
|
use crate::Settings;
|
|
|
|
|
|
|
|
pub trait NetworkTab {
|
|
|
|
fn get_type(&self) -> NetworkTabType;
|
2023-06-21 02:13:47 +03:00
|
|
|
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks);
|
2023-06-23 22:12:30 +03:00
|
|
|
fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks);
|
2023-06-17 13:23:31 +03:00
|
|
|
}
|
2023-05-04 20:09:26 +03:00
|
|
|
|
2023-05-17 00:36:59 +03:00
|
|
|
#[derive(PartialEq)]
|
2023-06-17 13:23:31 +03:00
|
|
|
pub enum NetworkTabType {
|
2023-05-04 20:09:26 +03:00
|
|
|
Node,
|
|
|
|
Metrics,
|
2023-06-03 11:22:51 +03:00
|
|
|
Mining,
|
2023-06-17 13:23:31 +03:00
|
|
|
Settings
|
2023-05-04 20:09:26 +03:00
|
|
|
}
|
|
|
|
|
2023-06-19 01:29:15 +03:00
|
|
|
impl NetworkTabType {
|
|
|
|
pub fn name(&self) -> String {
|
|
|
|
match *self {
|
|
|
|
NetworkTabType::Node => { t!("network.node") }
|
|
|
|
NetworkTabType::Metrics => { t!("network.metrics") }
|
|
|
|
NetworkTabType::Mining => { t!("network.mining") }
|
|
|
|
NetworkTabType::Settings => { t!("network.settings") }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-04 20:09:26 +03:00
|
|
|
pub struct Network {
|
2023-06-17 13:23:31 +03:00
|
|
|
current_tab: Box<dyn NetworkTab>,
|
2023-06-26 18:51:19 +03:00
|
|
|
allowed_modal_ids: Vec<&'static str>,
|
2023-05-04 20:09:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Network {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2023-06-17 13:23:31 +03:00
|
|
|
current_tab: Box::new(NetworkNode::default()),
|
2023-06-26 18:51:19 +03:00
|
|
|
allowed_modal_ids: vec![
|
|
|
|
StratumServerSetup::STRATUM_PORT_MODAL
|
|
|
|
]
|
2023-05-04 20:09:26 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-26 18:51:19 +03:00
|
|
|
impl ModalContainer for Network {
|
|
|
|
fn allowed_modal_ids(&self) -> &Vec<&'static str> {
|
|
|
|
self.allowed_modal_ids.as_ref()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-04 20:09:26 +03:00
|
|
|
impl Network {
|
2023-06-21 02:13:47 +03:00
|
|
|
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
2023-06-26 18:51:19 +03:00
|
|
|
// Show modal content if it's opened.
|
|
|
|
let modal_id = Navigator::is_modal_open();
|
|
|
|
if modal_id.is_some() && self.can_show_modal(modal_id.unwrap()) {
|
|
|
|
Navigator::modal_ui(ui, |ui, modal| {
|
2023-06-23 22:12:30 +03:00
|
|
|
self.current_tab.as_mut().on_modal_ui(ui, modal, cb);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-05-04 20:09:26 +03:00
|
|
|
egui::TopBottomPanel::top("network_title")
|
|
|
|
.resizable(false)
|
|
|
|
.frame(egui::Frame {
|
2023-06-03 11:22:51 +03:00
|
|
|
fill: Colors::YELLOW,
|
2023-05-04 20:09:26 +03:00
|
|
|
inner_margin: Margin::same(0.0),
|
|
|
|
outer_margin: Margin::same(0.0),
|
|
|
|
..Default::default()
|
|
|
|
})
|
|
|
|
.show_inside(ui, |ui| {
|
2023-05-23 23:45:16 +03:00
|
|
|
self.draw_title(ui, frame);
|
2023-05-04 20:09:26 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
egui::TopBottomPanel::bottom("network_tabs")
|
|
|
|
.frame(egui::Frame {
|
2023-06-02 21:19:34 +03:00
|
|
|
outer_margin: Margin::same(5.0),
|
2023-05-04 20:09:26 +03:00
|
|
|
.. Default::default()
|
|
|
|
})
|
|
|
|
.show_inside(ui, |ui| {
|
|
|
|
self.draw_tabs(ui);
|
|
|
|
});
|
|
|
|
|
2023-06-02 02:05:34 +03:00
|
|
|
egui::CentralPanel::default()
|
|
|
|
.frame(egui::Frame {
|
|
|
|
stroke: View::DEFAULT_STROKE,
|
|
|
|
inner_margin: Margin::same(4.0),
|
2023-06-03 11:22:51 +03:00
|
|
|
fill: Colors::WHITE,
|
2023-06-02 02:05:34 +03:00
|
|
|
.. Default::default()
|
|
|
|
})
|
|
|
|
.show_inside(ui, |ui| {
|
2023-06-21 02:13:47 +03:00
|
|
|
self.current_tab.ui(ui, cb);
|
2023-06-02 02:05:34 +03:00
|
|
|
});
|
2023-05-04 20:09:26 +03:00
|
|
|
}
|
|
|
|
|
2023-05-17 00:36:59 +03:00
|
|
|
fn draw_tabs(&mut self, ui: &mut egui::Ui) {
|
2023-06-02 02:05:34 +03:00
|
|
|
ui.scope(|ui| {
|
2023-06-02 21:19:34 +03:00
|
|
|
// Setup spacing between tabs.
|
|
|
|
ui.style_mut().spacing.item_spacing = egui::vec2(5.0, 0.0);
|
|
|
|
// Setup vertical padding inside tab button.
|
|
|
|
ui.style_mut().spacing.button_padding = egui::vec2(0.0, 3.0);
|
2023-06-02 02:05:34 +03:00
|
|
|
|
|
|
|
ui.columns(4, |columns| {
|
2023-06-02 21:19:34 +03:00
|
|
|
columns[0].vertical_centered_justified(|ui| {
|
2023-06-19 01:29:15 +03:00
|
|
|
View::tab_button(ui, DATABASE, self.is_current_tab(NetworkTabType::Node), || {
|
2023-06-17 13:23:31 +03:00
|
|
|
self.current_tab = Box::new(NetworkNode::default());
|
|
|
|
});
|
2023-05-17 00:36:59 +03:00
|
|
|
});
|
2023-06-02 21:19:34 +03:00
|
|
|
columns[1].vertical_centered_justified(|ui| {
|
2023-06-19 01:29:15 +03:00
|
|
|
View::tab_button(ui, GAUGE, self.is_current_tab(NetworkTabType::Metrics), || {
|
2023-06-17 13:23:31 +03:00
|
|
|
self.current_tab = Box::new(NetworkMetrics::default());
|
|
|
|
});
|
2023-05-04 20:09:26 +03:00
|
|
|
});
|
2023-06-02 21:19:34 +03:00
|
|
|
columns[2].vertical_centered_justified(|ui| {
|
2023-06-19 01:29:15 +03:00
|
|
|
View::tab_button(ui, FACTORY, self.is_current_tab(NetworkTabType::Mining), || {
|
2023-06-17 13:23:31 +03:00
|
|
|
self.current_tab = Box::new(NetworkMining::default());
|
|
|
|
});
|
2023-05-04 20:09:26 +03:00
|
|
|
});
|
2023-06-02 21:19:34 +03:00
|
|
|
columns[3].vertical_centered_justified(|ui| {
|
2023-06-19 01:29:15 +03:00
|
|
|
View::tab_button(ui, FADERS, self.is_current_tab(NetworkTabType::Settings), || {
|
2023-06-17 13:23:31 +03:00
|
|
|
self.current_tab = Box::new(NetworkSettings::default());
|
|
|
|
});
|
2023-05-04 20:09:26 +03:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-06-19 01:29:15 +03:00
|
|
|
fn is_current_tab(&self, tab_type: NetworkTabType) -> bool {
|
|
|
|
self.current_tab.get_type() == tab_type
|
|
|
|
}
|
|
|
|
|
2023-05-23 23:45:16 +03:00
|
|
|
fn draw_title(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
2023-05-04 20:09:26 +03:00
|
|
|
StripBuilder::new(ui)
|
|
|
|
.size(Size::exact(52.0))
|
|
|
|
.vertical(|mut strip| {
|
|
|
|
strip.strip(|builder| {
|
|
|
|
builder
|
|
|
|
.size(Size::exact(52.0))
|
|
|
|
.size(Size::remainder())
|
|
|
|
.size(Size::exact(52.0))
|
|
|
|
.horizontal(|mut strip| {
|
2023-05-17 00:36:59 +03:00
|
|
|
strip.cell(|ui| {
|
|
|
|
ui.centered_and_justified(|ui| {
|
2023-05-18 03:53:38 +03:00
|
|
|
View::title_button(ui, DOTS_THREE_OUTLINE_VERTICAL, || {
|
2023-05-17 00:36:59 +03:00
|
|
|
//TODO: Actions for node
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2023-05-04 20:09:26 +03:00
|
|
|
strip.strip(|builder| {
|
|
|
|
self.draw_title_text(builder);
|
|
|
|
});
|
|
|
|
strip.cell(|ui| {
|
2023-06-02 02:05:34 +03:00
|
|
|
if !View::is_dual_panel_mode(frame) {
|
2023-05-04 20:09:26 +03:00
|
|
|
ui.centered_and_justified(|ui| {
|
2023-05-18 03:53:38 +03:00
|
|
|
View::title_button(ui, CARDHOLDER, || {
|
2023-05-23 23:45:16 +03:00
|
|
|
Navigator::toggle_side_panel();
|
2023-05-17 00:36:59 +03:00
|
|
|
});
|
2023-05-04 20:09:26 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-05-17 00:36:59 +03:00
|
|
|
fn draw_title_text(&self, builder: StripBuilder) {
|
|
|
|
builder
|
|
|
|
.size(Size::remainder())
|
2023-06-02 02:05:34 +03:00
|
|
|
.size(Size::exact(32.0))
|
2023-05-17 00:36:59 +03:00
|
|
|
.vertical(|mut strip| {
|
|
|
|
strip.cell(|ui| {
|
2023-06-02 02:05:34 +03:00
|
|
|
ui.add_space(2.0);
|
|
|
|
ui.vertical_centered(|ui| {
|
2023-06-19 01:29:15 +03:00
|
|
|
ui.label(RichText::new(self.current_tab.get_type().name().to_uppercase())
|
2023-06-02 02:05:34 +03:00
|
|
|
.size(18.0)
|
2023-06-03 11:22:51 +03:00
|
|
|
.color(Colors::TITLE));
|
2023-05-17 00:36:59 +03:00
|
|
|
});
|
2023-05-04 20:09:26 +03:00
|
|
|
});
|
|
|
|
strip.cell(|ui| {
|
|
|
|
ui.centered_and_justified(|ui| {
|
2023-05-23 01:46:42 +03:00
|
|
|
let sync_status = Node::get_sync_status();
|
2023-05-17 00:36:59 +03:00
|
|
|
|
|
|
|
// Setup text color animation based on sync status
|
|
|
|
let idle = match sync_status {
|
2023-05-23 01:46:42 +03:00
|
|
|
None => { !Node::is_starting() }
|
2023-05-17 00:36:59 +03:00
|
|
|
Some(ss) => { ss == SyncStatus::NoSync }
|
2023-05-04 20:09:26 +03:00
|
|
|
};
|
2023-05-17 00:36:59 +03:00
|
|
|
let (dark, bright) = (0.3, 1.0);
|
|
|
|
let color_factor = if !idle {
|
2023-06-03 11:22:51 +03:00
|
|
|
lerp(dark..=bright, ui.input().time.cos().abs()) as f32
|
2023-05-17 00:36:59 +03:00
|
|
|
} else {
|
2023-06-03 11:22:51 +03:00
|
|
|
bright as f32
|
2023-05-17 00:36:59 +03:00
|
|
|
};
|
|
|
|
|
2023-06-02 02:05:34 +03:00
|
|
|
// Draw sync text
|
2023-06-03 21:35:38 +03:00
|
|
|
let status_color_rgba = Rgba::from(Colors::TEXT) * color_factor;
|
2023-06-02 02:05:34 +03:00
|
|
|
let status_color = Color32::from(status_color_rgba);
|
2023-06-02 21:19:34 +03:00
|
|
|
View::ellipsize_text(ui, Node::get_sync_status_text(), 15.0, status_color);
|
2023-06-02 02:05:34 +03:00
|
|
|
|
2023-05-17 00:36:59 +03:00
|
|
|
// Repaint based on sync status
|
|
|
|
if idle {
|
|
|
|
ui.ctx().request_repaint_after(Duration::from_millis(600));
|
|
|
|
} else {
|
|
|
|
ui.ctx().request_repaint();
|
|
|
|
}
|
2023-05-04 20:09:26 +03:00
|
|
|
});
|
|
|
|
});
|
2023-05-17 00:36:59 +03:00
|
|
|
});
|
2023-05-04 20:09:26 +03:00
|
|
|
}
|
2023-06-02 21:19:34 +03:00
|
|
|
|
2023-06-23 22:12:30 +03:00
|
|
|
/// Content to draw when node is disabled.
|
|
|
|
pub fn disabled_node_ui(ui: &mut egui::Ui) {
|
2023-06-17 13:23:31 +03:00
|
|
|
View::center_content(ui, 162.0, |ui| {
|
2023-06-03 21:35:38 +03:00
|
|
|
let text = t!("network.disabled_server", "dots" => DOTS_THREE_OUTLINE_VERTICAL);
|
2023-06-03 11:22:51 +03:00
|
|
|
ui.label(RichText::new(text)
|
|
|
|
.size(16.0)
|
|
|
|
.color(Colors::INACTIVE_TEXT)
|
2023-06-02 21:19:34 +03:00
|
|
|
);
|
2023-06-03 11:22:51 +03:00
|
|
|
|
|
|
|
ui.add_space(10.0);
|
|
|
|
|
2023-06-19 01:29:15 +03:00
|
|
|
View::button(ui, t!("network.enable_node"), Colors::GOLD, || {
|
2023-06-15 23:54:41 +03:00
|
|
|
Node::start();
|
2023-06-02 21:19:34 +03:00
|
|
|
});
|
2023-06-17 13:23:31 +03:00
|
|
|
|
2023-06-19 01:29:15 +03:00
|
|
|
ui.add_space(2.0);
|
2023-06-17 13:23:31 +03:00
|
|
|
|
2023-06-19 01:29:15 +03:00
|
|
|
let autostart: bool = Settings::app_config_to_read().auto_start_node;
|
2023-06-17 13:23:31 +03:00
|
|
|
View::checkbox(ui, autostart, t!("network.autorun"), || {
|
2023-06-19 01:29:15 +03:00
|
|
|
let mut w_app_config = Settings::app_config_to_update();
|
2023-06-17 13:23:31 +03:00
|
|
|
w_app_config.auto_start_node = !autostart;
|
|
|
|
w_app_config.save();
|
|
|
|
});
|
2023-06-02 21:19:34 +03:00
|
|
|
});
|
|
|
|
}
|
2023-06-23 22:12:30 +03:00
|
|
|
|
|
|
|
/// List of available IP addresses.
|
|
|
|
pub fn get_ip_list() -> Vec<IpAddr> {
|
|
|
|
let mut addresses = Vec::new();
|
|
|
|
for net_if in pnet::datalink::interfaces() {
|
|
|
|
for ip in net_if.ips {
|
|
|
|
if ip.is_ipv4() {
|
|
|
|
addresses.push(ip.ip());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
addresses
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check whether a port is available on the provided host.
|
|
|
|
pub fn is_port_available(host: &str, port: u16) -> bool {
|
|
|
|
let ip_addr = Ipv4Addr::from_str(host).unwrap();
|
|
|
|
let ipv4 = SocketAddrV4::new(ip_addr, port);
|
|
|
|
TcpListener::bind(ipv4).is_ok()
|
|
|
|
}
|
2023-05-04 20:09:26 +03:00
|
|
|
}
|
|
|
|
|