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