diff --git a/launch/tier4_system_launch/config/system_error_monitor/diagnostic_aggregator/system.param.yaml b/launch/tier4_system_launch/config/system_error_monitor/diagnostic_aggregator/system.param.yaml
index af6b9ab8a64c2..443695c9f0e69 100644
--- a/launch/tier4_system_launch/config/system_error_monitor/diagnostic_aggregator/system.param.yaml
+++ b/launch/tier4_system_launch/config/system_error_monitor/diagnostic_aggregator/system.param.yaml
@@ -171,3 +171,13 @@
path: tasks_summary
contains: [": Tasks Summary"]
timeout: 3.0
+
+ hardware:
+ type: diagnostic_aggregator/AnalyzerGroup
+ path: voltage
+ analyzers:
+ cmos_battery:
+ type: diagnostic_aggregator/GenericAnalyzer
+ path: cmos_battery
+ contains: [": CMOS Battery Status"]
+ timeout: 3.0
diff --git a/launch/tier4_system_launch/config/system_monitor/voltage_monitor.param.yaml b/launch/tier4_system_launch/config/system_monitor/voltage_monitor.param.yaml
new file mode 100644
index 0000000000000..e55410be39250
--- /dev/null
+++ b/launch/tier4_system_launch/config/system_monitor/voltage_monitor.param.yaml
@@ -0,0 +1,5 @@
+/**:
+ ros__parameters:
+ cmos_battery_warn: 2.90
+ cmos_battery_error: 2.70
+ cmos_battery_label: ""
diff --git a/system/system_error_monitor/config/diagnostic_aggregator/system.param.yaml b/system/system_error_monitor/config/diagnostic_aggregator/system.param.yaml
index fff9f8615f1ea..01e973b8ff26a 100644
--- a/system/system_error_monitor/config/diagnostic_aggregator/system.param.yaml
+++ b/system/system_error_monitor/config/diagnostic_aggregator/system.param.yaml
@@ -225,3 +225,13 @@
path: tasks_summary
contains: [": Tasks Summary"]
timeout: 3.0
+
+ hardware:
+ type: diagnostic_aggregator/AnalyzerGroup
+ path: voltage
+ analyzers:
+ cmos_battery:
+ type: diagnostic_aggregator/GenericAnalyzer
+ path: cmos_battery
+ contains: [": CMOS Battery Status"]
+ timeout: 3.0
diff --git a/system/system_monitor/CMakeLists.txt b/system/system_monitor/CMakeLists.txt
index a20beca4135e0..c5a2daa3e87ba 100644
--- a/system/system_monitor/CMakeLists.txt
+++ b/system/system_monitor/CMakeLists.txt
@@ -105,6 +105,10 @@ ament_auto_add_library(gpu_monitor_lib SHARED
${GPU_MONITOR_SOURCE}
)
+ament_auto_add_library(voltage_monitor_lib SHARED
+ src/voltage_monitor/voltage_monitor.cpp
+)
+
ament_auto_add_executable(msr_reader
reader/msr_reader/msr_reader.cpp
)
@@ -129,6 +133,7 @@ find_package(Boost REQUIRED COMPONENTS
)
## Specify libraries to link a library or executable target against
+target_link_libraries(voltage_monitor_lib ${Boost_LIBRARIES} ${LIBRARIES})
target_link_libraries(cpu_monitor_lib ${Boost_LIBRARIES} ${LIBRARIES})
target_link_libraries(hdd_monitor_lib ${Boost_LIBRARIES} ${LIBRARIES})
target_link_libraries(mem_monitor_lib ${LIBRARIES})
@@ -175,6 +180,11 @@ rclcpp_components_register_node(gpu_monitor_lib
EXECUTABLE gpu_monitor
)
+rclcpp_components_register_node(voltage_monitor_lib
+ PLUGIN "VoltageMonitor"
+ EXECUTABLE voltage_monitor
+)
+
# TODO(yunus.caliskan): Port the tests to ROS2, robustify the tests.
if(BUILD_TESTING)
# ament_add_ros_isolated_gtest(test_cpu_monitor
diff --git a/system/system_monitor/README.md b/system/system_monitor/README.md
index c30709d5cffea..54d72c840f98a 100644
--- a/system/system_monitor/README.md
+++ b/system/system_monitor/README.md
@@ -13,6 +13,7 @@ This package provides the following nodes for monitoring system:
- NTP Monitor
- Process Monitor
- GPU Monitor
+- Voltage Monitor
### Supported architecture
@@ -50,6 +51,7 @@ Every topic is published in 1 minute interval.
- [NTP Monitor](docs/topics_ntp_monitor.md)
- [Process Monitor](docs/topics_process_monitor.md)
- [GPU Monitor](docs/topics_gpu_monitor.md)
+- [Voltage Monitor](docs/topics_voltage_monitor.md)
[Usage] ✓:Supported, -:Not supported
@@ -83,6 +85,7 @@ Every topic is published in 1 minute interval.
| | GPU Memory Usage | ✓ | - | - | |
| | GPU Thermal Throttling | ✓ | - | - | |
| | GPU Frequency | ✓ | ✓ | - | For Intel platform, monitor whether current GPU clock is supported by the GPU. |
+| Voltage Monitor | CMOS Battery Staus | ✓ | - | - | Battery Health for RTC and BIOS - |
## ROS parameters
@@ -183,6 +186,85 @@ Currently GPU monitor for intel platform only supports NVIDIA GPU whose informat
Also you need to install CUDA libraries.
For installation instructions for CUDA 10.0, see [NVIDIA CUDA Installation Guide for Linux](https://docs.nvidia.com/cuda/archive/10.0/cuda-installation-guide-linux/index.html).
+## Voltage monitor for CMOS Battery
+
+Some platforms have built-in batteries for the RTC and CMOS. This node determines the battery status from the result of executing cat /proc/driver/rtc.
+Also, if lm-sensors is installed, it is possible to use the results.
+However, the return value of sensors varies depending on the chipset, so it is necessary to set a string to extract the corresponding voltage.
+It is also necessary to set the voltage for warning and error.
+For example, if you want a warning when the voltage is less than 2.9V and an error when it is less than 2.7V.
+The execution result of sensors on the chipset nct6106 is as follows, and "in7:" is the voltage of the CMOS battery.
+
+```txt
+$ sensors
+pch_cannonlake-virtual-0
+Adapter: Virtual device
+temp1: +42.0°C
+
+nct6106-isa-0a10
+Adapter: ISA adapter
+in0: 728.00 mV (min = +0.00 V, max = +1.74 V)
+in1: 1.01 V (min = +0.00 V, max = +2.04 V)
+in2: 3.34 V (min = +0.00 V, max = +4.08 V)
+in3: 3.34 V (min = +0.00 V, max = +4.08 V)
+in4: 1.07 V (min = +0.00 V, max = +2.04 V)
+in5: 1.05 V (min = +0.00 V, max = +2.04 V)
+in6: 1.67 V (min = +0.00 V, max = +2.04 V)
+in7: 3.06 V (min = +0.00 V, max = +4.08 V)
+in8: 2.10 V (min = +0.00 V, max = +4.08 V)
+fan1: 2789 RPM (min = 0 RPM)
+fan2: 0 RPM (min = 0 RPM)
+```
+
+The setting value of voltage_monitor.param.yaml is as follows.
+
+```yaml
+/**:
+ ros__parameters:
+ cmos_battery_warn: 2.90
+ cmos_battery_error: 2.70
+ cmos_battery_label: "in7:"
+```
+
+The above values of 2.7V and 2.90V are hypothetical. Depending on the motherboard and chipset, the value may vary. However, if the voltage of the lithium battery drops below 2.7V, it is recommended to replace it.
+In the above example, the message output to the topic /diagnostics is as follows.
+If the voltage < 2.9V then:
+
+```txt
+ name: /autoware/system/resource_monitoring/voltage/cmos_battery
+ message: Warning
+ hardware_id: ''
+ values:
+ - key: 'voltage_monitor: CMOS Battery Status'
+ value: Low Battery
+```
+
+If the voltage < 2.7V then:
+
+```txt
+ name: /autoware/system/resource_monitoring/voltage/cmos_battery
+ message: Warning
+ hardware_id: ''
+ values:
+ - key: 'voltage_monitor: CMOS Battery Status'
+ value: Battery Died
+```
+
+If neither, then:
+
+```txt
+ name: /autoware/system/resource_monitoring/voltage/cmos_battery
+ message: OK
+ hardware_id: ''
+ values:
+ - key: 'voltage_monitor: CMOS Battery Status'
+ value: OK
+```
+
+If the CMOS battery voltage drops less than voltage_error or voltage_warn,It will be a warning.
+If the battery runs out, the RTC will stop working when the power is turned off. However, since the vehicle can run, it is not an error. The vehicle will stop when an error occurs, but there is no need to stop immediately.
+It can be determined by the value of "Low Battery" or "Battery Died".
+
## UML diagrams
See [Class diagrams](docs/class_diagrams.md).
diff --git a/system/system_monitor/config/voltage_monitor.param.yaml b/system/system_monitor/config/voltage_monitor.param.yaml
new file mode 100644
index 0000000000000..e55410be39250
--- /dev/null
+++ b/system/system_monitor/config/voltage_monitor.param.yaml
@@ -0,0 +1,5 @@
+/**:
+ ros__parameters:
+ cmos_battery_warn: 2.90
+ cmos_battery_error: 2.70
+ cmos_battery_label: ""
diff --git a/system/system_monitor/docs/ros_parameters.md b/system/system_monitor/docs/ros_parameters.md
index a5bb41cb7c769..56dd746f985f7 100644
--- a/system/system_monitor/docs/ros_parameters.md
+++ b/system/system_monitor/docs/ros_parameters.md
@@ -100,3 +100,13 @@ gpu_monitor:
| gpu_usage_error | float | %(1e-2) | 1.00 | Generates error when GPU usage reaches a specified value or higher. |
| memory_usage_warn | float | %(1e-2) | 0.90 | Generates warning when GPU memory usage reaches a specified value or higher. |
| memory_usage_error | float | %(1e-2) | 1.00 | Generates error when GPU memory usage reaches a specified value or higher. |
+
+## Voltage Monitor
+
+voltage_monitor:
+
+| Name | Type | Unit | Default | Notes |
+| :----------------- | :----: | :--: | :-----: | :------------------------------------------------------------------------------ |
+| cmos_battery_warn | float | volt | 2.9 | Generates warning when voltage of CMOS Battery is lower. |
+| cmos_battery_error | float | volt | 2.7 | Generates error when voltage of CMOS Battery is lower. |
+| cmos_battery_label | string | n/a | "" | voltage string in sensors command outputs. if emtpy no voltage will be checked. |
diff --git a/system/system_monitor/docs/topics_voltage_monitor.md b/system/system_monitor/docs/topics_voltage_monitor.md
new file mode 100644
index 0000000000000..f1a70178e485c
--- /dev/null
+++ b/system/system_monitor/docs/topics_voltage_monitor.md
@@ -0,0 +1,42 @@
+# ROS topics: Voltage Monitor
+
+"CMOS Battery Status" and "CMOS battey voltage" are exclusive.
+Only one or the other is generated.
+Which one is generated depends on the value of cmos_battery_label.
+
+## CMOS Battery Status
+
+/diagnostics/voltage_monitor: CMOS Battery Status
+
+[summary]
+
+| level | message |
+| ----- | ------------ |
+| OK | OK |
+| WARN | Battery Dead |
+
+[values]
+
+| key (example) | value (example) |
+| ------------------ | ----------------- |
+| CMOS battey status | OK / Battery Dead |
+
+\*key: thermal_zone[0-9] for ARM architecture.
+
+## CMOS Battery Voltage
+
+/diagnostics/voltage_monitor: CMOS battey voltage
+
+[summary]
+
+| level | message |
+| ----- | ------------ |
+| OK | OK |
+| WARN | Low Battery |
+| WARN | Battery Died |
+
+[values]
+
+| key | value (example) |
+| ------------------- | --------------- |
+| CMOS battey voltage | 3.06 |
diff --git a/system/system_monitor/include/system_monitor/voltage_monitor/voltage_monitor.hpp b/system/system_monitor/include/system_monitor/voltage_monitor/voltage_monitor.hpp
new file mode 100644
index 0000000000000..eca6de2fcc834
--- /dev/null
+++ b/system/system_monitor/include/system_monitor/voltage_monitor/voltage_monitor.hpp
@@ -0,0 +1,72 @@
+// Copyright 2022 Tier IV, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @file voltage_monitor.h
+ * @brief voltage monitor class
+ */
+
+#ifndef SYSTEM_MONITOR__VOLTAGE_MONITOR__VOLTAGE_MONITOR_HPP_
+#define SYSTEM_MONITOR__VOLTAGE_MONITOR__VOLTAGE_MONITOR_HPP_
+
+#include
+
+#include
+#include
+#include
+class VoltageMonitor : public rclcpp::Node
+{
+public:
+ /**
+ * @brief constructor
+ * @param [in] options Options associated with this node.
+ */
+ explicit VoltageMonitor(const rclcpp::NodeOptions & options);
+
+ /**
+ * @brief Update the diagnostic state.
+ */
+ void update();
+
+protected:
+ using DiagStatus = diagnostic_msgs::msg::DiagnosticStatus;
+
+ diagnostic_updater::Updater updater_; //!< @brief Updater class which advertises to /diagnostics
+
+ char hostname_[HOST_NAME_MAX + 1]; //!< @brief host name
+
+ /**
+ * @brief check CMOS Battey
+ * @param [out] stat diagnostic message passed directly to diagnostic publish calls
+ * @note NOLINT syntax is needed since diagnostic_updater asks for a non-const reference
+ * to pass diagnostic message updated in this function to diagnostic publish calls.
+ */
+ void checkVoltage(
+ diagnostic_updater::DiagnosticStatusWrapper & stat); // NOLINT(runtime/references)
+ /**
+ * @brief check CMOS Battey
+ * @param [out] stat diagnostic message passed directly to diagnostic publish calls
+ * @note NOLINT syntax is needed since diagnostic_updater asks for a non-const reference
+ * to pass diagnostic message updated in this function to diagnostic publish calls.
+ */
+ void checkBatteryStatus(
+ diagnostic_updater::DiagnosticStatusWrapper & stat); // NOLINT(runtime/references)
+
+ float voltage_warn_;
+ float voltage_error_;
+ std::string voltage_string_;
+ std::regex voltage_regex_;
+};
+
+#endif // SYSTEM_MONITOR__VOLTAGE_MONITOR__VOLTAGE_MONITOR_HPP_
diff --git a/system/system_monitor/launch/system_monitor.launch.py b/system/system_monitor/launch/system_monitor.launch.py
index 463fdb6bab0a8..c4168e05e508c 100644
--- a/system/system_monitor/launch/system_monitor.launch.py
+++ b/system/system_monitor/launch/system_monitor.launch.py
@@ -96,6 +96,16 @@ def launch_setup(context, *args, **kwargs):
gpu_monitor_config,
],
)
+ with open(LaunchConfiguration("voltage_monitor_config_file").perform(context), "r") as f:
+ voltage_monitor_config = yaml.safe_load(f)["/**"]["ros__parameters"]
+ voltage_monitor = ComposableNode(
+ package="system_monitor",
+ plugin="VoltageMonitor",
+ name="voltage_monitor",
+ parameters=[
+ voltage_monitor_config,
+ ],
+ )
# set container to run all required components in the same process
container = ComposableNodeContainer(
@@ -111,6 +121,7 @@ def launch_setup(context, *args, **kwargs):
ntp_monitor,
process_monitor,
gpu_monitor,
+ voltage_monitor,
],
output="screen",
)
@@ -151,6 +162,10 @@ def generate_launch_description():
"gpu_monitor_config_file",
default_value=os.path.join(system_monitor_path, "gpu_monitor.param.yaml"),
),
+ DeclareLaunchArgument(
+ "voltage_monitor_config_file",
+ default_value=os.path.join(system_monitor_path, "voltage_monitor.param.yaml"),
+ ),
OpaqueFunction(function=launch_setup),
]
)
diff --git a/system/system_monitor/launch/system_monitor.launch.xml b/system/system_monitor/launch/system_monitor.launch.xml
index 423b9f779fd1d..e7c55c24c4c79 100644
--- a/system/system_monitor/launch/system_monitor.launch.xml
+++ b/system/system_monitor/launch/system_monitor.launch.xml
@@ -6,6 +6,7 @@
+
@@ -29,5 +30,8 @@
+
+
+
diff --git a/system/system_monitor/src/voltage_monitor/voltage_monitor.cpp b/system/system_monitor/src/voltage_monitor/voltage_monitor.cpp
new file mode 100644
index 0000000000000..e0bb665330b91
--- /dev/null
+++ b/system/system_monitor/src/voltage_monitor/voltage_monitor.cpp
@@ -0,0 +1,206 @@
+// Copyright 2022 Autoware Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @file _voltage_monitor.cpp
+ * @brief voltage monitor class
+ */
+
+#include "system_monitor/voltage_monitor/voltage_monitor.hpp"
+
+#include "system_monitor/system_monitor_utility.hpp"
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+namespace bp = boost::process;
+
+VoltageMonitor::VoltageMonitor(const rclcpp::NodeOptions & options)
+: Node("voltage_monitor", options), updater_(this), hostname_()
+{
+ gethostname(hostname_, sizeof(hostname_));
+
+ updater_.setHardwareID(hostname_);
+ // Publisher
+ rclcpp::QoS durable_qos{1};
+ durable_qos.transient_local();
+
+ voltage_string_ = declare_parameter("cmos_battery_label", "");
+ voltage_warn_ = declare_parameter("cmos_battery_warn", 2.95);
+ voltage_error_ = declare_parameter("cmos_battery_error", 2.75);
+ bool sensors_exists = false;
+ if (voltage_string_ == "") {
+ sensors_exists = false;
+ } else {
+ // Check if command exists
+ fs::path p = bp::search_path("sensors");
+ sensors_exists = (p.empty()) ? false : true;
+ }
+ gethostname(hostname_, sizeof(hostname_));
+ auto callback = &VoltageMonitor::checkBatteryStatus;
+ if (sensors_exists) {
+ try {
+ std::regex re(R"((\d+).(\d+))");
+ voltage_regex_ = re;
+ } catch (std::regex_error & e) {
+ // never comes here.
+ RCLCPP_WARN(get_logger(), "std::regex_error %d", e.code());
+ return;
+ }
+ callback = &VoltageMonitor::checkVoltage;
+ }
+ updater_.add("CMOS Battery Status", this, callback);
+}
+
+void VoltageMonitor::checkVoltage(diagnostic_updater::DiagnosticStatusWrapper & stat)
+{
+ // Remember start time to measure elapsed time
+ const auto t_start = SystemMonitorUtility::startMeasurement();
+ float voltage = 0.0;
+
+ int out_fd[2];
+ if (RCUTILS_UNLIKELY(pipe2(out_fd, O_CLOEXEC) != 0)) {
+ stat.summary(DiagStatus::ERROR, "pipe2 error");
+ stat.add("pipe2", strerror(errno));
+ return;
+ }
+ bp::pipe out_pipe{out_fd[0], out_fd[1]};
+ bp::ipstream is_out{std::move(out_pipe)};
+
+ int err_fd[2];
+ if (RCUTILS_UNLIKELY(pipe2(err_fd, O_CLOEXEC) != 0)) {
+ stat.summary(DiagStatus::ERROR, "pipe2 error");
+ stat.add("pipe2", strerror(errno));
+ return;
+ }
+ bp::pipe err_pipe{err_fd[0], err_fd[1]};
+ bp::ipstream is_err{std::move(err_pipe)};
+
+ bp::child c("sensors", bp::std_out > is_out, bp::std_err > is_err);
+ c.wait();
+
+ if (RCUTILS_UNLIKELY(c.exit_code() != 0)) { // failed to execute sensors
+ std::ostringstream os;
+ is_err >> os.rdbuf();
+ stat.summary(DiagStatus::ERROR, "sensors error");
+ stat.add("sensors", os.str().c_str());
+ return;
+ }
+ std::string line;
+ while (std::getline(is_out, line)) {
+ auto voltageStringPos = line.find(voltage_string_.c_str());
+ if (voltageStringPos != std::string::npos) {
+ try {
+ std::smatch match;
+ std::regex_search(line, match, voltage_regex_);
+ auto voltageString = match.str();
+ voltage = std::stof(voltageString);
+ } catch (std::regex_error & e) {
+ stat.summary(DiagStatus::WARN, "format error");
+ stat.add("exception in std::regex_search ", fmt::format("{}", e.code()));
+ return;
+ }
+ break;
+ }
+ }
+ stat.add("CMOS battey voltage", fmt::format("{}", voltage));
+ if (voltage < voltage_error_) {
+ stat.summary(DiagStatus::WARN, "Battery Died");
+ } else if (voltage < voltage_warn_) {
+ stat.summary(DiagStatus::WARN, "Low Battery");
+ } else {
+ stat.summary(DiagStatus::OK, "OK");
+ }
+
+ // Measure elapsed time since start time and report
+ SystemMonitorUtility::stopMeasurement(t_start, stat);
+}
+
+void VoltageMonitor::checkBatteryStatus(diagnostic_updater::DiagnosticStatusWrapper & stat)
+{
+ // Remember start time to measure elapsed time
+ const auto t_start = SystemMonitorUtility::startMeasurement();
+
+ // Get status of RTC
+ int out_fd[2];
+ if (RCUTILS_UNLIKELY(pipe2(out_fd, O_CLOEXEC) != 0)) {
+ stat.summary(DiagStatus::ERROR, "pipe2 error");
+ stat.add("pipe2", strerror(errno));
+ return;
+ }
+ bp::pipe out_pipe{out_fd[0], out_fd[1]};
+ bp::ipstream is_out{std::move(out_pipe)};
+
+ int err_fd[2];
+ if (RCUTILS_UNLIKELY(pipe2(err_fd, O_CLOEXEC) != 0)) {
+ stat.summary(DiagStatus::ERROR, "pipe2 error");
+ stat.add("pipe2", strerror(errno));
+ return;
+ }
+ bp::pipe err_pipe{err_fd[0], err_fd[1]};
+ bp::ipstream is_err{std::move(err_pipe)};
+
+ bp::child c("cat /proc/driver/rtc", bp::std_out > is_out, bp::std_err > is_err);
+ c.wait();
+
+ if (RCUTILS_UNLIKELY(c.exit_code() != 0)) {
+ std::ostringstream os;
+ is_err >> os.rdbuf();
+ stat.summary(DiagStatus::ERROR, "rtc error");
+ stat.add("rtc", os.str().c_str());
+ return;
+ }
+
+ std::string line;
+ bool status = false;
+ while (std::getline(is_out, line)) {
+ auto batStatusLine = line.find("batt_status");
+ if (batStatusLine != std::string::npos) {
+ auto batStatus = line.find("okay");
+ if (batStatus != std::string::npos) {
+ status = true;
+ break;
+ }
+ }
+ }
+
+ if (status) {
+ stat.add("CMOS battey status", std::string("OK"));
+ stat.summary(DiagStatus::OK, "OK");
+ } else {
+ stat.add("CMOS battey status", std::string("Battery Dead"));
+ stat.summary(DiagStatus::WARN, "Battery Dead");
+ }
+
+ // Measure elapsed time since start time and report
+ SystemMonitorUtility::stopMeasurement(t_start, stat);
+}
+
+void VoltageMonitor::update() { updater_.force_update(); }
+
+#include
+RCLCPP_COMPONENTS_REGISTER_NODE(VoltageMonitor)