added cpu, renaming
This commit is contained in:
parent
1519a1bee4
commit
c8668e5f00
@ -2,6 +2,7 @@
|
|||||||
#define CONFIG_MANAGER_H
|
#define CONFIG_MANAGER_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
class ConfigManager {
|
class ConfigManager {
|
||||||
public:
|
public:
|
||||||
@ -21,13 +22,22 @@ public:
|
|||||||
void setWindowWidth(int w) { windowWidth = w; }
|
void setWindowWidth(int w) { windowWidth = w; }
|
||||||
void setWindowHeight(int h) { windowHeight = h; }
|
void setWindowHeight(int h) { windowHeight = h; }
|
||||||
void setPollingTime(int t) { pollingTime = t; }
|
void setPollingTime(int t) { pollingTime = t; }
|
||||||
|
|
||||||
|
std::map<std::string, std::string> getSensorColors() const { return sensorColors; }
|
||||||
|
std::map<std::string, std::string> getSensorNames() const { return sensorNames; }
|
||||||
|
|
||||||
|
void setSensorColor(const std::string &id, const std::string &color) { sensorColors[id] = color; }
|
||||||
|
void setSensorName(const std::string &id, const std::string &name) { sensorNames[id] = name; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string getConfigFilePath() const;
|
std::string getConfigFilePath() const;
|
||||||
|
|
||||||
int windowWidth;
|
int windowWidth;
|
||||||
int windowHeight;
|
int windowHeight;
|
||||||
int pollingTime;
|
int pollingTime;
|
||||||
|
|
||||||
|
std::map<std::string, std::string> sensorColors;
|
||||||
|
std::map<std::string, std::string> sensorNames;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONFIG_MANAGER_H
|
#endif // CONFIG_MANAGER_H
|
||||||
|
|||||||
@ -25,6 +25,7 @@ private:
|
|||||||
static void onClearButtonClicked(GtkButton *button, gpointer userData);
|
static void onClearButtonClicked(GtkButton *button, gpointer userData);
|
||||||
static void onQuitButtonClicked(GtkButton *button, gpointer userData);
|
static void onQuitButtonClicked(GtkButton *button, gpointer userData);
|
||||||
static void onColorSet(GObject *object, GParamSpec *pspec, gpointer userData);
|
static void onColorSet(GObject *object, GParamSpec *pspec, gpointer userData);
|
||||||
|
static void onNameChanged(GtkEditable *editable, gpointer userData);
|
||||||
|
|
||||||
GtkWidget *window;
|
GtkWidget *window;
|
||||||
GtkWidget *statusLabel;
|
GtkWidget *statusLabel;
|
||||||
@ -35,6 +36,7 @@ private:
|
|||||||
guint timerID;
|
guint timerID;
|
||||||
int refreshRateSec;
|
int refreshRateSec;
|
||||||
GtkWidget *legendBox;
|
GtkWidget *legendBox;
|
||||||
|
std::map<std::string, GtkWidget*> tempLabels;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Global main loop reference for proper shutdown
|
// Global main loop reference for proper shutdown
|
||||||
|
|||||||
@ -26,6 +26,15 @@ public:
|
|||||||
std::map<std::string, std::map<std::string, double>> getAllTemperatures();
|
std::map<std::string, std::map<std::string, double>> getAllTemperatures();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct SensorInfo {
|
||||||
|
std::string deviceName;
|
||||||
|
std::string sensorName;
|
||||||
|
std::string path;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<SensorInfo> discoveredSensors;
|
||||||
|
|
||||||
|
void discoverSensors();
|
||||||
double readTemperatureFromFile(const std::string &filePath);
|
double readTemperatureFromFile(const std::string &filePath);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,8 @@ struct DataPoint {
|
|||||||
struct SeriesData {
|
struct SeriesData {
|
||||||
std::vector<DataPoint> points;
|
std::vector<DataPoint> points;
|
||||||
GdkRGBA color;
|
GdkRGBA color;
|
||||||
std::string name;
|
std::string id; // Unique internal ID (device + sensor)
|
||||||
|
std::string name; // User-friendly display name
|
||||||
};
|
};
|
||||||
|
|
||||||
class TemperatureChart {
|
class TemperatureChart {
|
||||||
@ -34,6 +35,11 @@ public:
|
|||||||
// Get list of series with their colors
|
// Get list of series with their colors
|
||||||
std::vector<std::pair<std::string, GdkRGBA>> getSeriesColors() const;
|
std::vector<std::pair<std::string, GdkRGBA>> getSeriesColors() const;
|
||||||
void setSeriesColor(const std::string &seriesName, const GdkRGBA &color);
|
void setSeriesColor(const std::string &seriesName, const GdkRGBA &color);
|
||||||
|
|
||||||
|
// Name management
|
||||||
|
std::string getSeriesName(const std::string &seriesId) const;
|
||||||
|
void setSeriesName(const std::string &seriesId, const std::string &name);
|
||||||
|
|
||||||
void drawChart(GtkDrawingArea *area, cairo_t *cr, int width, int height);
|
void drawChart(GtkDrawingArea *area, cairo_t *cr, int width, int height);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -2,3 +2,30 @@
|
|||||||
window {
|
window {
|
||||||
/* Icon can be set via CSS if needed */
|
/* Icon can be set via CSS if needed */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.small-entry {
|
||||||
|
font-size: 9px;
|
||||||
|
min-height: 0;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0px 1px;
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-entry:focus {
|
||||||
|
background: rgba(255,255,255,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.temp-label-small {
|
||||||
|
font-size: 9px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 1px;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact box items */
|
||||||
|
.legend-item {
|
||||||
|
margin: 0 4px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
ConfigManager::ConfigManager()
|
ConfigManager::ConfigManager()
|
||||||
: windowWidth(1200), windowHeight(700), pollingTime(3)
|
: windowWidth(1200), windowHeight(700), pollingTime(1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,6 +67,10 @@ void ConfigManager::load()
|
|||||||
windowHeight = std::stoi(value);
|
windowHeight = std::stoi(value);
|
||||||
} else if (key == "polling_time") {
|
} else if (key == "polling_time") {
|
||||||
pollingTime = std::stoi(value);
|
pollingTime = std::stoi(value);
|
||||||
|
} else if (key.find("color_") == 0) {
|
||||||
|
sensorColors[key.substr(6)] = value;
|
||||||
|
} else if (key.find("name_") == 0) {
|
||||||
|
sensorNames[key.substr(5)] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,5 +94,13 @@ void ConfigManager::save()
|
|||||||
file << "window_height = " << windowHeight << "\n";
|
file << "window_height = " << windowHeight << "\n";
|
||||||
file << "polling_time = " << pollingTime << "\n";
|
file << "polling_time = " << pollingTime << "\n";
|
||||||
|
|
||||||
|
for (auto const& [id, color] : sensorColors) {
|
||||||
|
file << "color_" << id << " = " << color << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& [id, name] : sensorNames) {
|
||||||
|
file << "name_" << id << " = " << name << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <iostream>
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
// Declaration from resources.c
|
// Declaration from resources.c
|
||||||
@ -15,6 +16,7 @@ GMainLoop *gMainLoop = nullptr;
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
// Daemonize the process
|
// Daemonize the process
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
|
|
||||||
@ -45,16 +47,23 @@ int main(int argc, char *argv[])
|
|||||||
dup2(fd, STDERR_FILENO);
|
dup2(fd, STDERR_FILENO);
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
gtk_init();
|
gtk_init();
|
||||||
|
|
||||||
|
std::cerr << "GTK Initialized" << std::endl;
|
||||||
|
|
||||||
// Register resources containing the application icon
|
// Register resources containing the application icon
|
||||||
GResource *resource = resources_get_resource();
|
GResource *resource = resources_get_resource();
|
||||||
g_resources_register(resource);
|
g_resources_register(resource);
|
||||||
|
|
||||||
|
std::cerr << "Resources registered" << std::endl;
|
||||||
|
|
||||||
gMainLoop = g_main_loop_new(nullptr, FALSE);
|
gMainLoop = g_main_loop_new(nullptr, FALSE);
|
||||||
|
|
||||||
|
std::cerr << "Creating MainWindow" << std::endl;
|
||||||
MainWindow *window = new MainWindow();
|
MainWindow *window = new MainWindow();
|
||||||
|
std::cerr << "MainWindow created" << std::endl;
|
||||||
window->show();
|
window->show();
|
||||||
|
|
||||||
// Run the main event loop
|
// Run the main event loop
|
||||||
|
|||||||
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
MainWindow::MainWindow()
|
MainWindow::MainWindow()
|
||||||
: window(nullptr), statusLabel(nullptr), refreshRateSpinBox(nullptr),
|
: window(nullptr), statusLabel(nullptr), refreshRateSpinBox(nullptr),
|
||||||
chart(nullptr), monitor(nullptr), config(nullptr), timerID(0), refreshRateSec(3)
|
chart(nullptr), monitor(nullptr), config(nullptr), timerID(0),
|
||||||
|
refreshRateSec(3)
|
||||||
{
|
{
|
||||||
monitor = new TempMonitor();
|
monitor = new TempMonitor();
|
||||||
config = new ConfigManager();
|
config = new ConfigManager();
|
||||||
@ -48,6 +49,14 @@ MainWindow::~MainWindow()
|
|||||||
|
|
||||||
void MainWindow::setupUI()
|
void MainWindow::setupUI()
|
||||||
{
|
{
|
||||||
|
// Load CSS
|
||||||
|
GtkCssProvider *provider = gtk_css_provider_new();
|
||||||
|
gtk_css_provider_load_from_resource(provider, "/org/kamma/nvme-monitor/style.css");
|
||||||
|
gtk_style_context_add_provider_for_display(gdk_display_get_default(),
|
||||||
|
GTK_STYLE_PROVIDER(provider),
|
||||||
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||||
|
g_object_unref(provider);
|
||||||
|
|
||||||
window = gtk_window_new();
|
window = gtk_window_new();
|
||||||
gtk_window_set_title(GTK_WINDOW(window), "NVMe Temperature Monitor");
|
gtk_window_set_title(GTK_WINDOW(window), "NVMe Temperature Monitor");
|
||||||
|
|
||||||
@ -94,7 +103,7 @@ void MainWindow::setupUI()
|
|||||||
GtkWidget *clearButton = gtk_button_new_with_label("Clear Data");
|
GtkWidget *clearButton = gtk_button_new_with_label("Clear Data");
|
||||||
g_signal_connect(clearButton, "clicked", G_CALLBACK(onClearButtonClicked), this);
|
g_signal_connect(clearButton, "clicked", G_CALLBACK(onClearButtonClicked), this);
|
||||||
gtk_box_append(GTK_BOX(controlBox), clearButton);
|
gtk_box_append(GTK_BOX(controlBox), clearButton);
|
||||||
|
|
||||||
// Status label
|
// Status label
|
||||||
statusLabel = gtk_label_new("Initializing...");
|
statusLabel = gtk_label_new("Initializing...");
|
||||||
gtk_label_set_xalign(GTK_LABEL(statusLabel), 0);
|
gtk_label_set_xalign(GTK_LABEL(statusLabel), 0);
|
||||||
@ -113,10 +122,15 @@ void MainWindow::setupUI()
|
|||||||
gtk_box_append(GTK_BOX(mainBox), chart->getWidget());
|
gtk_box_append(GTK_BOX(mainBox), chart->getWidget());
|
||||||
gtk_widget_set_vexpand(chart->getWidget(), TRUE);
|
gtk_widget_set_vexpand(chart->getWidget(), TRUE);
|
||||||
|
|
||||||
// Legend box
|
// Legend box with horizontal scrolling only
|
||||||
legendBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
|
legendBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||||
gtk_widget_set_margin_top(legendBox, 10);
|
|
||||||
gtk_box_append(GTK_BOX(mainBox), legendBox);
|
GtkWidget *legendScroll = gtk_scrolled_window_new();
|
||||||
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(legendScroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
|
||||||
|
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(legendScroll), legendBox);
|
||||||
|
gtk_widget_set_margin_top(legendScroll, 2);
|
||||||
|
|
||||||
|
gtk_box_append(GTK_BOX(mainBox), legendScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean MainWindow::onDeleteWindow(GtkWidget *widget, gpointer userData)
|
gboolean MainWindow::onDeleteWindow(GtkWidget *widget, gpointer userData)
|
||||||
@ -185,6 +199,7 @@ void MainWindow::saveWindowState()
|
|||||||
|
|
||||||
void MainWindow::updateTemperatures()
|
void MainWindow::updateTemperatures()
|
||||||
{
|
{
|
||||||
|
std::cerr << "updateTemperatures starting" << std::endl;
|
||||||
auto allTemps = monitor->getAllTemperatures();
|
auto allTemps = monitor->getAllTemperatures();
|
||||||
|
|
||||||
// Get current real time in milliseconds since epoch
|
// Get current real time in milliseconds since epoch
|
||||||
@ -198,13 +213,41 @@ void MainWindow::updateTemperatures()
|
|||||||
const std::string &device = deviceEntry.first;
|
const std::string &device = deviceEntry.first;
|
||||||
const auto &temps = deviceEntry.second;
|
const auto &temps = deviceEntry.second;
|
||||||
|
|
||||||
|
std::cerr << "Processing device: " << device << " with " << temps.size() << " sensors" << std::endl;
|
||||||
|
|
||||||
for (const auto &tempEntry : temps) {
|
for (const auto &tempEntry : temps) {
|
||||||
const std::string &sensorName = tempEntry.first;
|
const std::string &sensorName = tempEntry.first;
|
||||||
double temperature = tempEntry.second;
|
double temperature = tempEntry.second;
|
||||||
|
|
||||||
|
std::cerr << " Sensor: " << sensorName << " Temp: " << temperature << std::endl;
|
||||||
|
|
||||||
if (chart->addTemperatureData(device, sensorName, temperature, currentTime)) {
|
if (chart->addTemperatureData(device, sensorName, temperature, currentTime)) {
|
||||||
needsLegendUpdate = true;
|
needsLegendUpdate = true;
|
||||||
|
|
||||||
|
// Apply saved settings for new series
|
||||||
|
std::string seriesId = device + " - " + sensorName;
|
||||||
|
auto savedNames = config->getSensorNames();
|
||||||
|
if (savedNames.count(seriesId)) {
|
||||||
|
chart->setSeriesName(seriesId, savedNames[seriesId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto savedColors = config->getSensorColors();
|
||||||
|
if (savedColors.count(seriesId)) {
|
||||||
|
GdkRGBA color;
|
||||||
|
if (gdk_rgba_parse(&color, savedColors[seriesId].c_str())) {
|
||||||
|
chart->setSeriesColor(seriesId, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update individual temperature label in legend
|
||||||
|
std::string seriesId = device + " - " + sensorName;
|
||||||
|
if (tempLabels.count(seriesId)) {
|
||||||
|
char buf[16];
|
||||||
|
snprintf(buf, sizeof(buf), "%.1f°C", temperature);
|
||||||
|
gtk_label_set_text(GTK_LABEL(tempLabels[seriesId]), buf);
|
||||||
|
}
|
||||||
|
|
||||||
totalReadings++;
|
totalReadings++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,33 +278,48 @@ void MainWindow::updateLegend()
|
|||||||
child = next;
|
child = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tempLabels.clear();
|
||||||
|
|
||||||
// Get series colors from chart
|
// Get series colors from chart
|
||||||
auto seriesColors = chart->getSeriesColors();
|
auto seriesColors = chart->getSeriesColors();
|
||||||
|
|
||||||
// Add legend items
|
// Add legend items
|
||||||
for (const auto &pair : seriesColors) {
|
for (const auto &pair : seriesColors) {
|
||||||
const std::string &seriesName = pair.first;
|
const std::string &seriesId = pair.first;
|
||||||
const GdkRGBA &color = pair.second;
|
const GdkRGBA &color = pair.second;
|
||||||
|
std::string displayName = chart->getSeriesName(seriesId);
|
||||||
|
|
||||||
// Create container for legend item
|
// Create container for legend item
|
||||||
GtkWidget *itemBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
|
GtkWidget *itemBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
|
||||||
|
gtk_widget_add_css_class(itemBox, "legend-item");
|
||||||
|
|
||||||
// Create color dialog and button (modern GTK4 way)
|
// Create color dialog and button
|
||||||
GtkColorDialog *dialog = gtk_color_dialog_new();
|
GtkColorDialog *dialog = gtk_color_dialog_new();
|
||||||
GtkWidget *colorButton = gtk_color_dialog_button_new(dialog);
|
GtkWidget *colorButton = gtk_color_dialog_button_new(dialog);
|
||||||
gtk_color_dialog_button_set_rgba(GTK_COLOR_DIALOG_BUTTON(colorButton), &color);
|
gtk_color_dialog_button_set_rgba(GTK_COLOR_DIALOG_BUTTON(colorButton), &color);
|
||||||
gtk_widget_set_size_request(colorButton, 24, 24);
|
gtk_widget_set_size_request(colorButton, 12, 12);
|
||||||
|
|
||||||
// Store series name in user data to know which series to update
|
|
||||||
g_object_set_data_full(G_OBJECT(colorButton), "series-name", g_strdup(seriesName.c_str()), g_free);
|
|
||||||
|
|
||||||
|
// Store series id in user data
|
||||||
|
g_object_set_data_full(G_OBJECT(colorButton), "series-name", g_strdup(seriesId.c_str()), g_free);
|
||||||
g_signal_connect(colorButton, "notify::rgba", G_CALLBACK(onColorSet), this);
|
g_signal_connect(colorButton, "notify::rgba", G_CALLBACK(onColorSet), this);
|
||||||
|
|
||||||
gtk_box_append(GTK_BOX(itemBox), colorButton);
|
gtk_box_append(GTK_BOX(itemBox), colorButton);
|
||||||
|
|
||||||
// Create label with series name
|
// Create editable label for series name
|
||||||
GtkWidget *label = gtk_label_new(seriesName.c_str());
|
GtkWidget *entry = gtk_entry_new();
|
||||||
gtk_box_append(GTK_BOX(itemBox), label);
|
gtk_editable_set_text(GTK_EDITABLE(entry), displayName.c_str());
|
||||||
|
gtk_widget_set_size_request(entry, 60, -1);
|
||||||
|
gtk_widget_add_css_class(entry, "small-entry");
|
||||||
|
|
||||||
|
// Store series id in user data
|
||||||
|
g_object_set_data_full(G_OBJECT(entry), "series-name", g_strdup(seriesId.c_str()), g_free);
|
||||||
|
g_signal_connect(entry, "changed", G_CALLBACK(onNameChanged), this);
|
||||||
|
gtk_box_append(GTK_BOX(itemBox), entry);
|
||||||
|
|
||||||
|
// Temperature label
|
||||||
|
GtkWidget *tempLabel = gtk_label_new("---°C");
|
||||||
|
gtk_widget_add_css_class(tempLabel, "temp-label-small");
|
||||||
|
gtk_box_append(GTK_BOX(itemBox), tempLabel);
|
||||||
|
tempLabels[seriesId] = tempLabel;
|
||||||
|
|
||||||
gtk_box_append(GTK_BOX(legendBox), itemBox);
|
gtk_box_append(GTK_BOX(legendBox), itemBox);
|
||||||
}
|
}
|
||||||
@ -283,6 +341,27 @@ void MainWindow::onColorSet(GObject *object, GParamSpec *pspec, gpointer userDat
|
|||||||
const GdkRGBA *color = gtk_color_dialog_button_get_rgba(GTK_COLOR_DIALOG_BUTTON(object));
|
const GdkRGBA *color = gtk_color_dialog_button_get_rgba(GTK_COLOR_DIALOG_BUTTON(object));
|
||||||
if (color) {
|
if (color) {
|
||||||
self->chart->setSeriesColor(seriesName, *color);
|
self->chart->setSeriesColor(seriesName, *color);
|
||||||
|
|
||||||
|
// Save to config
|
||||||
|
char *colorStr = gdk_rgba_to_string(color);
|
||||||
|
self->config->setSensorColor(seriesName, colorStr);
|
||||||
|
g_free(colorStr);
|
||||||
|
self->config->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onNameChanged(GtkEditable *editable, gpointer userData)
|
||||||
|
{
|
||||||
|
MainWindow *self = static_cast<MainWindow*>(userData);
|
||||||
|
const char *seriesId = static_cast<const char*>(g_object_get_data(G_OBJECT(editable), "series-name"));
|
||||||
|
|
||||||
|
if (seriesId) {
|
||||||
|
const char *newName = gtk_editable_get_text(editable);
|
||||||
|
if (newName) {
|
||||||
|
self->chart->setSeriesName(seriesId, newName);
|
||||||
|
self->config->setSensorName(seriesId, newName);
|
||||||
|
self->config->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,72 @@
|
|||||||
|
|
||||||
TempMonitor::TempMonitor()
|
TempMonitor::TempMonitor()
|
||||||
{
|
{
|
||||||
|
discoverSensors();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TempMonitor::discoverSensors()
|
||||||
|
{
|
||||||
|
discoveredSensors.clear();
|
||||||
|
DIR* hwmonDir = opendir("/sys/class/hwmon");
|
||||||
|
if (!hwmonDir) return;
|
||||||
|
|
||||||
|
struct dirent* entry;
|
||||||
|
while ((entry = readdir(hwmonDir)) != nullptr) {
|
||||||
|
std::string hwmonName = entry->d_name;
|
||||||
|
if (hwmonName.find("hwmon") != 0) continue;
|
||||||
|
|
||||||
|
std::string path = "/sys/class/hwmon/" + hwmonName;
|
||||||
|
|
||||||
|
// Read "name" from sysfs
|
||||||
|
std::string nameFromSysfs = "Unknown";
|
||||||
|
std::ifstream nameFile(path + "/name");
|
||||||
|
if (nameFile.is_open()) {
|
||||||
|
std::getline(nameFile, nameFromSysfs);
|
||||||
|
nameFromSysfs.erase(nameFromSysfs.find_last_not_of(" \n\r\t") + 1);
|
||||||
|
nameFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter: Only interested in nvme and coretemp (CPU)
|
||||||
|
if (nameFromSysfs != "nvme" && nameFromSysfs != "coretemp") continue;
|
||||||
|
|
||||||
|
DIR* dDir = opendir(path.c_str());
|
||||||
|
if (dDir) {
|
||||||
|
struct dirent* sEntry;
|
||||||
|
while ((sEntry = readdir(dDir)) != nullptr) {
|
||||||
|
std::string fname = sEntry->d_name;
|
||||||
|
// Only temperature sensors (temp*_input)
|
||||||
|
if (fname.find("temp") == 0 && fname.find("_input") != std::string::npos) {
|
||||||
|
std::string id = fname.substr(4, fname.find("_input") - 4);
|
||||||
|
|
||||||
|
// Use "label" from sysfs for sensor name if available
|
||||||
|
std::string labelFromSysfs = "";
|
||||||
|
std::ifstream labelFile(path + "/temp" + id + "_label");
|
||||||
|
if (labelFile.is_open()) {
|
||||||
|
std::getline(labelFile, labelFromSysfs);
|
||||||
|
labelFromSysfs.erase(labelFromSysfs.find_last_not_of(" \n\r\t") + 1);
|
||||||
|
labelFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For CPU (coretemp), filter only Package id 0
|
||||||
|
if (nameFromSysfs == "coretemp") {
|
||||||
|
if (labelFromSysfs != "Package id 0") continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For NVMe, filter out Composite sensor
|
||||||
|
if (nameFromSysfs == "nvme") {
|
||||||
|
if (labelFromSysfs == "Composite") continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string finalSensorName = labelFromSysfs.empty() ? "temp" + id : labelFromSysfs;
|
||||||
|
std::string deviceDisplayName = nameFromSysfs + " (" + hwmonName + ")";
|
||||||
|
|
||||||
|
discoveredSensors.push_back({deviceDisplayName, finalSensorName, path + "/" + fname});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(hwmonDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> TempMonitor::getAvailableDevices()
|
std::vector<std::string> TempMonitor::getAvailableDevices()
|
||||||
@ -52,73 +118,12 @@ std::map<std::string, std::map<std::string, double>> TempMonitor::getAllTemperat
|
|||||||
{
|
{
|
||||||
std::map<std::string, std::map<std::string, double>> allTemperatures;
|
std::map<std::string, std::map<std::string, double>> allTemperatures;
|
||||||
|
|
||||||
DIR* hwmonDir = opendir("/sys/class/hwmon");
|
for (const auto& sensor : discoveredSensors) {
|
||||||
if (!hwmonDir) return allTemperatures;
|
double temp = readTemperatureFromFile(sensor.path);
|
||||||
|
if (temp > -200.0) {
|
||||||
struct dirent* entry;
|
allTemperatures[sensor.deviceName][sensor.sensorName] = temp;
|
||||||
while ((entry = readdir(hwmonDir)) != nullptr) {
|
|
||||||
std::string hwmonName = entry->d_name;
|
|
||||||
if (hwmonName.find("hwmon") != 0) continue;
|
|
||||||
|
|
||||||
std::string path = "/sys/class/hwmon/" + hwmonName;
|
|
||||||
|
|
||||||
// Read "name" from sysfs as requested
|
|
||||||
std::string nameFromSysfs = "Unknown";
|
|
||||||
std::ifstream nameFile(path + "/name");
|
|
||||||
if (nameFile.is_open()) {
|
|
||||||
std::getline(nameFile, nameFromSysfs);
|
|
||||||
nameFromSysfs.erase(nameFromSysfs.find_last_not_of(" \n\r\t") + 1);
|
|
||||||
nameFile.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter: Only interested in nvme and coretemp (CPU)
|
|
||||||
if (nameFromSysfs != "nvme" && nameFromSysfs != "coretemp") continue;
|
|
||||||
|
|
||||||
std::map<std::string, double> sensors;
|
|
||||||
DIR* dDir = opendir(path.c_str());
|
|
||||||
if (dDir) {
|
|
||||||
struct dirent* sEntry;
|
|
||||||
while ((sEntry = readdir(dDir)) != nullptr) {
|
|
||||||
std::string fname = sEntry->d_name;
|
|
||||||
// Only temperature sensors (temp*_input)
|
|
||||||
if (fname.find("temp") == 0 && fname.find("_input") != std::string::npos) {
|
|
||||||
std::string id = fname.substr(4, fname.find("_input") - 4);
|
|
||||||
|
|
||||||
double temp = readTemperatureFromFile(path + "/" + fname);
|
|
||||||
if (temp > -200.0) {
|
|
||||||
// Use "label" from sysfs for sensor name if available
|
|
||||||
std::string labelFromSysfs = "";
|
|
||||||
std::ifstream labelFile(path + "/temp" + id + "_label");
|
|
||||||
if (labelFile.is_open()) {
|
|
||||||
std::getline(labelFile, labelFromSysfs);
|
|
||||||
labelFromSysfs.erase(labelFromSysfs.find_last_not_of(" \n\r\t") + 1);
|
|
||||||
labelFile.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// For CPU (coretemp), filter only Package id 0
|
|
||||||
if (nameFromSysfs == "coretemp") {
|
|
||||||
if (labelFromSysfs != "Package id 0") continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For NVMe, filter out Composite sensor
|
|
||||||
if (nameFromSysfs == "nvme") {
|
|
||||||
if (labelFromSysfs == "Composite") continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string finalSensorName = labelFromSysfs.empty() ? "temp" + id : labelFromSysfs;
|
|
||||||
sensors[finalSensorName] = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir(dDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sensors.empty()) {
|
|
||||||
// Label device with its sysfs name and instance ID
|
|
||||||
allTemperatures[nameFromSysfs + " (" + hwmonName + ")"] = sensors;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir(hwmonDir);
|
|
||||||
|
|
||||||
return allTemperatures;
|
return allTemperatures;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -180,6 +180,7 @@ bool TemperatureChart::addTemperatureData(const std::string &device, const std::
|
|||||||
if (seriesMap.find(seriesKey) == seriesMap.end()) {
|
if (seriesMap.find(seriesKey) == seriesMap.end()) {
|
||||||
SeriesData series;
|
SeriesData series;
|
||||||
series.color = getColorForSeries(seriesKey);
|
series.color = getColorForSeries(seriesKey);
|
||||||
|
series.id = seriesKey;
|
||||||
series.name = seriesKey;
|
series.name = seriesKey;
|
||||||
seriesMap[seriesKey] = series;
|
seriesMap[seriesKey] = series;
|
||||||
isNew = true;
|
isNew = true;
|
||||||
@ -230,8 +231,8 @@ void TemperatureChart::clear()
|
|||||||
std::vector<std::pair<std::string, GdkRGBA>> TemperatureChart::getSeriesColors() const
|
std::vector<std::pair<std::string, GdkRGBA>> TemperatureChart::getSeriesColors() const
|
||||||
{
|
{
|
||||||
std::vector<std::pair<std::string, GdkRGBA>> result;
|
std::vector<std::pair<std::string, GdkRGBA>> result;
|
||||||
for (const auto &entry : colorMap) {
|
for (const auto &pair : seriesMap) {
|
||||||
result.push_back({entry.first, entry.second});
|
result.push_back({pair.first, pair.second.color});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -249,6 +250,22 @@ void TemperatureChart::setSeriesColor(const std::string &seriesName, const GdkRG
|
|||||||
gtk_widget_queue_draw(drawingArea);
|
gtk_widget_queue_draw(drawingArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string TemperatureChart::getSeriesName(const std::string &seriesId) const
|
||||||
|
{
|
||||||
|
auto it = seriesMap.find(seriesId);
|
||||||
|
if (it != seriesMap.end()) {
|
||||||
|
return it->second.name;
|
||||||
|
}
|
||||||
|
return seriesId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TemperatureChart::setSeriesName(const std::string &seriesId, const std::string &name)
|
||||||
|
{
|
||||||
|
if (seriesMap.find(seriesId) != seriesMap.end()) {
|
||||||
|
seriesMap[seriesId].name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gboolean TemperatureChart::onTick(gpointer userData)
|
gboolean TemperatureChart::onTick(gpointer userData)
|
||||||
{
|
{
|
||||||
TemperatureChart *self = static_cast<TemperatureChart*>(userData);
|
TemperatureChart *self = static_cast<TemperatureChart*>(userData);
|
||||||
@ -414,7 +431,7 @@ TemperatureChart::NearestPoint TemperatureChart::findNearestDataPoint(double mou
|
|||||||
result.found = true;
|
result.found = true;
|
||||||
result.temperature = point.temperature;
|
result.temperature = point.temperature;
|
||||||
result.timestamp = point.timestamp;
|
result.timestamp = point.timestamp;
|
||||||
result.seriesName = seriesEntry.first;
|
result.seriesName = series.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user