blob: f09d0e65d7e80d2c488c57a7b27437a387fd9494 [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");
13
14namespace frc971::orin {
15namespace {
16std::optional<std::string> ReadFileFirstLine(std::string_view file_name) {
17 std::ifstream file(std::string(file_name), std::ios_base::in);
18 if (!file.good()) {
19 VLOG(1) << "Can't read " << file_name;
20 return std::nullopt;
21 }
22 std::string line;
23 std::getline(file, line);
24 return line;
25}
26
27std::string GetHwmonNumber(const char *dir_name) {
28 DIR *dirp = opendir(dir_name);
29 if (!dirp) {
30 VLOG(1) << "Can't open " << dir_name;
31 return "";
32 }
33 struct dirent *directory_entry;
34 while ((directory_entry = readdir(dirp)) != NULL) {
35 std::string entry_name(directory_entry->d_name);
36 if (entry_name.starts_with("hwmon")) {
37 closedir(dirp);
38 return entry_name;
39 }
40 }
41 closedir(dirp);
42 return "";
43}
44} // namespace
45
46// Periodically sends out the HardwareStats message with hardware statistics
47// info.
48class HardwareMonitor {
49 public:
50 HardwareMonitor(aos::EventLoop *event_loop)
51 : event_loop_(event_loop),
52 sender_(event_loop_->MakeSender<HardwareStats>("/hardware_monitor")),
53 fan_hwmon_(
54 GetHwmonNumber("/sys/devices/platform/39c0000.tachometer/hwmon/")),
55 electrical_hwmon_(GetHwmonNumber(
56 "/sys/devices/platform/c240000.i2c/i2c-1/1-0040/hwmon/")) {
57 periodic_timer_ =
58 event_loop_->AddTimer([this]() { PublishHardwareStats(); });
59 event_loop_->OnRun([this]() {
60 periodic_timer_->Schedule(event_loop_->monotonic_now(),
61 std::chrono::seconds(5));
62 });
63 }
64
65 private:
66 void PublishHardwareStats() {
67 aos::Sender<HardwareStats>::Builder builder = sender_.MakeBuilder();
68 // Iterate through all thermal zones
69 std::vector<flatbuffers::Offset<ThermalZone>> thermal_zones;
70 for (int zone_id = 0; zone_id < 9; zone_id++) {
71 ThermalZone::Builder thermal_zone_builder =
72 builder.MakeBuilder<ThermalZone>();
73 thermal_zone_builder.add_id(zone_id);
74
75 std::optional<std::string> zone_name = ReadFileFirstLine(absl::StrFormat(
76 "/sys/devices/virtual/thermal/thermal_zone%d/type", zone_id));
77 if (zone_name) {
78 thermal_zone_builder.add_name(builder.fbb()->CreateString(*zone_name));
79 }
80
81 std::optional<std::string> temperature_str =
82 ReadFileFirstLine(absl::StrFormat(
83 "/sys/devices/virtual/thermal/thermal_zone%d/temp", zone_id));
84 uint64_t temperature = 0;
85 if (temperature_str && absl::SimpleAtoi(*temperature_str, &temperature)) {
86 thermal_zone_builder.add_temperature(temperature);
87 }
88
89 thermal_zones.emplace_back(thermal_zone_builder.Finish());
90 }
91
92 // Get fan speed
93 std::optional<std::string> fan_speed_str = ReadFileFirstLine(
94 absl::StrFormat("/sys/class/hwmon/%s/rpm", fan_hwmon_));
95
96 // Iterate through INA3221 electrical reading channels
97 std::vector<flatbuffers::Offset<ElectricalReading>> electrical_readings;
98 for (int channel = 1; channel <= 3; channel++) {
99 ElectricalReading::Builder electrical_reading_builder =
100 builder.MakeBuilder<ElectricalReading>();
101 electrical_reading_builder.add_channel(channel);
102
103 std::optional<std::string> label = ReadFileFirstLine(absl::StrFormat(
104 "/sys/class/hwmon/%s/in%d_label", electrical_hwmon_, channel));
105 if (label) {
106 electrical_reading_builder.add_label(
107 builder.fbb()->CreateString(*label));
108 }
109
110 std::optional<std::string> voltage_str =
111 ReadFileFirstLine(absl::StrFormat("/sys/class/hwmon/%s/in%d_input",
112 electrical_hwmon_, channel));
113 uint64_t voltage = 0;
114 if (voltage_str && absl::SimpleAtoi(*voltage_str, &voltage)) {
115 electrical_reading_builder.add_voltage(voltage);
116 }
117
118 std::optional<std::string> current_str =
119 ReadFileFirstLine(absl::StrFormat("/sys/class/hwmon/%s/curr%d_input",
120 electrical_hwmon_, channel));
121 uint64_t current = 0;
122 if (current_str && absl::SimpleAtoi(*current_str, &current)) {
123 electrical_reading_builder.add_current(current);
124 }
125
126 uint64_t power = voltage * current / 1000;
127 if (power != 0) {
128 electrical_reading_builder.add_power(power);
129 }
130
131 electrical_readings.emplace_back(electrical_reading_builder.Finish());
132 }
133
134 HardwareStats::Builder hardware_stats_builder =
135 builder.MakeBuilder<HardwareStats>();
136 hardware_stats_builder.add_thermal_zones(
137 builder.fbb()->CreateVector(thermal_zones));
138 uint64_t fan_speed = 0;
139 if (fan_speed_str && absl::SimpleAtoi(*fan_speed_str, &fan_speed)) {
140 hardware_stats_builder.add_fan_speed(fan_speed);
141 }
142 hardware_stats_builder.add_electrical_readings(
143 builder.fbb()->CreateVector(electrical_readings));
144
145 builder.CheckOk(builder.Send(hardware_stats_builder.Finish()));
146 }
147
148 aos::EventLoop *event_loop_;
149
150 aos::Sender<HardwareStats> sender_;
151
152 aos::TimerHandler *periodic_timer_;
153
154 std::string fan_hwmon_;
155
156 std::string electrical_hwmon_;
157};
158
159} // namespace frc971::orin
160
161int main(int argc, char **argv) {
162 aos::InitGoogle(&argc, &argv);
163
164 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
165 aos::configuration::ReadConfig(FLAGS_config);
166
167 aos::ShmEventLoop shm_event_loop(&config.message());
168
169 frc971::orin::HardwareMonitor hardware_monitor(&shm_event_loop);
170
171 shm_event_loop.Run();
172
173 return 0;
174}