blob: da2497adf5d28600915abbaf9e573355b3577295 [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, "",
Tyler Chatowb8fa2902020-10-31 14:58:58 -070032 "Intenal use: 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 =
Tyler Chatowb8fa2902020-10-31 14:58:58 -070073 std::count_if(config_msg->channels()->begin(),
74 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;
Tyler Chatowe6f5bef2020-10-31 14:22:04 -070079
Tyler Chatowb8fa2902020-10-31 14:58:58 -070080 const bool editing_message =
81 !channel_name.empty() && FLAGS__bash_autocomplete_word == message_type;
82 const bool editing_channel =
83 !editing_message && FLAGS__bash_autocomplete_word == channel_name;
Tyler Chatowe6f5bef2020-10-31 14:22:04 -070084
85 std::cout << "COMPREPLY=(";
86
87 // If we have a unique match, don't provide any suggestions. Otherwise, check
88 // that were're editing one of the two positional arguments.
89 if (!unique_match && (editing_message || editing_channel)) {
90 for (const aos::Channel *channel : *config_msg->channels()) {
91 if (FLAGS_all || aos::configuration::ChannelIsReadableOnNode(
92 channel, event_loop.node())) {
93 // Suggest only message types if the message type argument is being
94 // entered.
95 if (editing_message) {
96 // Then, filter for only channel names that match exactly and types
97 // that begin with message_type.
98 if (channel->name()->string_view() == channel_name &&
99 channel->type()->string_view().find(message_type) == 0) {
100 std::cout << '\'' << channel->type()->c_str() << "' ";
101 }
102 } else if (channel->name()->string_view().find(channel_name) == 0) {
103 // If the message type empty, then return full autocomplete.
104 // Otherwise, since the message type is poulated yet not being edited,
105 // the user must be editing the channel name alone, in which case only
106 // suggest channel names, not pairs.
107 if (message_type.empty()) {
108 std::cout << '\'' << channel->name()->c_str() << ' '
109 << channel->type()->c_str() << "' ";
110 } else {
111 std::cout << '\'' << channel->name()->c_str() << "' ";
112 }
113 }
114 }
115 }
116 }
117 std::cout << ')';
118}
119
120bool EndsWith(std::string_view str, std::string_view ending) {
121 const std::size_t offset = str.size() - ending.size();
Tyler Chatowb8fa2902020-10-31 14:58:58 -0700122 return str.size() >= ending.size() &&
123 std::equal(str.begin() + offset, str.end(), ending.begin(),
124 ending.end());
Tyler Chatowe6f5bef2020-10-31 14:22:04 -0700125}
126
Brian Silverman1bc2e962020-04-28 15:22:01 -0700127} // namespace
Austin Schuh15649d62019-12-28 16:36:38 -0800128
Tyler Chatow5e369a42019-11-23 11:57:31 -0800129int main(int argc, char **argv) {
Tyler Chatowe6f5bef2020-10-31 14:22:04 -0700130 gflags::SetUsageMessage(
131 "Prints messages from arbitrary channels as they are received given a "
132 "configuration file describing the channels to listen on.\nTypical "
133 "Usage: aos_dump [--config path_to_config.json] channel_name "
134 "message_type\nExample Usage: aos_dump --config pingpong_config.json "
135 "/test aos.examples.Ping");
Tyler Chatow5e369a42019-11-23 11:57:31 -0800136 aos::InitGoogle(&argc, &argv);
137
Tyler Chatowe6f5bef2020-10-31 14:22:04 -0700138 // Don't generate failure output if the config doesn't exist while attempting
139 // to autocomplete.
140 if (struct stat file_stat;
141 FLAGS__bash_autocomplete &&
142 (!(EndsWith(FLAGS_config, ".json") || EndsWith(FLAGS_config, ".bfbs")) ||
143 stat(FLAGS_config.c_str(), &file_stat) != 0 ||
144 (file_stat.st_mode & S_IFMT) != S_IFREG)) {
145 std::cout << "COMPREPLY=()";
146 return 0;
147 }
148
Tyler Chatow5e369a42019-11-23 11:57:31 -0800149 std::string channel_name;
150 std::string message_type;
151 if (argc > 1) {
152 channel_name = argv[1];
153 }
154 if (argc > 2) {
155 message_type = argv[2];
156 }
157
158 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
159 aos::configuration::ReadConfig(FLAGS_config);
160
161 const aos::Configuration *config_msg = &config.message();
Austin Schuh594eef82020-09-10 18:43:01 -0700162 aos::ShmEventLoop event_loop(config_msg);
James Kuszmaul5c22e082019-12-14 20:43:07 -0800163 event_loop.SkipTimingReport();
Tyler Chatow67ddb032020-01-12 14:30:04 -0800164 event_loop.SkipAosLog();
Tyler Chatow5e369a42019-11-23 11:57:31 -0800165
Tyler Chatowe6f5bef2020-10-31 14:22:04 -0700166 if (FLAGS__bash_autocomplete) {
167 Autocomplete(config_msg, event_loop, channel_name, message_type);
168 return 0;
169 }
170
Tyler Chatow5e369a42019-11-23 11:57:31 -0800171 if (argc == 1) {
172 std::cout << "Channels:\n";
173 for (const aos::Channel *channel : *config_msg->channels()) {
Brian Silverman42402e52020-09-22 22:21:13 -0700174 if (FLAGS_all || aos::configuration::ChannelIsReadableOnNode(
175 channel, event_loop.node())) {
176 std::cout << channel->name()->c_str() << ' ' << channel->type()->c_str()
177 << '\n';
178 }
Tyler Chatow5e369a42019-11-23 11:57:31 -0800179 }
180 return 0;
181 }
182
Brian Silverman83ff9c12020-06-23 16:20:27 -0700183 std::vector<const aos::Channel *> found_channels;
Tyler Chatow5e369a42019-11-23 11:57:31 -0800184 const flatbuffers::Vector<flatbuffers::Offset<aos::Channel>> *channels =
185 config_msg->channels();
Brian Silverman83ff9c12020-06-23 16:20:27 -0700186 bool found_exact = false;
Tyler Chatow5e369a42019-11-23 11:57:31 -0800187 for (const aos::Channel *channel : *channels) {
Brian Silverman42402e52020-09-22 22:21:13 -0700188 if (!FLAGS_all && !aos::configuration::ChannelIsReadableOnNode(
189 channel, event_loop.node())) {
190 continue;
191 }
Brian Silverman83ff9c12020-06-23 16:20:27 -0700192 if (channel->name()->c_str() != channel_name) {
193 continue;
Tyler Chatow5e369a42019-11-23 11:57:31 -0800194 }
Brian Silverman83ff9c12020-06-23 16:20:27 -0700195 if (channel->type()->string_view() == message_type) {
196 if (!found_exact) {
197 found_channels.clear();
198 found_exact = true;
199 }
200 } else if (!found_exact && channel->type()->string_view().find(
201 message_type) != std::string_view::npos) {
202 } else {
203 continue;
204 }
205 found_channels.push_back(channel);
Tyler Chatow5e369a42019-11-23 11:57:31 -0800206 }
207
Brian Silverman83ff9c12020-06-23 16:20:27 -0700208 if (found_channels.empty()) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800209 LOG(FATAL) << "Could not find any channels with the given name and type.";
Brian Silverman83ff9c12020-06-23 16:20:27 -0700210 } else if (found_channels.size() > 1 && !message_type.empty()) {
Tyler Chatow5e369a42019-11-23 11:57:31 -0800211 LOG(FATAL) << "Multiple channels found with same type";
212 }
213
Austin Schuh594eef82020-09-10 18:43:01 -0700214 uint64_t message_count = 0;
215
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700216 aos::FastStringBuilder str_builder;
217
Austin Schuh01d88fc2020-09-13 18:48:16 -0700218 aos::monotonic_clock::time_point next_send_time =
219 aos::monotonic_clock::min_time;
Brian Silverman83ff9c12020-06-23 16:20:27 -0700220 for (const aos::Channel *channel : found_channels) {
221 if (FLAGS_fetch) {
222 const std::unique_ptr<aos::RawFetcher> fetcher =
223 event_loop.MakeRawFetcher(channel);
224 if (fetcher->Fetch()) {
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700225 PrintMessage(channel, fetcher->context(), &str_builder);
Austin Schuh594eef82020-09-10 18:43:01 -0700226 ++message_count;
Brian Silverman83ff9c12020-06-23 16:20:27 -0700227 }
228 }
229
Austin Schuh594eef82020-09-10 18:43:01 -0700230 if (FLAGS_count > 0 && message_count >= FLAGS_count) {
231 return 0;
232 }
233
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700234 event_loop.MakeRawWatcher(
Austin Schuh01d88fc2020-09-13 18:48:16 -0700235 channel,
236 [channel, &str_builder, &event_loop, &message_count, &next_send_time](
237 const aos::Context &context, const void * /*message*/) {
238 if (context.monotonic_event_time > next_send_time) {
239 PrintMessage(channel, context, &str_builder);
240 next_send_time = context.monotonic_event_time +
241 std::chrono::milliseconds(FLAGS_rate_limit);
242 if (FLAGS_count > 0 && message_count >= FLAGS_count) {
243 event_loop.Exit();
244 }
Austin Schuh594eef82020-09-10 18:43:01 -0700245 }
Tyler Chatowfcf16f42020-07-26 12:41:36 -0700246 });
Brian Silverman83ff9c12020-06-23 16:20:27 -0700247 }
248
Tyler Chatow5e369a42019-11-23 11:57:31 -0800249 event_loop.Run();
Austin Schuhae87e312020-08-01 16:15:01 -0700250
Tyler Chatow5e369a42019-11-23 11:57:31 -0800251 return 0;
252}