blob: a110fcefd141ecb8bc91ddc5dcaf464dc2bbea23 [file] [log] [blame]
Tyler Chatowe6f5bef2020-10-31 14:22:04 -07001#include <unistd.h>
2
Tyler Chatow5e369a42019-11-23 11:57:31 -08003#include <iostream>
4#include <map>
5
6#include "aos/configuration.h"
7#include "aos/events/shm_event_loop.h"
8#include "aos/init.h"
9#include "aos/json_to_flatbuffer.h"
10#include "gflags/gflags.h"
11
12DEFINE_string(config, "./config.json", "File path of aos configuration");
Austin Schuhd3936202020-04-07 20:11:07 -070013DEFINE_int32(max_vector_size, 100,
14 "If positive, vectors longer than this will not be printed");
Brian Silverman1bc2e962020-04-28 15:22:01 -070015DEFINE_bool(fetch, false,
16 "If true, fetch the current message on the channel first");
Ravago Jones5cc9df52020-09-02 21:29:58 -070017DEFINE_bool(pretty, false,
18 "If true, pretty print the messages on multiple lines");
Brian Silverman42402e52020-09-22 22:21:13 -070019DEFINE_bool(all, false,
20 "If true, print out the channels for all nodes, not just the "
21 "channels which are visible on this node.");
Austin Schuh594eef82020-09-10 18:43:01 -070022DEFINE_bool(print_timestamps, true, "If true, timestamps are printed.");
23DEFINE_uint64(count, 0,
24 "If >0, aos_dump will exit after printing this many messages.");
Austin Schuh01d88fc2020-09-13 18:48:16 -070025DEFINE_int32(rate_limit, 0,
26 "The minimum amount of time to wait in milliseconds before "
27 "sending another message");
Tyler Chatowe6f5bef2020-10-31 14:22:04 -070028DEFINE_bool(
29 _bash_autocomplete, false,
30 "Internal use: Outputs channel list for use with autocomplete script.");
31DEFINE_string(_bash_autocomplete_word, "",
32 "Intenal use: Index of current word being autocompleted");
Brian Silverman1bc2e962020-04-28 15:22:01 -070033
34namespace {
35
Tyler Chatowfcf16f42020-07-26 12:41:36 -070036void PrintMessage(const aos::Channel *channel, const aos::Context &context,
37 aos::FastStringBuilder *builder) {
Brian Silverman1bc2e962020-04-28 15:22:01 -070038 // Print the flatbuffer out to stdout, both to remove the
39 // unnecessary cruft from glog and to allow the user to readily
40 // redirect just the logged output independent of any debugging
41 // information on stderr.
Tyler Chatowfcf16f42020-07-26 12:41:36 -070042
43 builder->Reset();
Ravago Jones5cc9df52020-09-02 21:29:58 -070044 aos::FlatbufferToJson(
45 builder, channel->schema(), static_cast<const uint8_t *>(context.data),
46 {FLAGS_pretty, static_cast<size_t>(FLAGS_max_vector_size)});
Tyler Chatowfcf16f42020-07-26 12:41:36 -070047
Austin Schuh594eef82020-09-10 18:43:01 -070048 if (FLAGS_print_timestamps) {
49 if (context.monotonic_remote_time != context.monotonic_event_time) {
50 std::cout << context.realtime_remote_time << " ("
51 << context.monotonic_remote_time << ") delivered "
52 << context.realtime_event_time << " ("
53 << context.monotonic_event_time << "): " << *builder << '\n';
54 } else {
55 std::cout << context.realtime_event_time << " ("
56 << context.monotonic_event_time << "): " << *builder << '\n';
57 }
Brian Silverman1bc2e962020-04-28 15:22:01 -070058 } else {
Austin Schuh594eef82020-09-10 18:43:01 -070059 std::cout << *builder << '\n';
Brian Silverman1bc2e962020-04-28 15:22:01 -070060 }
61}
62
Tyler Chatowe6f5bef2020-10-31 14:22:04 -070063// Generate eval command to populate autocomplete responses. Eval escapes spaces
64// so channels are paired with their types. If a complete channel name is found,
65// only autocompletes the type to avoid repeating arguments. Returns no
66// autocomplete suggestions if a channel and type is found with the current
67// arguments.
68void Autocomplete(const aos::Configuration *config_msg,
69 const aos::ShmEventLoop &event_loop,
70 std::string_view channel_name,
71 std::string_view message_type) {
72 const bool unique_match =
73 std::count_if(
74 config_msg->channels()->begin(), config_msg->channels()->end(),
75 [channel_name, message_type](const aos::Channel *channel) {
76 return channel->name()->string_view() == channel_name &&
77 channel->type()->string_view() == message_type;
78 }) == 1;
79
80 const bool editing_message = !channel_name.empty() && FLAGS__bash_autocomplete_word == message_type;
81 const bool editing_channel = !editing_message && FLAGS__bash_autocomplete_word == channel_name;
82
83 std::cout << "COMPREPLY=(";
84
85 // If we have a unique match, don't provide any suggestions. Otherwise, check
86 // that were're editing one of the two positional arguments.
87 if (!unique_match && (editing_message || editing_channel)) {
88 for (const aos::Channel *channel : *config_msg->channels()) {
89 if (FLAGS_all || aos::configuration::ChannelIsReadableOnNode(
90 channel, event_loop.node())) {
91 // Suggest only message types if the message type argument is being
92 // entered.
93 if (editing_message) {
94 // Then, filter for only channel names that match exactly and types
95 // that begin with message_type.
96 if (channel->name()->string_view() == channel_name &&
97 channel->type()->string_view().find(message_type) == 0) {
98 std::cout << '\'' << channel->type()->c_str() << "' ";
99 }
100 } else if (channel->name()->string_view().find(channel_name) == 0) {
101 // If the message type empty, then return full autocomplete.
102 // Otherwise, since the message type is poulated yet not being edited,
103 // the user must be editing the channel name alone, in which case only
104 // suggest channel names, not pairs.
105 if (message_type.empty()) {
106 std::cout << '\'' << channel->name()->c_str() << ' '
107 << channel->type()->c_str() << "' ";
108 } else {
109 std::cout << '\'' << channel->name()->c_str() << "' ";
110 }
111 }
112 }
113 }
114 }
115 std::cout << ')';
116}
117
118bool EndsWith(std::string_view str, std::string_view ending) {
119 const std::size_t offset = str.size() - ending.size();
120 return str.size() >= ending.size() && std::equal(str.begin() + offset, str.end(),
121 ending.begin(), ending.end());
122}
123
Brian Silverman1bc2e962020-04-28 15:22:01 -0700124} // namespace
Austin Schuh15649d62019-12-28 16:36:38 -0800125
Tyler Chatow5e369a42019-11-23 11:57:31 -0800126int main(int argc, char **argv) {
Tyler Chatowe6f5bef2020-10-31 14:22:04 -0700127 gflags::SetUsageMessage(
128 "Prints messages from arbitrary channels as they are received given a "
129 "configuration file describing the channels to listen on.\nTypical "
130 "Usage: aos_dump [--config path_to_config.json] channel_name "
131 "message_type\nExample Usage: aos_dump --config pingpong_config.json "
132 "/test aos.examples.Ping");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800133 aos::InitGoogle(&argc, &argv);
134
Tyler Chatowe6f5bef2020-10-31 14:22:04 -0700135 // Don't generate failure output if the config doesn't exist while attempting
136 // to autocomplete.
137 if (struct stat file_stat;
138 FLAGS__bash_autocomplete &&
139 (!(EndsWith(FLAGS_config, ".json") || EndsWith(FLAGS_config, ".bfbs")) ||
140 stat(FLAGS_config.c_str(), &file_stat) != 0 ||
141 (file_stat.st_mode & S_IFMT) != S_IFREG)) {
142 std::cout << "COMPREPLY=()";
143 return 0;
144 }
145
Tyler Chatow5e369a42019-11-23 11:57:31 -0800146 std::string channel_name;
147 std::string message_type;
148 if (argc > 1) {
149 channel_name = argv[1];
150 }
151 if (argc > 2) {
152 message_type = argv[2];
153 }
154
155 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
156 aos::configuration::ReadConfig(FLAGS_config);
157
158 const aos::Configuration *config_msg = &config.message();
Austin Schuh594eef82020-09-10 18:43:01 -0700159 aos::ShmEventLoop event_loop(config_msg);
James Kuszmaul5c22e082019-12-14 20:43:07 -0800160 event_loop.SkipTimingReport();
Tyler Chatow67ddb032020-01-12 14:30:04 -0800161 event_loop.SkipAosLog();
Tyler Chatow5e369a42019-11-23 11:57:31 -0800162
Tyler Chatowe6f5bef2020-10-31 14:22:04 -0700163 if (FLAGS__bash_autocomplete) {
164 Autocomplete(config_msg, event_loop, channel_name, message_type);
165 return 0;
166 }
167
Tyler Chatow5e369a42019-11-23 11:57:31 -0800168 if (argc == 1) {
169 std::cout << "Channels:\n";
170 for (const aos::Channel *channel : *config_msg->channels()) {
Brian Silverman42402e52020-09-22 22:21:13 -0700171 if (FLAGS_all || aos::configuration::ChannelIsReadableOnNode(
172 channel, event_loop.node())) {
173 std::cout << channel->name()->c_str() << ' ' << channel->type()->c_str()
174 << '\n';
175 }
Tyler Chatow5e369a42019-11-23 11:57:31 -0800176 }
177 return 0;
178 }
179
Brian Silverman83ff9c12020-06-23 16:20:27 -0700180 std::vector<const aos::Channel *> found_channels;
Tyler Chatow5e369a42019-11-23 11:57:31 -0800181 const flatbuffers::Vector<flatbuffers::Offset<aos::Channel>> *channels =
182 config_msg->channels();
Brian Silverman83ff9c12020-06-23 16:20:27 -0700183 bool found_exact = false;
Tyler Chatow5e369a42019-11-23 11:57:31 -0800184 for (const aos::Channel *channel : *channels) {
Brian Silverman42402e52020-09-22 22:21:13 -0700185 if (!FLAGS_all && !aos::configuration::ChannelIsReadableOnNode(
186 channel, event_loop.node())) {
187 continue;
188 }
Brian Silverman83ff9c12020-06-23 16:20:27 -0700189 if (channel->name()->c_str() != channel_name) {
190 continue;
Tyler Chatow5e369a42019-11-23 11:57:31 -0800191 }
Brian Silverman83ff9c12020-06-23 16:20:27 -0700192 if (channel->type()->string_view() == message_type) {
193 if (!found_exact) {
194 found_channels.clear();
195 found_exact = true;
196 }
197 } else if (!found_exact && channel->type()->string_view().find(
198 message_type) != std::string_view::npos) {
199 } else {
200 continue;
201 }
202 found_channels.push_back(channel);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800203 }
204
Brian Silverman83ff9c12020-06-23 16:20:27 -0700205 if (found_channels.empty()) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800206 LOG(FATAL) << "Could not find any channels with the given name and type.";
Brian Silverman83ff9c12020-06-23 16:20:27 -0700207 } else if (found_channels.size() > 1 && !message_type.empty()) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800208 LOG(FATAL) << "Multiple channels found with same type";
209 }
210
Austin Schuh594eef82020-09-10 18:43:01 -0700211 uint64_t message_count = 0;
212
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700213 aos::FastStringBuilder str_builder;
214
Austin Schuh01d88fc2020-09-13 18:48:16 -0700215 aos::monotonic_clock::time_point next_send_time =
216 aos::monotonic_clock::min_time;
Brian Silverman83ff9c12020-06-23 16:20:27 -0700217 for (const aos::Channel *channel : found_channels) {
218 if (FLAGS_fetch) {
219 const std::unique_ptr<aos::RawFetcher> fetcher =
220 event_loop.MakeRawFetcher(channel);
221 if (fetcher->Fetch()) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700222 PrintMessage(channel, fetcher->context(), &str_builder);
Austin Schuh594eef82020-09-10 18:43:01 -0700223 ++message_count;
Brian Silverman83ff9c12020-06-23 16:20:27 -0700224 }
225 }
226
Austin Schuh594eef82020-09-10 18:43:01 -0700227 if (FLAGS_count > 0 && message_count >= FLAGS_count) {
228 return 0;
229 }
230
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700231 event_loop.MakeRawWatcher(
Austin Schuh01d88fc2020-09-13 18:48:16 -0700232 channel,
233 [channel, &str_builder, &event_loop, &message_count, &next_send_time](
234 const aos::Context &context, const void * /*message*/) {
235 if (context.monotonic_event_time > next_send_time) {
236 PrintMessage(channel, context, &str_builder);
237 next_send_time = context.monotonic_event_time +
238 std::chrono::milliseconds(FLAGS_rate_limit);
239 if (FLAGS_count > 0 && message_count >= FLAGS_count) {
240 event_loop.Exit();
241 }
Austin Schuh594eef82020-09-10 18:43:01 -0700242 }
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700243 });
Brian Silverman83ff9c12020-06-23 16:20:27 -0700244 }
245
Tyler Chatow5e369a42019-11-23 11:57:31 -0800246 event_loop.Run();
Austin Schuhae87e312020-08-01 16:15:01 -0700247
Tyler Chatow5e369a42019-11-23 11:57:31 -0800248 return 0;
249}