blob: cc019fde6666c42137bd8b4119d0ef0b8bc23df0 [file] [log] [blame]
Tushar Pankaj47313c12024-03-16 17:03:53 -07001#include <dirent.h>
2#include <sys/statvfs.h>
3
Austin Schuh99f7c6a2024-06-25 22:07:44 -07004#include "absl/flags/flag.h"
Tushar Pankaj47313c12024-03-16 17:03:53 -07005#include "absl/strings/numbers.h"
6#include "absl/strings/str_format.h"
Tushar Pankaj47313c12024-03-16 17:03:53 -07007
8#include "aos/events/shm_event_loop.h"
9#include "aos/init.h"
10#include "frc971/orin/hardware_stats_generated.h"
11
Austin Schuh99f7c6a2024-06-25 22:07:44 -070012ABSL_FLAG(std::string, config, "aos_config.json",
13 "File path of aos configuration");
14ABSL_FLAG(bool, log_voltages, false, "If true, log voltages too.");
Tushar Pankaj47313c12024-03-16 17:03:53 -070015
16namespace frc971::orin {
17namespace {
18std::optional<std::string> ReadFileFirstLine(std::string_view file_name) {
19 std::ifstream file(std::string(file_name), std::ios_base::in);
20 if (!file.good()) {
21 VLOG(1) << "Can't read " << file_name;
22 return std::nullopt;
23 }
24 std::string line;
25 std::getline(file, line);
26 return line;
27}
28
29std::string GetHwmonNumber(const char *dir_name) {
30 DIR *dirp = opendir(dir_name);
31 if (!dirp) {
32 VLOG(1) << "Can't open " << dir_name;
33 return "";
34 }
35 struct dirent *directory_entry;
36 while ((directory_entry = readdir(dirp)) != NULL) {
37 std::string entry_name(directory_entry->d_name);
38 if (entry_name.starts_with("hwmon")) {
39 closedir(dirp);
40 return entry_name;
41 }
42 }
43 closedir(dirp);
44 return "";
45}
46} // namespace
47
48// Periodically sends out the HardwareStats message with hardware statistics
49// info.
50class HardwareMonitor {
51 public:
52 HardwareMonitor(aos::EventLoop *event_loop)
53 : event_loop_(event_loop),
54 sender_(event_loop_->MakeSender<HardwareStats>("/hardware_monitor")),
55 fan_hwmon_(
56 GetHwmonNumber("/sys/devices/platform/39c0000.tachometer/hwmon/")),
57 electrical_hwmon_(GetHwmonNumber(
58 "/sys/devices/platform/c240000.i2c/i2c-1/1-0040/hwmon/")) {
59 periodic_timer_ =
60 event_loop_->AddTimer([this]() { PublishHardwareStats(); });
61 event_loop_->OnRun([this]() {
62 periodic_timer_->Schedule(event_loop_->monotonic_now(),
63 std::chrono::seconds(5));
64 });
65 }
66
67 private:
68 void PublishHardwareStats() {
69 aos::Sender<HardwareStats>::Builder builder = sender_.MakeBuilder();
70 // Iterate through all thermal zones
71 std::vector<flatbuffers::Offset<ThermalZone>> thermal_zones;
72 for (int zone_id = 0; zone_id < 9; zone_id++) {
Austin Schuh7998b8f2024-03-22 19:51:47 -070073 std::optional<std::string> zone_name = ReadFileFirstLine(absl::StrFormat(
74 "/sys/devices/virtual/thermal/thermal_zone%d/type", zone_id));
75 flatbuffers::Offset<flatbuffers::String> name_offset;
76 if (zone_name) {
77 name_offset = builder.fbb()->CreateString(*zone_name);
78 }
79
Tushar Pankaj47313c12024-03-16 17:03:53 -070080 ThermalZone::Builder thermal_zone_builder =
81 builder.MakeBuilder<ThermalZone>();
82 thermal_zone_builder.add_id(zone_id);
83
Austin Schuh7998b8f2024-03-22 19:51:47 -070084 if (!name_offset.IsNull()) {
85 thermal_zone_builder.add_name(name_offset);
Tushar Pankaj47313c12024-03-16 17:03:53 -070086 }
87
88 std::optional<std::string> temperature_str =
89 ReadFileFirstLine(absl::StrFormat(
90 "/sys/devices/virtual/thermal/thermal_zone%d/temp", zone_id));
91 uint64_t temperature = 0;
92 if (temperature_str && absl::SimpleAtoi(*temperature_str, &temperature)) {
93 thermal_zone_builder.add_temperature(temperature);
94 }
95
96 thermal_zones.emplace_back(thermal_zone_builder.Finish());
97 }
98
99 // Get fan speed
100 std::optional<std::string> fan_speed_str = ReadFileFirstLine(
101 absl::StrFormat("/sys/class/hwmon/%s/rpm", fan_hwmon_));
102
Austin Schuh7998b8f2024-03-22 19:51:47 -0700103 flatbuffers::Offset<
104 flatbuffers::Vector<flatbuffers::Offset<ElectricalReading>>>
105 electrical_readings_offset;
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700106 if (absl::GetFlag(FLAGS_log_voltages)) {
Austin Schuh7998b8f2024-03-22 19:51:47 -0700107 std::vector<flatbuffers::Offset<ElectricalReading>> electrical_readings;
108 // Iterate through INA3221 electrical reading channels
109 for (int channel = 1; channel <= 3; channel++) {
110 std::optional<std::string> label = ReadFileFirstLine(absl::StrFormat(
111 "/sys/class/hwmon/%s/in%d_label", electrical_hwmon_, channel));
Tushar Pankaj47313c12024-03-16 17:03:53 -0700112
Austin Schuh7998b8f2024-03-22 19:51:47 -0700113 flatbuffers::Offset<flatbuffers::String> label_offset;
114 if (label) {
115 label_offset = builder.fbb()->CreateString(*label);
116 }
117
118 ElectricalReading::Builder electrical_reading_builder =
119 builder.MakeBuilder<ElectricalReading>();
120 electrical_reading_builder.add_channel(channel);
121
122 if (!label_offset.IsNull()) {
123 electrical_reading_builder.add_label(label_offset);
124 }
125
126 std::optional<std::string> voltage_str =
127 ReadFileFirstLine(absl::StrFormat("/sys/class/hwmon/%s/in%d_input",
128 electrical_hwmon_, channel));
129 uint64_t voltage = 0;
130 if (voltage_str && absl::SimpleAtoi(*voltage_str, &voltage)) {
131 electrical_reading_builder.add_voltage(voltage);
132 }
133
134 std::optional<std::string> current_str = ReadFileFirstLine(
135 absl::StrFormat("/sys/class/hwmon/%s/curr%d_input",
136 electrical_hwmon_, channel));
137 uint64_t current = 0;
138 if (current_str && absl::SimpleAtoi(*current_str, &current)) {
139 electrical_reading_builder.add_current(current);
140 }
141
142 uint64_t power = voltage * current / 1000;
143 if (power != 0) {
144 electrical_reading_builder.add_power(power);
145 }
146
147 electrical_readings.emplace_back(electrical_reading_builder.Finish());
Tushar Pankaj47313c12024-03-16 17:03:53 -0700148 }
Austin Schuh7998b8f2024-03-22 19:51:47 -0700149 electrical_readings_offset =
150 builder.fbb()->CreateVector(electrical_readings);
Tushar Pankaj47313c12024-03-16 17:03:53 -0700151 }
152
Austin Schuh7998b8f2024-03-22 19:51:47 -0700153 auto thermal_zone_offset = builder.fbb()->CreateVector(thermal_zones);
Tushar Pankaj47313c12024-03-16 17:03:53 -0700154 HardwareStats::Builder hardware_stats_builder =
155 builder.MakeBuilder<HardwareStats>();
Austin Schuh7998b8f2024-03-22 19:51:47 -0700156 hardware_stats_builder.add_thermal_zones(thermal_zone_offset);
Tushar Pankaj47313c12024-03-16 17:03:53 -0700157 uint64_t fan_speed = 0;
158 if (fan_speed_str && absl::SimpleAtoi(*fan_speed_str, &fan_speed)) {
159 hardware_stats_builder.add_fan_speed(fan_speed);
160 }
Austin Schuh7998b8f2024-03-22 19:51:47 -0700161 if (!electrical_readings_offset.IsNull()) {
162 hardware_stats_builder.add_electrical_readings(
163 electrical_readings_offset);
164 }
Tushar Pankaj47313c12024-03-16 17:03:53 -0700165
166 builder.CheckOk(builder.Send(hardware_stats_builder.Finish()));
167 }
168
169 aos::EventLoop *event_loop_;
170
171 aos::Sender<HardwareStats> sender_;
172
173 aos::TimerHandler *periodic_timer_;
174
175 std::string fan_hwmon_;
176
177 std::string electrical_hwmon_;
178};
179
180} // namespace frc971::orin
181
182int main(int argc, char **argv) {
183 aos::InitGoogle(&argc, &argv);
184
185 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700186 aos::configuration::ReadConfig(absl::GetFlag(FLAGS_config));
Tushar Pankaj47313c12024-03-16 17:03:53 -0700187
188 aos::ShmEventLoop shm_event_loop(&config.message());
189
190 frc971::orin::HardwareMonitor hardware_monitor(&shm_event_loop);
191
192 shm_event_loop.Run();
193
194 return 0;
195}