blob: 47e77036f89db2a1e365baef210e03820037360d [file] [log] [blame]
James Kuszmaul14d7ea12023-12-09 15:41:14 -08001#include <optional>
2
3#include "gflags/gflags.h"
4
5#include "aos/events/logging/log_reader.h"
6#include "aos/init.h"
7#include "aos/util/simulation_logger.h"
8#include "frc971/input/joystick_state_generated.h"
9
10DEFINE_string(output_folder, "/tmp/trimmed/",
11 "Name of the folder to write the trimmed log to.");
James Kuszmaulee525992024-03-15 21:01:16 -070012DEFINE_string(node, "roborio", "");
James Kuszmaul14d7ea12023-12-09 15:41:14 -080013DEFINE_double(pre_enable_time_sec, 10.0,
14 "Amount of time to leave in the new log before the first enable "
15 "signal happens.");
16DEFINE_double(post_enable_time_sec, 1.0,
17 "Amount of time to leave in the new log after the final enable "
18 "signal ends.");
James Kuszmaulee525992024-03-15 21:01:16 -070019DEFINE_double(force_start_monotonic, -1.0,
20 "If set, time, in seconds, at which to forcibly trim the start "
21 "of the log.");
22DEFINE_double(
23 force_end_monotonic, -1.0,
24 "If set, time, in seconds, at which to forcibly trim the end of the log.");
James Kuszmaul14d7ea12023-12-09 15:41:14 -080025
26int main(int argc, char *argv[]) {
27 gflags::SetUsageMessage(
28 "Trims the sections at the start/end of a log where the robot is "
29 "disabled.");
30 aos::InitGoogle(&argc, &argv);
31 const std::vector<aos::logger::LogFile> logfiles =
32 aos::logger::SortParts(aos::logger::FindLogs(argc, argv));
33 std::optional<aos::monotonic_clock::time_point> start_time;
34 std::optional<aos::monotonic_clock::time_point> end_time;
35 bool printed_match = false;
James Kuszmaulee525992024-03-15 21:01:16 -070036 bool force_time_range = FLAGS_force_start_monotonic > 0;
James Kuszmaul14d7ea12023-12-09 15:41:14 -080037 // We need to do two passes through the logfile; one to figure out when the
38 // start/end times are, one to actually do the trimming.
James Kuszmaulee525992024-03-15 21:01:16 -070039 if (!force_time_range) {
James Kuszmaul14d7ea12023-12-09 15:41:14 -080040 aos::logger::LogReader reader(logfiles);
41 const aos::Node *roborio =
James Kuszmaulee525992024-03-15 21:01:16 -070042 aos::configuration::GetNode(reader.configuration(), FLAGS_node);
James Kuszmaul14d7ea12023-12-09 15:41:14 -080043 reader.Register();
44 std::unique_ptr<aos::EventLoop> event_loop =
James Kuszmaulee525992024-03-15 21:01:16 -070045 reader.event_loop_factory()->MakeEventLoop(FLAGS_node, roborio);
James Kuszmaul14d7ea12023-12-09 15:41:14 -080046 event_loop->MakeWatcher(
47 "/aos", [&start_time, &end_time, &printed_match,
48 &event_loop](const aos::JoystickState &msg) {
49 if (!printed_match && msg.match_type() != aos::MatchType::kNone) {
50 LOG(INFO) << "Match Type: "
51 << aos::EnumNameMatchType(msg.match_type());
52 LOG(INFO) << "Match #: " << msg.match_number();
53 printed_match = true;
54 }
55
56 if (msg.enabled()) {
57 // Note that time is monotonic, so we don't need to e.g. do min's or
58 // max's on the start/end time.
59 if (!start_time.has_value()) {
60 start_time = event_loop->context().monotonic_event_time;
61 }
62 end_time = event_loop->context().monotonic_event_time;
63 }
64 });
65
66 reader.event_loop_factory()->Run();
67
68 if (!printed_match) {
69 LOG(INFO) << "No match info.";
70 }
James Kuszmaulee525992024-03-15 21:01:16 -070071 if (!start_time.has_value()) {
72 LOG(WARNING) << "Log does not ontain any JoystickState messages.";
73 return 1;
74 }
75 LOG(INFO) << "First enable at " << start_time.value();
76 LOG(INFO) << "Final enable at " << end_time.value();
77 start_time.value() -= std::chrono::duration_cast<std::chrono::nanoseconds>(
78 std::chrono::duration<double>(FLAGS_pre_enable_time_sec));
79 end_time.value() += std::chrono::duration_cast<std::chrono::nanoseconds>(
80 std::chrono::duration<double>(FLAGS_post_enable_time_sec));
81 } else {
82 CHECK_LT(FLAGS_force_start_monotonic, FLAGS_force_end_monotonic);
83 start_time = aos::monotonic_clock::time_point(
84 std::chrono::duration_cast<std::chrono::nanoseconds>(
85 std::chrono::duration<double>(FLAGS_force_start_monotonic)));
86 end_time = aos::monotonic_clock::time_point(
87 std::chrono::duration_cast<std::chrono::nanoseconds>(
88 std::chrono::duration<double>(FLAGS_force_end_monotonic)));
James Kuszmaul14d7ea12023-12-09 15:41:14 -080089 }
James Kuszmaul14d7ea12023-12-09 15:41:14 -080090
91 {
92 aos::logger::LogReader reader(logfiles);
93 const aos::Node *roborio =
James Kuszmaulee525992024-03-15 21:01:16 -070094 aos::configuration::GetNode(reader.configuration(), FLAGS_node);
James Kuszmaul14d7ea12023-12-09 15:41:14 -080095 reader.Register();
96 std::unique_ptr<aos::EventLoop> event_loop =
James Kuszmaulee525992024-03-15 21:01:16 -070097 reader.event_loop_factory()->MakeEventLoop(FLAGS_node, roborio);
James Kuszmaul14d7ea12023-12-09 15:41:14 -080098 auto exit_timer = event_loop->AddTimer(
99 [&reader]() { reader.event_loop_factory()->Exit(); });
100 exit_timer->Schedule(start_time.value());
101 reader.event_loop_factory()->Run();
102 const std::set<std::string> logger_nodes =
103 aos::logger::LoggerNodes(logfiles);
104 // Only start up loggers that generated the original set of logfiles.
105 // This mostly exists to make it so that utilities like log_to_mcap can
106 // easily auto-detect which node to replay as when consuming the input logs.
107 auto loggers = aos::util::MakeLoggersForNodes(
108 reader.event_loop_factory(), {logger_nodes.begin(), logger_nodes.end()},
109 FLAGS_output_folder);
110 exit_timer->Schedule(end_time.value());
111
112 reader.event_loop_factory()->Run();
113 }
114
115 LOG(INFO) << "Trimmed logs written to " << FLAGS_output_folder;
116
117 return EXIT_SUCCESS;
118}