mirror of
https://github.com/ashaduri/gsmartcontrol.git
synced 2026-09-26 22:05:34 +00:00
Implemented parsing of devstat section.
Implemented Statistics tab. Increased borders in some tabs. Some code style changes.
This commit is contained in:
+6
-10
@@ -26,9 +26,8 @@ Don't rely on smartctl return code (2), parse the output instead.
|
||||
Need usage cases.
|
||||
|
||||
|
||||
Add ability (through ctrl-C?) to copy individual rows from attributes page,
|
||||
Add ability (through ctrl-C?) to copy individual rows from attributes and statistics pages,
|
||||
in tab-separated format (for easy pasting into libreoffice).
|
||||
Same for Devstat page.
|
||||
|
||||
|
||||
Add menu item - "update drive database"
|
||||
@@ -54,15 +53,12 @@ Add ability to load --xall output:
|
||||
Some of this stuff is better than attributes, maybe show them before attributes.
|
||||
Also needs the warnings and descriptions.
|
||||
|
||||
Per-line selftest error highlighting.
|
||||
Tab highlighting according to error severity.
|
||||
|
||||
|
||||
Show temperature in Temperature Log, separately (even if SCT is unsupported. use devstat / attributes).
|
||||
|
||||
Add a button (with dropdown) to Info window, to run
|
||||
-l sataphy (advanced)
|
||||
-l directory (very advanced)
|
||||
-l scttemp (rarely supported and difficult to parse)
|
||||
Maybe add -l selective here (and remove it from GUI).
|
||||
Just show the output in a text window.
|
||||
"Put this all in Error Log", raname to Logs.
|
||||
Make it a dropdown combo and "Retrieve" button, showing the output where selective is now.
|
||||
|
||||
|
||||
Document RAID support.
|
||||
|
||||
@@ -375,7 +375,7 @@ void Cmdex::stopped_cleanup()
|
||||
// Called when child exits
|
||||
void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer data)
|
||||
{
|
||||
DBG_FUNCTION_ENTER_MSG;
|
||||
// DBG_FUNCTION_ENTER_MSG;
|
||||
Cmdex* self = static_cast<Cmdex*>(data);
|
||||
|
||||
g_timer_stop(self->timer_); // stop the timer
|
||||
@@ -426,7 +426,7 @@ void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer da
|
||||
if (self->exited_callback_)
|
||||
self->exited_callback_(self->exited_callback_data_);
|
||||
|
||||
DBG_FUNCTION_EXIT_MSG;
|
||||
// DBG_FUNCTION_EXIT_MSG;
|
||||
}
|
||||
|
||||
|
||||
@@ -436,7 +436,7 @@ void Cmdex::on_child_watch_handler(GPid arg_pid, int waitpid_status, gpointer da
|
||||
gboolean Cmdex::on_channel_io(GIOChannel* channel,
|
||||
GIOCondition cond, Cmdex* self, channel_t type)
|
||||
{
|
||||
DBG_FUNCTION_ENTER_MSG;
|
||||
// DBG_FUNCTION_ENTER_MSG;
|
||||
// debug_out_dump("app", "Cmdex::on_channel_io("
|
||||
// << (type == channel_type_stdout ? "STDOUT" : "STDERR") << ") " << int(cond) << "\n");
|
||||
|
||||
@@ -481,7 +481,7 @@ gboolean Cmdex::on_channel_io(GIOChannel* channel,
|
||||
}
|
||||
} while (g_io_channel_get_buffer_condition(channel) & G_IO_IN);
|
||||
|
||||
DBG_FUNCTION_EXIT_MSG;
|
||||
// DBG_FUNCTION_EXIT_MSG;
|
||||
|
||||
// false if the source should be removed, true otherwise.
|
||||
return continue_events;
|
||||
|
||||
@@ -1980,11 +1980,11 @@ Index Estimated Time Temperature Celsius
|
||||
// supported / unsupported
|
||||
{
|
||||
StorageProperty p(pt);
|
||||
p.set_name("SCT commands supported", "sct_supported");
|
||||
p.set_name("SCT commands unsupported", "sct_unsupported");
|
||||
|
||||
// p.reported_value; // nothing
|
||||
p.value_type = StorageProperty::value_type_bool;
|
||||
p.value_bool = !app_pcre_match("/(SCT Commands not supported)|(SCT Data Table command not supported)/mi", sub);
|
||||
p.value_bool = app_pcre_match("/(SCT Commands not supported)|(SCT Data Table command not supported)/mi", sub);
|
||||
|
||||
add_property(p);
|
||||
data_found = true;
|
||||
@@ -2043,7 +2043,7 @@ bool SmartctlParser::parse_section_data_subsection_devstat(const std::string& su
|
||||
{
|
||||
StorageProperty pt; // template for easy copying
|
||||
pt.section = StorageProperty::section_data;
|
||||
pt.subsection = StorageProperty::subsection_erc_log;
|
||||
pt.subsection = StorageProperty::subsection_devstat;
|
||||
|
||||
// devstat log contains:
|
||||
/*
|
||||
@@ -2062,18 +2062,6 @@ Page Offset Size Value Flags Description
|
||||
0x03 0x008 4 6356 -D- Spindle Motor Power-on Hours
|
||||
0x03 0x010 4 6356 -D- Head Flying Hours
|
||||
*/
|
||||
bool data_found = false; // true if something was found.
|
||||
|
||||
// the whole subsection
|
||||
{
|
||||
StorageProperty p(pt);
|
||||
p.set_name("Device statistics", "devstat_log");
|
||||
p.reported_value = sub;
|
||||
p.value_type = StorageProperty::value_type_string;
|
||||
p.value_string = p.reported_value;
|
||||
|
||||
add_property(p);
|
||||
}
|
||||
|
||||
// supported / unsupported
|
||||
{
|
||||
@@ -2085,12 +2073,79 @@ Page Offset Size Value Flags Description
|
||||
p.value_bool = !app_pcre_match("/Device Statistics \\(GP\\/SMART Log 0x04\\) not supported/mi", sub);
|
||||
|
||||
add_property(p);
|
||||
data_found = true;
|
||||
}
|
||||
|
||||
// TODO
|
||||
bool entries_found = false; // at least one entry was found
|
||||
|
||||
return data_found;}
|
||||
// split to lines
|
||||
std::vector<std::string> lines;
|
||||
hz::string_split(sub, '\n', lines, true);
|
||||
|
||||
std::string space_re = "[ \\t]+";
|
||||
|
||||
std::string flag_re = "([A-Z=-]{3,})";
|
||||
// Page Offset Size Value Flags Description
|
||||
std::string line_re = "[ \\t]*([0-9a-z]+)" + space_re + "([0-9a-z=]+)" + space_re + "([0-9=]+)" + space_re + "([0-9=-]+)" + space_re + flag_re + space_re + "(.+)";
|
||||
|
||||
pcrecpp::RE re_stat_line = app_pcre_re("/" + line_re + "/mi");
|
||||
pcrecpp::RE re_flag_descr = app_pcre_re("/^[\\t ]+\\|/mi");
|
||||
|
||||
|
||||
for(unsigned int i = 0; i < lines.size(); ++i) {
|
||||
std::string line = lines[i];
|
||||
|
||||
// skip the non-informative lines
|
||||
if (line.empty()
|
||||
|| app_pcre_match("/Device Statistics \\(GP Log 0x04\\)/mi", line)
|
||||
|| app_pcre_match("/Page[ \\t]+Offset[ \\t]+Size/mi", line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (re_flag_descr.PartialMatch(line)) { // " |||_ C monitored condition met", etc...
|
||||
continue; // skip flag description lines
|
||||
}
|
||||
|
||||
std::string page, offset, size, value, flags, description;
|
||||
|
||||
bool matched = true;
|
||||
if (!re_stat_line.FullMatch(line, &page, &offset, &size, &value, &flags, &description)) {
|
||||
matched = false;
|
||||
debug_out_warn("app", DBG_FUNC_MSG << "Cannot parse devstat line.\n");
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
debug_out_dump("app", "------------ Begin unparsable devstat line dump ------------\n");
|
||||
debug_out_dump("app", line << "\n");
|
||||
debug_out_dump("app", "------------- End unparsable devstat line dump -------------\n");
|
||||
continue; // continue to the next line
|
||||
}
|
||||
|
||||
|
||||
StorageStatistic st;
|
||||
st.is_header = (hz::string_trim_copy(value) == "=");
|
||||
st.flags = st.is_header ? std::string() : hz::string_trim_copy(flags);
|
||||
st.value = st.is_header ? std::string() : hz::string_trim_copy(value);
|
||||
hz::string_is_numeric(st.value, st.value_int, false);
|
||||
|
||||
if (st.is_header) {
|
||||
description = hz::string_trim_copy(hz::string_trim_copy(description, "="));
|
||||
}
|
||||
|
||||
StorageProperty p(pt);
|
||||
p.set_name(hz::string_trim_copy(description));
|
||||
p.reported_value = line; // use the whole line here
|
||||
p.value_type = StorageProperty::value_type_statistic;
|
||||
p.value_statistic = st;
|
||||
|
||||
add_property(p);
|
||||
entries_found = true;
|
||||
}
|
||||
|
||||
if (!entries_found)
|
||||
set_error_msg("No entries found in Statistics section.");
|
||||
|
||||
return entries_found;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -66,6 +66,14 @@ std::ostream& operator<< (std::ostream& os, const StorageAttribute& p)
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const StorageStatistic& p)
|
||||
{
|
||||
os << p.value;
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string StorageErrorBlock::get_readable_error_types(const std::vector<std::string>& types)
|
||||
{
|
||||
std::map<std::string, std::string> m;
|
||||
@@ -174,6 +182,9 @@ void StorageProperty::dump(std::ostream& os, int internal_offset) const
|
||||
case StorageProperty::value_type_attribute:
|
||||
os << value_attribute;
|
||||
break;
|
||||
case StorageProperty::value_type_statistic:
|
||||
os << value_statistic;
|
||||
break;
|
||||
case StorageProperty::value_type_error_block:
|
||||
os << value_error_block;
|
||||
break;
|
||||
@@ -205,6 +216,8 @@ std::string StorageProperty::format_value(bool add_reported_too) const
|
||||
return hz::stream_cast<std::string>(value_capability);
|
||||
case StorageProperty::value_type_attribute:
|
||||
return hz::stream_cast<std::string>(value_attribute);
|
||||
case StorageProperty::value_type_statistic:
|
||||
return hz::stream_cast<std::string>(value_statistic);
|
||||
case StorageProperty::value_type_error_block:
|
||||
return hz::stream_cast<std::string>(value_error_block);
|
||||
case StorageProperty::value_type_selftest_entry:
|
||||
|
||||
@@ -129,7 +129,7 @@ class StorageAttribute {
|
||||
|
||||
|
||||
int32_t id; ///< Attribute ID (most vendors agree on this)
|
||||
std::string flag; ///< Some have it in 0xXXXX format, others in "PO--C-" format (some WD drives?).
|
||||
std::string flag; ///< "Old" format is "0xXXXX", "brief" format is "PO--C-".
|
||||
hz::OptionalValue<uint8_t> value; ///< Normalized value. May be unset ("---").
|
||||
hz::OptionalValue<uint8_t> worst; ///< Worst ever value. May be unset ("---").
|
||||
hz::OptionalValue<uint8_t> threshold; ///< Threshold for normalized value. May be unset ("---").
|
||||
@@ -148,6 +148,27 @@ std::ostream& operator<< (std::ostream& os, const StorageAttribute& p);
|
||||
|
||||
|
||||
|
||||
/// Holds one line of "devstat" subsection
|
||||
class StorageStatistic {
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
StorageStatistic() : is_header(false), value_int(0)
|
||||
{ }
|
||||
|
||||
bool is_header; ///< If the line is a header
|
||||
std::string flags; ///< Flags in "NDC" / "---" format
|
||||
std::string value; ///< Value as a string, as presented by smartctl (formatted).
|
||||
int64_t value_int; ///< Same as value, but parsed as int64.
|
||||
};
|
||||
|
||||
|
||||
/// Output operator for debug purposes
|
||||
std::ostream& operator<< (std::ostream& os, const StorageStatistic& p);
|
||||
|
||||
|
||||
|
||||
|
||||
/// Holds one error block of "error log" subsection
|
||||
class StorageErrorBlock {
|
||||
public:
|
||||
@@ -290,6 +311,7 @@ class StorageProperty {
|
||||
|
||||
value_type_capability, ///< For "capabilities" subsection (non-time-interval blocks only)
|
||||
value_type_attribute, ///< For "attributes" subsection
|
||||
value_type_statistic, ///< For "devstat" subsection
|
||||
value_type_error_block, // For "error_log" subsection
|
||||
value_type_selftest_entry // For "selftest_log" subsection
|
||||
};
|
||||
@@ -305,6 +327,7 @@ class StorageProperty {
|
||||
case value_type_time_length: return "time_length";
|
||||
case value_type_capability: return "capability";
|
||||
case value_type_attribute: return "attribute";
|
||||
case value_type_statistic: return "statistic";
|
||||
case value_type_error_block: return "error_block";
|
||||
case value_type_selftest_entry: return "selftest_entry";
|
||||
}
|
||||
@@ -457,6 +480,7 @@ class StorageProperty {
|
||||
|
||||
StorageCapability value_capability; ///< Value (if it's a capability)
|
||||
StorageAttribute value_attribute; ///< Value (if it's an attribute)
|
||||
StorageStatistic value_statistic; ///< Value (if it's a statistic from devstat)
|
||||
StorageErrorBlock value_error_block; ///< Value (if it's a error block)
|
||||
StorageSelftestEntry value_selftest_entry; ///< Value (if it's a self-test entry)
|
||||
|
||||
|
||||
@@ -1306,57 +1306,76 @@ bool storage_property_autoset_description(StorageProperty& p, StorageAttribute::
|
||||
found = true;
|
||||
}
|
||||
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_health) {
|
||||
found = auto_set(p, "overall_health", "Overall health self-assessment test result. Note: If the drive passes this test, it doesn't mean it's OK. "
|
||||
"However, if the drive doesn't pass it, then it's either already dead, or it's predicting its own failure within the next 24 hours. In this case do a backup immediately!");
|
||||
} else if (p.section == StorageProperty::section_data) {
|
||||
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_capabilities) {
|
||||
found = auto_set(p, "offline_status_group", "Offline Data Collection (a.k.a. Offline test) is usually automatically performed when the device is idle or every fixed amount of time. "
|
||||
"This should show if Automatic Offline Data Collection is enabled.")
|
||||
|| auto_set(p, "iodc_total_time_length", "Offline Data Collection (a.k.a. Offline test) is usually automatically performed when the device is idle or every fixed amount of time. "
|
||||
"This value shows the estimated time required to perform this operation in idle conditions. A value of 0 means unsupported.")
|
||||
|| auto_set(p, "short_total_time_length", "This value shows the estimated time required to perform a short self-test in idle conditions. A value of 0 means unsupported.")
|
||||
|| auto_set(p, "long_total_time_length", "This value shows the estimated time required to perform a long self-test in idle conditions. A value of 0 means unsupported.")
|
||||
|| auto_set(p, "conveyance_total_time_length", "This value shows the estimated time required to perform a conveyance self-test in idle conditions. A value of 0 means unsupported.")
|
||||
|| auto_set(p, "last_selftest_cap_group", "Status of the last self-test run.")
|
||||
|| auto_set(p, "offline_cap_group", "Drive properties related to Offline Data Collection and self-tests.")
|
||||
|| auto_set(p, "smart_cap_group", "Drive properties related to SMART handling.")
|
||||
|| auto_set(p, "error_log_cap_group", "Drive properties related to error logging.")
|
||||
|| auto_set(p, "sct_cap_group", "Drive properties related to temperature information.");
|
||||
switch (p.subsection) {
|
||||
case StorageProperty::subsection_health:
|
||||
found = auto_set(p, "overall_health", "Overall health self-assessment test result. Note: If the drive passes this test, it doesn't mean it's OK. "
|
||||
"However, if the drive doesn't pass it, then it's either already dead, or it's predicting its own failure within the next 24 hours. In this case do a backup immediately!");
|
||||
break;
|
||||
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_attributes) {
|
||||
found = auto_set(p, "data_structure_version", p.readable_name.c_str());
|
||||
if (!found) {
|
||||
auto_set_attr(p, disk_type);
|
||||
found = true; // true, because auto_set_attr() may set "Unknown attribute", which is still "found".
|
||||
case StorageProperty::subsection_capabilities:
|
||||
found = auto_set(p, "offline_status_group", "Offline Data Collection (a.k.a. Offline test) is usually automatically performed when the device is idle or every fixed amount of time. "
|
||||
"This should show if Automatic Offline Data Collection is enabled.")
|
||||
|| auto_set(p, "iodc_total_time_length", "Offline Data Collection (a.k.a. Offline test) is usually automatically performed when the device is idle or every fixed amount of time. "
|
||||
"This value shows the estimated time required to perform this operation in idle conditions. A value of 0 means unsupported.")
|
||||
|| auto_set(p, "short_total_time_length", "This value shows the estimated time required to perform a short self-test in idle conditions. A value of 0 means unsupported.")
|
||||
|| auto_set(p, "long_total_time_length", "This value shows the estimated time required to perform a long self-test in idle conditions. A value of 0 means unsupported.")
|
||||
|| auto_set(p, "conveyance_total_time_length", "This value shows the estimated time required to perform a conveyance self-test in idle conditions. A value of 0 means unsupported.")
|
||||
|| auto_set(p, "last_selftest_cap_group", "Status of the last self-test run.")
|
||||
|| auto_set(p, "offline_cap_group", "Drive properties related to Offline Data Collection and self-tests.")
|
||||
|| auto_set(p, "smart_cap_group", "Drive properties related to SMART handling.")
|
||||
|| auto_set(p, "error_log_cap_group", "Drive properties related to error logging.")
|
||||
|| auto_set(p, "sct_cap_group", "Drive properties related to temperature information.");
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_attributes:
|
||||
found = auto_set(p, "data_structure_version", p.readable_name.c_str());
|
||||
if (!found) {
|
||||
auto_set_attr(p, disk_type);
|
||||
found = true; // true, because auto_set_attr() may set "Unknown attribute", which is still "found".
|
||||
}
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_devstat:
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_error_log:
|
||||
found = auto_set(p, "error_log_version", p.readable_name.c_str());
|
||||
found = auto_set(p, "error_count", "Number of errors in error log. Note: Some manufacturers may list completely harmless errors in this log "
|
||||
"(e.g., command invalid, not implemented, etc...).");
|
||||
// || auto_set(p, "error_log_unsupported", "This device does not support error logging."); // the property text already says that
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_selftest_log:
|
||||
found = auto_set(p, "selftest_log_version", p.readable_name.c_str());
|
||||
found = auto_set(p, "selftest_num_entries", "Number of tests in selftest log. Note: This log usually contains only the last 20 or so manual tests. ");
|
||||
// || auto_set(p, "selftest_log_unsupported", "This device does not support self-test logging."); // the property text already says that
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_selective_selftest_log:
|
||||
// nothing here
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_temperature_log:
|
||||
// nothing here
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_erc_log:
|
||||
// nothing here
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_phy_log:
|
||||
// nothing here
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_directory_log:
|
||||
// nothing here
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_error_log) {
|
||||
found = auto_set(p, "error_log_version", p.readable_name.c_str());
|
||||
found = auto_set(p, "error_count", "Number of errors in error log. Note: Some manufacturers may list completely harmless errors in this log "
|
||||
"(e.g., command invalid, not implemented, etc...).");
|
||||
// || auto_set(p, "error_log_unsupported", "This device does not support error logging."); // the property text already says that
|
||||
|
||||
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_selftest_log) {
|
||||
found = auto_set(p, "selftest_log_version", p.readable_name.c_str());
|
||||
found = auto_set(p, "selftest_num_entries", "Number of tests in selftest log. Note: This log usually contains only the last 20 or so manual tests. ");
|
||||
// || auto_set(p, "selftest_log_unsupported", "This device does not support self-test logging."); // the property text already says that
|
||||
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_selective_selftest_log) {
|
||||
// nothing here
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_temperature_log) {
|
||||
// TODO Current temperature
|
||||
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_erc_log) {
|
||||
// nothing here
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_phy_log) {
|
||||
// nothing here
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_directory_log) {
|
||||
// nothing here
|
||||
}
|
||||
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
@@ -1386,143 +1405,164 @@ StorageProperty::warning_t storage_property_autoset_warning(StorageProperty& p)
|
||||
"Additionally, some drives do not log useful data with SMART disabled, so it's advisable to keep it always enabled.";
|
||||
}
|
||||
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_health) {
|
||||
if (name_match(p, "overall_health") && p.value_string != "PASSED") {
|
||||
w = StorageProperty::warning_alert;
|
||||
reason = "The drive is reporting that it will FAIL very soon. Please back up as soon as possible!";
|
||||
}
|
||||
} else if (p.section == StorageProperty::section_data) {
|
||||
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_capabilities) {
|
||||
// nothing
|
||||
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_attributes) {
|
||||
|
||||
// Set notices for known pre-fail attributes
|
||||
|
||||
// Reallocated Sector Count
|
||||
if (attr_match(p, "reallocated_sector_count") && p.value_attribute.raw_value_int > 0) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors.";
|
||||
|
||||
// Spin-up Retry Count
|
||||
} else if (attr_match(p, "spin_up_retry_count") && p.value_attribute.raw_value_int > 0) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. Your drive may have problems spinning up, which could lead to a complete mechanical failure. Please back up.";
|
||||
|
||||
// Soft Read Error Rate
|
||||
} else if (attr_match(p, "soft_read_error_rate") && p.value_attribute.raw_value_int > 0) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. 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, "temperature_celsius")
|
||||
&& p.value_attribute.raw_value_int > 50 && p.value_attribute.raw_value_int <= 120) { // 50C
|
||||
w = StorageProperty::warning_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, "temperature_celsius_x10") && p.value_attribute.raw_value_int > 500) { // 50C
|
||||
w = StorageProperty::warning_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.";
|
||||
|
||||
// Reallocation Event Count
|
||||
} else if (attr_match(p, "reallocation_event_count") && p.value_attribute.raw_value_int > 0) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors.";
|
||||
|
||||
// Current Pending Sector Count
|
||||
} else if ((attr_match(p, "current_pending_sector_count") || attr_match(p, "total_pending_sectors"))
|
||||
&& p.value_attribute.raw_value_int > 0) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors.";
|
||||
|
||||
// Uncorrectable Sector Count
|
||||
} else if ((attr_match(p, "offline_uncorrectable") || attr_match(p, "total_offline_uncorrectable"))
|
||||
&& p.value_attribute.raw_value_int > 0) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors.";
|
||||
|
||||
// SSD Life Left (%)
|
||||
} else if ((attr_match(p, "ssd_life_left"))
|
||||
&& p.value_attribute.value.value() < 50) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has less than half of its life left.";
|
||||
|
||||
// SSD Life Used (%)
|
||||
} else if ((attr_match(p, "ssd_life_used"))
|
||||
&& p.value_attribute.value.value() >= 50) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has less than half of its life left.";
|
||||
}
|
||||
|
||||
|
||||
// Now override this with SMART warnings / errors
|
||||
|
||||
if (p.value_type == StorageProperty::value_type_attribute) {
|
||||
if (p.value_attribute.when_failed == StorageAttribute::fail_time_now) { // NOW
|
||||
|
||||
if (p.value_attribute.attr_type == StorageAttribute::attr_type_oldage) { // old-age
|
||||
w = StorageProperty::warning_warn;
|
||||
reason = "The drive has a failing old-age attribute. Usually this indicates a wear-out. You should consider replacing the drive.";
|
||||
} else { // pre-fail
|
||||
switch(p.subsection) {
|
||||
case StorageProperty::subsection_health:
|
||||
if (name_match(p, "overall_health") && p.value_string != "PASSED") {
|
||||
w = StorageProperty::warning_alert;
|
||||
reason = "The drive has a failing pre-fail attribute. Usually this indicates a that the drive will FAIL soon. Please back up immediately!";
|
||||
reason = "The drive is reporting that it will FAIL very soon. Please back up as soon as possible!";
|
||||
}
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_capabilities:
|
||||
// nothing
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_attributes:
|
||||
|
||||
// Set notices for known pre-fail attributes
|
||||
|
||||
// Reallocated Sector Count
|
||||
if (attr_match(p, "reallocated_sector_count") && p.value_attribute.raw_value_int > 0) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors.";
|
||||
|
||||
// Spin-up Retry Count
|
||||
} else if (attr_match(p, "spin_up_retry_count") && p.value_attribute.raw_value_int > 0) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. Your drive may have problems spinning up, which could lead to a complete mechanical failure. Please back up.";
|
||||
|
||||
// Soft Read Error Rate
|
||||
} else if (attr_match(p, "soft_read_error_rate") && p.value_attribute.raw_value_int > 0) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. 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, "temperature_celsius")
|
||||
&& p.value_attribute.raw_value_int > 50 && p.value_attribute.raw_value_int <= 120) { // 50C
|
||||
w = StorageProperty::warning_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, "temperature_celsius_x10") && p.value_attribute.raw_value_int > 500) { // 50C
|
||||
w = StorageProperty::warning_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.";
|
||||
|
||||
// Reallocation Event Count
|
||||
} else if (attr_match(p, "reallocation_event_count") && p.value_attribute.raw_value_int > 0) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors.";
|
||||
|
||||
// Current Pending Sector Count
|
||||
} else if ((attr_match(p, "current_pending_sector_count") || attr_match(p, "total_pending_sectors"))
|
||||
&& p.value_attribute.raw_value_int > 0) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors.";
|
||||
|
||||
// Uncorrectable Sector Count
|
||||
} else if ((attr_match(p, "offline_uncorrectable") || attr_match(p, "total_offline_uncorrectable"))
|
||||
&& p.value_attribute.raw_value_int > 0) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has a non-zero Raw value, but there is no SMART warning yet. This could be an indication of future failures and/or potential data loss in bad sectors.";
|
||||
|
||||
// SSD Life Left (%)
|
||||
} else if ((attr_match(p, "ssd_life_left"))
|
||||
&& p.value_attribute.value.value() < 50) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has less than half of its life left.";
|
||||
|
||||
// SSD Life Used (%)
|
||||
} else if ((attr_match(p, "ssd_life_used"))
|
||||
&& p.value_attribute.value.value() >= 50) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive has less than half of its life left.";
|
||||
}
|
||||
|
||||
} else if (p.value_attribute.when_failed == StorageAttribute::fail_time_past) { // PAST
|
||||
// Now override this with SMART warnings / errors
|
||||
|
||||
if (p.value_attribute.attr_type == StorageAttribute::attr_type_oldage) { // old-age
|
||||
// nothing. we don't warn about e.g. temperature increase in the past
|
||||
} else { // pre-fail
|
||||
w = StorageProperty::warning_warn; // there was a problem, it got corrected (hopefully)
|
||||
reason = "The drive had a failing pre-fail attribute, but it has been restored to a normal value. This may be a serious problem, you should consider replacing the drive.";
|
||||
if (p.value_type == StorageProperty::value_type_attribute) {
|
||||
if (p.value_attribute.when_failed == StorageAttribute::fail_time_now) { // NOW
|
||||
|
||||
if (p.value_attribute.attr_type == StorageAttribute::attr_type_oldage) { // old-age
|
||||
w = StorageProperty::warning_warn;
|
||||
reason = "The drive has a failing old-age attribute. Usually this indicates a wear-out. You should consider replacing the drive.";
|
||||
} else { // pre-fail
|
||||
w = StorageProperty::warning_alert;
|
||||
reason = "The drive has a failing pre-fail attribute. Usually this indicates a that the drive will FAIL soon. Please back up immediately!";
|
||||
}
|
||||
|
||||
} else if (p.value_attribute.when_failed == StorageAttribute::fail_time_past) { // PAST
|
||||
|
||||
if (p.value_attribute.attr_type == StorageAttribute::attr_type_oldage) { // old-age
|
||||
// nothing. we don't warn about e.g. temperature increase in the past
|
||||
} else { // pre-fail
|
||||
w = StorageProperty::warning_warn; // there was a problem, it got corrected (hopefully)
|
||||
reason = "The drive had a failing pre-fail attribute, but it has been restored to a normal value. This may be a serious problem, you should consider replacing the drive.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_devstat:
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_error_log:
|
||||
// Note: The error list table doesn't display any descriptions, so if any
|
||||
// error-entry related descriptions are added here, don't forget to enable
|
||||
// the tooltips.
|
||||
|
||||
if (name_match(p, "error_count") && p.value_integer > 0) {
|
||||
w = StorageProperty::warning_warn;
|
||||
reason = "The drive is reporting internal errors. Usually this means uncorrectable data loss and similar severe errors. "
|
||||
"Check the actual errors for details.";
|
||||
|
||||
} else if (name_match(p, "error_log_unsupported")) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive does not support error logging. This means that SMART error history is unavailable.";
|
||||
}
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_selftest_log:
|
||||
// Note: The error list table doesn't display any descriptions, so if any
|
||||
// error-entry related descriptions are added here, don't forget to enable
|
||||
// the tooltips.
|
||||
|
||||
// Don't include selftest warnings - they may be old or something.
|
||||
// Self-tests are carried manually anyway, so the user is expected to check their status anyway.
|
||||
|
||||
if (name_match(p, "selftest_log_unsupported")) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive does not support self-test logging. This means that SMART test results won't be logged.";
|
||||
}
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_selective_selftest_log:
|
||||
// nothing here
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_temperature_log:
|
||||
if (name_match(p, "sct_unsupported")) { // TODO show this somewhere
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive does not support SCT Temperature logging. Check the Attributes tab for current temperature.";
|
||||
}
|
||||
// TODO Current temperature
|
||||
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_erc_log:
|
||||
// nothing here
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_phy_log:
|
||||
// nothing here
|
||||
break;
|
||||
|
||||
case StorageProperty::subsection_directory_log:
|
||||
// nothing here
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_error_log) {
|
||||
|
||||
// Note: The error list table doesn't display any descriptions, so if any
|
||||
// error-entry related descriptions are added here, don't forget to enable
|
||||
// the tooltips.
|
||||
|
||||
if (name_match(p, "error_count") && p.value_integer > 0) {
|
||||
w = StorageProperty::warning_warn;
|
||||
reason = "The drive is reporting internal errors. Usually this means uncorrectable data loss and similar severe errors. "
|
||||
"Check the actual errors for details.";
|
||||
|
||||
} else if (name_match(p, "error_log_unsupported")) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive does not support error logging. This means that SMART error history is unavailable.";
|
||||
}
|
||||
|
||||
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_selftest_log) {
|
||||
|
||||
// Note: The error list table doesn't display any descriptions, so if any
|
||||
// error-entry related descriptions are added here, don't forget to enable
|
||||
// the tooltips.
|
||||
|
||||
// Don't include selftest warnings - they may be old or something.
|
||||
// Self-tests are carried manually anyway, so the user is expected to check their status anyway.
|
||||
|
||||
if (name_match(p, "selftest_log_unsupported")) {
|
||||
w = StorageProperty::warning_notice;
|
||||
reason = "The drive does not support self-test logging. This means that SMART test results won't be logged.";
|
||||
}
|
||||
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_selective_selftest_log) {
|
||||
// nothing here
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_temperature_log) {
|
||||
// nothing here
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_erc_log) {
|
||||
// nothing here
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_phy_log) {
|
||||
// nothing here
|
||||
} else if (p.section == StorageProperty::section_data && p.subsection == StorageProperty::subsection_directory_log) {
|
||||
// nothing here
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -68,13 +68,16 @@ namespace {
|
||||
delete wv[i]; // since it's without parent anymore, it won't be auto-deleted.
|
||||
}
|
||||
|
||||
vbox->set_visible(!label_strings.empty());
|
||||
|
||||
if (label_strings.empty()) {
|
||||
// add one label only
|
||||
Gtk::Label* label = Gtk::manage(new Gtk::Label("No data available", Gtk::ALIGN_START));
|
||||
label->set_padding(6, 0);
|
||||
vbox->pack_start(*label, false, false);
|
||||
// Gtk::Label* label = Gtk::manage(new Gtk::Label("No data available", Gtk::ALIGN_START));
|
||||
// label->set_padding(6, 0);
|
||||
// vbox->pack_start(*label, false, false);
|
||||
|
||||
} else {
|
||||
|
||||
// add one label per element
|
||||
for (label_list_t::const_iterator iter = label_strings.begin(); iter != label_strings.end(); ++iter) {
|
||||
std::string label_text = Glib::Markup::escape_text(iter->label);
|
||||
@@ -126,6 +129,35 @@ namespace {
|
||||
|
||||
|
||||
|
||||
/// Cell renderer functions for attribute cells
|
||||
inline void app_statistic_cell_renderer_func(Gtk::CellRenderer* cr, const Gtk::TreeModel::iterator& iter,
|
||||
Gtk::TreeModelColumn<const StorageProperty*> storage_column)
|
||||
{
|
||||
const StorageProperty* p = (*iter)[storage_column];
|
||||
Gtk::CellRendererText* crt = hz::down_cast<Gtk::CellRendererText*>(cr);
|
||||
if (crt) {
|
||||
std::string fg, bg;
|
||||
if (app_property_get_row_highlight_colors(p->warning, fg, bg)) {
|
||||
// Note: property_cell_background makes horizontal tree lines disappear around it,
|
||||
// but property_background doesn't play nice with sorted column color.
|
||||
crt->property_cell_background() = bg;
|
||||
crt->property_foreground() = fg;
|
||||
} else {
|
||||
// this is needed because cellrenderer is shared in column, so the previous call
|
||||
// may set the color for all subsequent cells.
|
||||
crt->property_cell_background().reset_value();
|
||||
crt->property_foreground().reset_value();
|
||||
}
|
||||
if (p->value_statistic.is_header) {
|
||||
crt->property_weight() = Pango::WEIGHT_BOLD;
|
||||
} else {
|
||||
crt->property_weight().reset_value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Highlight a tab label according to \c warning
|
||||
inline void app_highlight_tab_label(Gtk::Widget* label_widget,
|
||||
StorageProperty::warning_t warning, const Glib::ustring& original_label)
|
||||
@@ -567,7 +599,7 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests
|
||||
treeview->set_search_column(col_name.index());
|
||||
Gtk::CellRendererText* cr_name = hz::down_cast<Gtk::CellRendererText*>(treeview->get_column_cell_renderer(num_tree_cols - 1));
|
||||
if (cr_name)
|
||||
cr_name->property_weight() = Pango::WEIGHT_BOLD ;
|
||||
cr_name->property_weight() = Pango::WEIGHT_BOLD;
|
||||
|
||||
Gtk::TreeModelColumn<Glib::ustring> col_failed;
|
||||
model_columns.add(col_failed);
|
||||
@@ -641,7 +673,6 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests
|
||||
|
||||
|
||||
StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none;
|
||||
int index = 1;
|
||||
label_list_t label_strings; // outside-of-tree properties
|
||||
|
||||
for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) {
|
||||
@@ -682,8 +713,6 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests
|
||||
|
||||
if (int(iter->warning) > int(max_tab_warning))
|
||||
max_tab_warning = iter->warning;
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
|
||||
@@ -700,6 +729,97 @@ void GscInfoWindow::fill_ui_with_info(bool scan, bool clear_ui, bool clear_tests
|
||||
// ------------------------------------------- Statistics
|
||||
|
||||
|
||||
do {
|
||||
|
||||
Gtk::TreeView* treeview = lookup_widget<Gtk::TreeView*>("statistics_treeview");
|
||||
if (!treeview)
|
||||
break;
|
||||
|
||||
Gtk::TreeModelColumnRecord model_columns;
|
||||
unsigned int num_tree_cols = 0;
|
||||
|
||||
Gtk::TreeModelColumn<Glib::ustring> col_description;
|
||||
model_columns.add(col_description);
|
||||
num_tree_cols = app_gtkmm_create_tree_view_column(col_description, *treeview,
|
||||
"Description", "Entry description", true);
|
||||
treeview->set_search_column(col_description.index());
|
||||
// Gtk::CellRendererText* cr_name = hz::down_cast<Gtk::CellRendererText*>(treeview->get_column_cell_renderer(num_tree_cols - 1));
|
||||
// if (cr_name)
|
||||
// cr_name->property_weight() = Pango::WEIGHT_BOLD ;
|
||||
|
||||
Gtk::TreeModelColumn<std::string> col_value;
|
||||
model_columns.add(col_value);
|
||||
num_tree_cols = app_gtkmm_create_tree_view_column(col_value, *treeview,
|
||||
"Value", "Value (can be normalized if 'N' flag is present)", false);
|
||||
|
||||
Gtk::TreeModelColumn<std::string> col_flags;
|
||||
model_columns.add(col_flags);
|
||||
num_tree_cols = app_gtkmm_create_tree_view_column(col_flags, *treeview,
|
||||
"Flags", "Flags\n\n"
|
||||
"N: value is normalized\n"
|
||||
"D: supports DSN\n"
|
||||
"C: monitored condition met", false);
|
||||
|
||||
Gtk::TreeModelColumn<Glib::ustring> col_tooltip;
|
||||
model_columns.add(col_tooltip);
|
||||
treeview->set_tooltip_column(col_tooltip.index());
|
||||
|
||||
|
||||
Gtk::TreeModelColumn<const StorageProperty*> col_storage;
|
||||
model_columns.add(col_storage);
|
||||
|
||||
|
||||
// create a TreeModel (ListStore)
|
||||
Glib::RefPtr<Gtk::ListStore> list_store = Gtk::ListStore::create(model_columns);
|
||||
treeview->set_model(list_store);
|
||||
// No sorting (we don't want to screw up the headers).
|
||||
|
||||
for (unsigned int i = 0; i < num_tree_cols; ++i) {
|
||||
Gtk::TreeViewColumn* tcol = treeview->get_column(i);
|
||||
tcol->set_cell_data_func(*(tcol->get_first_cell()),
|
||||
sigc::bind(sigc::ptr_fun(app_statistic_cell_renderer_func), col_storage));
|
||||
}
|
||||
|
||||
|
||||
|
||||
StorageProperty::warning_t max_tab_warning = StorageProperty::warning_none;
|
||||
label_list_t label_strings; // outside-of-tree properties
|
||||
|
||||
for (prop_iterator iter = props.begin(); iter != props.end(); ++iter) {
|
||||
if (iter->section != StorageProperty::section_data || iter->subsection != StorageProperty::subsection_devstat)
|
||||
continue;
|
||||
|
||||
// add non-entry-type properties to label above
|
||||
if (iter->value_type != StorageProperty::value_type_statistic) {
|
||||
label_strings.push_back(PropertyLabel(iter->readable_name + ": " + iter->format_value(), &(*iter)));
|
||||
|
||||
if (int(iter->warning) > int(max_tab_warning))
|
||||
max_tab_warning = iter->warning;
|
||||
continue;
|
||||
}
|
||||
|
||||
Gtk::TreeRow row = *(list_store->append());
|
||||
|
||||
row[col_description] = (iter->value_statistic.is_header ? iter->readable_name : (" " + iter->readable_name));
|
||||
row[col_value] = iter->value_statistic.value;
|
||||
row[col_flags] = iter->value_statistic.flags; // it's a string, not int.
|
||||
row[col_tooltip] = iter->get_description();
|
||||
row[col_storage] = &(*iter);
|
||||
|
||||
if (int(iter->warning) > int(max_tab_warning))
|
||||
max_tab_warning = iter->warning;
|
||||
}
|
||||
|
||||
|
||||
Gtk::Box* label_vbox = lookup_widget<Gtk::Box*>("statistics_label_vbox");
|
||||
app_set_top_labels(label_vbox, label_strings);
|
||||
|
||||
// tab label
|
||||
app_highlight_tab_label(lookup_widget("statistics_tab_label"), max_tab_warning, tab_statistics_name);
|
||||
|
||||
} while (false);
|
||||
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------- Tests
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
<object class="GtkViewport">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">6</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="identity_table">
|
||||
<property name="visible">True</property>
|
||||
@@ -148,7 +149,6 @@
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="tab_expand">True</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
@@ -156,7 +156,9 @@
|
||||
<object class="GtkBox" id="attributes_tab_vbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="attributes_label_vbox">
|
||||
<property name="visible">True</property>
|
||||
@@ -212,7 +214,6 @@
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
<property name="tab_expand">True</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
@@ -220,36 +221,47 @@
|
||||
<object class="GtkBox" id="statistics_tab_vbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox3">
|
||||
<object class="GtkBox" id="statistics_label_vbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">3</property>
|
||||
<child>
|
||||
<object class="GtkPaned" id="vpaned2">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">4</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow10">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="statistics_treeview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="position_set">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
<property name="headers_clickable">False</property>
|
||||
<property name="enable_grid_lines">both</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -268,7 +280,6 @@
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
<property name="tab_expand">True</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
@@ -276,7 +287,7 @@
|
||||
<object class="GtkBox" id="test_tab_vbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">11</property>
|
||||
<property name="border_width">12</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
@@ -296,7 +307,7 @@
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
@@ -515,7 +526,6 @@
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">6</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
@@ -523,6 +533,7 @@
|
||||
<object class="GtkFrame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">6</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
@@ -611,7 +622,6 @@ Self-test log contains information about the most recent manually performed SMAR
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">3</property>
|
||||
<property name="tab_expand">True</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
@@ -619,12 +629,14 @@ Self-test log contains information about the most recent manually performed SMAR
|
||||
<object class="GtkBox" id="error_log_tab_vbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="error_log_label_vbox">
|
||||
<property name="visible">True</property>
|
||||
@@ -735,7 +747,6 @@ Self-test log contains information about the most recent manually performed SMAR
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">4</property>
|
||||
<property name="tab_expand">True</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
@@ -776,7 +787,7 @@ Self-test log contains information about the most recent manually performed SMAR
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="temperature_tab_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Current temperature and history</property>
|
||||
<property name="label" translatable="yes">Temperature Log</property>
|
||||
</object>
|
||||
@@ -795,7 +806,6 @@ Self-test log contains information about the most recent manually performed SMAR
|
||||
<object class="GtkNotebook" id="advanced_notebook">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="show_border">False</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="capabilities_scrolledwindow">
|
||||
<property name="visible">True</property>
|
||||
@@ -975,7 +985,6 @@ Self-test log contains information about the most recent manually performed SMAR
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">6</property>
|
||||
<property name="tab_expand">True</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
Reference in New Issue
Block a user