Temperature: Fixed reporting unreasonably large values when raw value encodes min/max values as well (#78); support protocol-independent temperature reporting in JSON.

This commit is contained in:
Alexander Shaduri
2025-02-14 13:28:43 +04:00
parent 58d2a8b58f
commit d45e5360cc
4 changed files with 140 additions and 16 deletions
+113 -4
View File
@@ -378,6 +378,11 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_info(
{"write_cache/enabled", _("Write Cache"), bool_formatter(_("Enabled"), _("Disabled"))},
{"ata_dsn/enabled", _("DSN Feature"), bool_formatter(_("Enabled"), _("Disabled"))},
{"ata_security/string", _("ATA Security"), string_formatter()},
// Protocol-independent JSON-only values
{"power_cycle_count", _("Number of Power Cycles"), integer_formatter<int64_t>()},
{"power_on_time/hours", _("Powered for"), integer_formatter<int64_t>("{} hours")},
{"temperature/current", _("Current Temperature"), integer_formatter<int64_t>("{}° Celsius")},
};
bool any_found = false;
@@ -1090,58 +1095,162 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_sctte
std::vector<std::string> lines;
if (get_node_exists(json_root_node, "ata_sct_status/format_version").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_status/format_version", _("SCT status version"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_status/format_version").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("SCT status version: {}", get_node_data<int64_t>(json_root_node, "ata_sct_status/format_version").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_status/sct_version").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_status/sct_version", _("SCT format version"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_status/sct_version").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("SCT format version: {}", get_node_data<int64_t>(json_root_node, "ata_sct_status/sct_version").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_status/device_state").value_or(false)) {
if (get_node_exists(json_root_node, "ata_sct_status/device_state/string").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_status/device_state/string", _("Device state"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<std::string>(json_root_node, "ata_sct_status/device_state/string").value_or(std::string());
add_property(p);
lines.emplace_back(fmt::format("Device state: {}", get_node_data<std::string>(json_root_node, "ata_sct_status/device_state/string").value_or(std::string())));
}
if (get_node_exists(json_root_node, "ata_sct_status/temperature/current").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_status/temperature/current", _("Current temperature (C)"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/current").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("Current temperature: {}° Celsius", get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/current").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_status/temperature/power_cycle_min").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_status/temperature/power_cycle_min", _("Power cycle min. temperature (C)"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/power_cycle_min").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("Power cycle min. temperature: {}° Celsius", get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/power_cycle_min").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_status/temperature/power_cycle_max").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_status/temperature/power_cycle_max", _("Power cycle max. temperature (C)"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/power_cycle_max").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("Power cycle max. temperature: {}° Celsius", get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/power_cycle_max").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_status/temperature/lifetime_min").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_status/temperature/lifetime_min", _("Lifetime min. temperature (C)"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/lifetime_min").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("Lifetime min. temperature: {}° Celsius", get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/lifetime_min").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_status/temperature/lifetime_max").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_status/temperature/lifetime_max", _("Lifetime max. temperature (C)"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/lifetime_max").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("Lifetime max. temperature: {}° Celsius", get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/lifetime_max").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_status/temperature/under_limit_count").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_status/temperature/under_limit_count", _("Under limit count"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/under_limit_count").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("Under limit count: {}", get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/under_limit_count").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_status/temperature/over_limit_count").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_status/temperature/over_limit_count", _("Over limit count"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/over_limit_count").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("Over limit count: {}", get_node_data<int64_t>(json_root_node, "ata_sct_status/temperature/over_limit_count").value_or(0)));
}
lines.emplace_back();
if (get_node_exists(json_root_node, "ata_sct_temperature_history/version").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_temperature_history/version", _("SCT temperature history version"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/version").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("SCT temperature history version: {}", get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/version").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_temperature_history/sampling_period_minutes").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_temperature_history/sampling_period_minutes", _("Temperature sampling period (min)"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/sampling_period_minutes").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("Temperature sampling period: {} min.", get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/sampling_period_minutes").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_temperature_history/logging_interval_minutes").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_temperature_history/logging_interval_minutes", _("Temperature logging interval (min)"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/logging_interval_minutes").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("Temperature logging interval: {} min.", get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/logging_interval_minutes").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_temperature_history/temperature/op_limit_min").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_temperature_history/temperature/op_limit_min", _("Recommended operating temperature (minimum) (C)"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/temperature/op_limit_min").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("Recommended operating temperature (minimum): {}° Celsius",
get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/temperature/op_limit_min").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_temperature_history/temperature/op_limit_max").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_temperature_history/temperature/op_limit_max", _("Recommended operating temperature (maximum) (C)"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/temperature/op_limit_max").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("Recommended operating temperature (maximum): {}° Celsius",
get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/temperature/op_limit_max").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_temperature_history/temperature/limit_min").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_temperature_history/temperature/limit_min", _("Allowed operating temperature (minimum) (C)"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/temperature/limit_min").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("Allowed operating temperature (minimum): {}° Celsius",
get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/temperature/limit_min").value_or(0)));
}
if (get_node_exists(json_root_node, "ata_sct_temperature_history/temperature/limit_max").value_or(false)) {
StorageProperty p;
p.set_name("ata_sct_temperature_history/temperature/limit_max", _("Allowed operating temperature (maximum) (C)"));
p.section = StoragePropertySection::TemperatureLog;
p.value = get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/temperature/limit_max").value_or(0);
add_property(p);
lines.emplace_back(fmt::format("Allowed operating temperature (maximum): {}° Celsius",
get_node_data<int64_t>(json_root_node, "ata_sct_temperature_history/temperature/limit_max").value_or(0)));
}
@@ -1230,8 +1339,8 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_devst
StorageProperty page_prop;
{
std::string gen_name = get_node_data<std::string>(page_entry, "name").value_or(std::string());
std::string disp_name = gen_name; // TODO: Translate
const std::string gen_name = get_node_data<std::string>(page_entry, "name").value_or(std::string());
const std::string disp_name = gen_name; // TODO: Translate
page_prop.set_name(gen_name, disp_name);
page_prop.section = StoragePropertySection::Statistics;
page_prop.value = page_stat;
@@ -1251,7 +1360,7 @@ hz::ExpectedVoid<SmartctlParserError> SmartctlJsonAtaParser::parse_section_devst
s.offset = get_node_data<int64_t>(table_entry, "offset").value_or(0);
StorageProperty p;
std::string gen_name = get_node_data<std::string>(table_entry, "name").value_or(std::string());
const std::string gen_name = get_node_data<std::string>(table_entry, "name").value_or(std::string());
p.set_name(gen_name, gen_name, gen_name); // The description database will correct this.
p.section = StoragePropertySection::Statistics;
p.value = s;
@@ -21,6 +21,7 @@ Copyright:
#include "storage_property_descr_ata_attribute.h"
//#include "warning_colors.h"
#include "storage_property_descr_helpers.h"
#include "hz/string_num.h"
namespace {
@@ -1282,12 +1283,16 @@ void storage_property_ata_attribute_autoset_warning(StorageProperty& p)
"This could be an indication of future failures and/or potential data loss in bad sectors.";
// Temperature (for some it may be 10xTemp, so limit the upper bound.)
} else if (attr_match(p, "attr_temperature_celsius")
&& attr.raw_value_int > 50 && attr.raw_value_int <= 120) { // 50C
w = WarningLevel::Notice;
reason = "The temperature of the drive is higher than 50 degrees Celsius. "
"This may shorten its lifespan and cause damage under severe load. Please install a cooling solution.";
} else if (attr_match(p, "attr_temperature_celsius")) {
// Raw value may be 27, or 253403791387 (which encodes min/max values as well).
// Use string instead.
std::int64_t temp_int = 0;
if (hz::string_is_numeric_nolocale(attr.raw_value, temp_int, false)
&& temp_int > 50 && temp_int <= 120) { // 50C
w = WarningLevel::Notice;
reason = "The temperature of the drive is higher than 50 degrees Celsius. "
"This may shorten its lifespan and cause damage under severe load. Please install a cooling solution.";
}
// Temperature (for some it may be 10xTemp, so limit the upper bound.)
} else if (attr_match(p, "attr_temperature_celsius_x10") && attr.raw_value_int > 500) { // 50C
w = WarningLevel::Notice;
@@ -156,7 +156,7 @@ namespace {
// Temperature Statistics
add("Current Temperature", "Current Temperature (C)", "",
add("Current Temperature", "Current Temperature (C)", "stat_temperature_celsius",
"Drive temperature (Celsius)");
add("Average Short Term Temperature", "Average Short Term Temperature (C)", "",
+15 -5
View File
@@ -1734,11 +1734,16 @@ void GscInfoWindow::fill_ui_temperature_log(const StoragePropertyRepository& pro
std::string temperature;
StorageProperty temp_property;
enum { temp_attr2 = 1, temp_attr1, temp_stat, temp_sct }; // less important to more important
enum { temp_attr2 = 1, temp_attr1, temp_stat, temp_sct, temp_info }; // less important to more important
int temp_prop_source = 0;
for (const auto& p : props) {
// Find temperature
if (temp_prop_source < temp_info && p.generic_name == "temperature/current") { // Protocol-independent temperature
temperature = hz::number_to_string_locale(p.get_value<int64_t>());
temp_property = p;
temp_prop_source = temp_info;
}
if (temp_prop_source < temp_sct && p.generic_name == "ata_sct_status/temperature/current") {
temperature = hz::number_to_string_locale(p.get_value<int64_t>());
temp_property = p;
@@ -1750,9 +1755,14 @@ void GscInfoWindow::fill_ui_temperature_log(const StoragePropertyRepository& pro
temp_prop_source = temp_stat;
}
if (temp_prop_source < temp_attr1 && p.generic_name == "attr_temperature_celsius") {
temperature = hz::number_to_string_locale(p.get_value<AtaStorageAttribute>().raw_value_int);
temp_property = p;
temp_prop_source = temp_attr1;
// Note: raw value may encode min/max as well, leading to very large values.
// Instead, convert the string value (can be "27" or "27 (Min/Max 11/59)").
std::int64_t temp_int = 0;
if (hz::string_is_numeric_nolocale(p.get_value<AtaStorageAttribute>().raw_value, temp_int, false)) {
temperature = hz::number_to_string_locale(temp_int);
temp_property = p;
temp_prop_source = temp_attr1;
}
}
if (temp_prop_source < temp_attr2 && p.generic_name == "attr_temperature_celsius_x10") {
temperature = hz::number_to_string_locale(p.get_value<AtaStorageAttribute>().raw_value_int / 10);
@@ -1785,7 +1795,7 @@ void GscInfoWindow::fill_ui_temperature_log(const StoragePropertyRepository& pro
if (temperature.empty()) {
temperature = C_("value", "Unknown");
} else {
temperature = Glib::ustring::compose(C_("temperature", "%1 C"), temperature);
temperature = Glib::ustring::compose(C_("temperature", "%1° C"), temperature);
}
temp_property.set_description(_("Current drive temperature in Celsius.")); // overrides attribute description
label_strings.emplace_back(Glib::ustring::compose(_("Current temperature: %1"),