tegratop 0.2.1

TUI for monitoring Nvidia jetson boards
Documentation
use ratatui::{
    layout::{Constraint, Direction, Layout, Rect},
    style::{Style, Stylize},
    widgets::{Block, Borders, Padding, Row, Table},
    Frame,
};
use std::fs;
use std::path::Path;
use strum::IntoEnumIterator;
use strum_macros::Display;
use strum_macros::EnumIter;

#[derive(EnumIter, Display, Debug, Default, PartialEq)]
pub enum HWName {
    #[default]
    APE,
    DLA,
    CVNAS,
    MSENC,
    NVENC,
    NVDEC,
    NVJPG,
    PVA,
    SE,
    VIC,
}

#[derive(Debug, Default)]
pub struct Engine {
    pub hws: Vec<HW>,
}

#[derive(Debug, Default, Display)]
pub enum HWState {
    #[default]
    Idle,
    Running,
}

#[derive(Debug, Default)]
pub struct HW {
    pub name: HWName,
    pub state: HWState,
    pub frequency: f64,
}

impl Engine {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn refresh(&mut self) {
        let mut hws: Vec<HW> = Vec::new();

        let stats_path = Path::new("/sys/kernel/debug/clk");

        for hw_name in HWName::iter() {
            let hw_stats_path = stats_path.join(hw_name.to_string().to_lowercase());

            if !hw_stats_path.exists() {
                continue;
            }

            let state = match fs::read_to_string(hw_stats_path.join("clk_enable_count")) {
                Ok(v) => match v.trim() {
                    "1" => HWState::Running,
                    _ => HWState::Idle,
                },
                Err(_) => continue,
            };

            let frequency = match fs::read_to_string(hw_stats_path.join("clk_rate")) {
                Ok(v) => v.trim().parse::<f64>().unwrap() / 1_000_000.0,
                Err(_) => continue,
            };

            let hw = HW {
                name: hw_name,
                state,
                frequency,
            };

            hws.push(hw);
        }

        self.hws = hws;
    }

    pub fn render(&self, frame: &mut Frame, block: Rect) {
        let container = Block::default()
            .borders(Borders::ALL)
            .title("Engines")
            .padding(Padding::horizontal(1))
            .title_style(Style::new().bold());

        let inside_container = container.inner(block);

        let (left_block, right_block) = {
            let chunks = Layout::default()
                .direction(Direction::Horizontal)
                .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
                .split(inside_container);

            (chunks[0], chunks[1])
        };

        let (left_engines, right_engines) = self.hws.split_at(self.hws.len() / 2);

        let left_rows: Vec<Row> = left_engines
            .iter()
            .map(|hw| {
                Row::new(vec![hw.name.to_string(), hw.state.to_string(), {
                    match hw.state {
                        HWState::Idle => "-".to_string(),
                        HWState::Running => format!("{:.1} MHz", hw.frequency),
                    }
                }])
            })
            .collect();

        let right_rows: Vec<Row> = right_engines
            .iter()
            .map(|hw| {
                Row::new(vec![hw.name.to_string(), hw.state.to_string(), {
                    match hw.state {
                        HWState::Idle => "-".to_string(),
                        HWState::Running => format!("{:.1} MHz", hw.frequency),
                    }
                }])
            })
            .collect();

        let widths = [
            Constraint::Length(7),
            Constraint::Length(8),
            Constraint::Length(10),
        ];

        let left_table = Table::new(left_rows, widths)
            .header(Row::new(vec!["Name", "State", "Frequency"]).style(Style::new().bold()))
            .block(Block::default());

        let right_table = Table::new(right_rows, widths)
            .header(Row::new(vec!["Name", "State", "Frequency"]).style(Style::new().bold()))
            .block(Block::default());

        frame.render_widget(container, block);
        frame.render_widget(left_table, left_block);
        frame.render_widget(right_table, right_block);
    }
}