12 #include <boost/filesystem.hpp> 13 #include <boost/lexical_cast.hpp> 21 #include <fmt/format.h> 32 namespace fs = boost::filesystem;
37 static fs::path
findFile(
const fs::path& base,
const std::string& name)
39 for(fs::directory_iterator it(base); it != fs::directory_iterator(); ++it)
41 if(fs::is_directory(*it))
47 else if(it->path().leaf() == name)
58 " rosmon [actions] [options] some_package test.launch [arg1:=value1 ...]\n" 59 " rosmon [actions] [options] path/to/test.launch [arg1:=value1 ...]\n" 61 "Actions (default is to launch the launch file):\n" 62 " --benchmark Exit after loading the launch file\n" 63 " --list-args List launch file arguments\n" 66 " --disable-ui Disable fancy terminal UI\n" 67 " --flush-log Flush logfile after writing an entry\n" 68 " --flush-stdout Flush stdout after writing an entry\n" 69 " --help This help screen\n" 70 " --log=FILE Write log file to FILE\n" 71 " --name=NAME Use NAME as ROS node name. By default, an anonymous\n" 73 " --no-start Don't automatically start the nodes in the beginning\n" 74 " --stop-timeout=SECONDS\n" 75 " Kill a process if it is still running this long\n" 76 " after the initial signal is send.\n" 77 " --disable-diagnostics\n" 78 " Disable publication of ros diagnostics message about\n" 80 " --diagnostics-prefix=PREFIX\n" 81 " Prefix for the ros diagnostics generated by this node.\n" 82 " By default this will be the node name.\n" 83 " --cpu-limit=[0-n]\n" 84 " Default CPU Usage limit of monitored process. n is the\n" 85 " number of CPU cores. This is the sum of system and user\n" 87 " --memory-limit=15MB\n" 88 " Default memory limit usage of monitored process.\n" 89 " --output-attr=obey|ignore\n" 90 " Obey or ignore output=\"*\" attributes on node tags.\n" 91 " Default is to ignore.\n" 93 "rosmon also obeys some environment variables:\n" 94 " ROSMON_COLOR_MODE Can be set to 'truecolor', '256colors', 'ansi'\n" 95 " to force a specific color mode\n" 96 " If unset, rosmon tries to detect the best\n" 97 " available color mode.\n" 108 fmt::print(
"{:>{}}: {}", event.
source, max_width, event.
message);
110 if(!event.
message.empty() &&
event.message.back() !=
'\n')
119 {
"disable-ui", no_argument,
nullptr,
'd'},
120 {
"benchmark", no_argument,
nullptr,
'b'},
121 {
"flush-log", no_argument,
nullptr,
'f'},
122 {
"flush-stdout", no_argument,
nullptr,
'F'},
123 {
"help", no_argument,
nullptr,
'h'},
124 {
"list-args", no_argument,
nullptr,
'L'},
125 {
"log", required_argument,
nullptr,
'l'},
126 {
"name", required_argument,
nullptr,
'n'},
127 {
"no-start", no_argument,
nullptr,
'S'},
128 {
"stop-timeout", required_argument,
nullptr,
's'},
129 {
"disable-diagnostics", no_argument,
nullptr,
'D'},
130 {
"cpu-limit", required_argument,
nullptr,
'c'},
131 {
"memory-limit", required_argument,
nullptr,
'm'},
132 {
"diagnostics-prefix", required_argument,
nullptr,
'p'},
133 {
"output-attr", required_argument,
nullptr,
'o'},
134 {
nullptr, 0,
nullptr, 0}
145 std::size_t max_width = 0;
147 for(
const auto& node : monitor.
nodes())
149 max_width = std::max(max_width, node->fullName().size());
155 int main(
int argc,
char** argv)
159 std::string launchFilePath;
162 bool enableUI =
true;
163 bool flushLog =
false;
164 bool startNodes =
true;
168 bool disableDiagnostics =
false;
169 std::string diagnosticsPrefix;
176 int c = getopt_long(argc, argv,
"h",
OPTIONS, &option_index);
213 stopTimeout = boost::lexical_cast<
double>(optarg);
215 catch(boost::bad_lexical_cast&)
217 fmt::print(stderr,
"Bad value for --stop-timeout argument: '{}'\n", optarg);
223 fmt::print(stderr,
"Stop timeout cannot be negative\n");
228 disableDiagnostics =
true;
233 cpuLimit = boost::lexical_cast<
float>(optarg);
235 catch(boost::bad_lexical_cast&)
237 fmt::print(stderr,
"Bad value for --cpu-limit argument: '{}'\n", optarg);
243 fmt::print(stderr,
"CPU Limit cannot be negative\n");
253 fmt::print(stderr,
"Bad value for --memory-limit argument: '{}'\n", optarg);
259 if(optarg == std::string(
"obey"))
261 else if(optarg == std::string(
"ignore"))
265 fmt::print(stderr,
"Bad value for --output-attr argument: '{}'\n", optarg);
270 fmt::print(stderr,
"Prefix : {}", optarg);
271 diagnosticsPrefix = std::string(optarg);
284 int firstArg = optind + 1;
285 for(; firstArg < argc; ++firstArg)
287 if(strstr(argv[firstArg],
":="))
294 if(firstArg - optind == 1)
296 launchFilePath = argv[optind];
298 else if(firstArg - optind == 2)
300 const char* packageName = argv[optind];
301 const char* fileName = argv[optind + 1];
303 std::string
package = rosmon::PackageRegistry::getPath(packageName);
306 fmt::print(stderr,
"Could not find path of package '{}'\n", packageName);
313 fmt::print(stderr,
"Could not find launch file '{}' in package '{}'\n",
314 fileName, packageName
319 launchFilePath = path.string();
328 boost::scoped_ptr<rosmon::Logger> logger;
331 setenv(
"ROSCONSOLE_FORMAT",
"[${function}]: ${message}", 0);
341 time_t t = time(
nullptr);
343 memset(¤tTime, 0,
sizeof(currentTime));
344 localtime_r(&t, ¤tTime);
347 strftime(buf,
sizeof(buf),
"/tmp/rosmon_%Y_%m_%d_%H_%M_%S.log", ¤tTime);
358 config->setDefaultStopTimeout(stopTimeout);
359 config->setDefaultCPULimit(cpuLimit);
360 config->setDefaultMemoryLimit(memoryLimit);
361 config->setOutputAttrMode(outputAttrMode);
364 for(
int i = firstArg; i < argc; ++i)
366 char*
arg = strstr(argv[i],
":=");
370 fmt::print(stderr,
"You specified a non-argument after an argument\n");
374 char* name = argv[i];
378 char* value = arg + 2;
380 config->setArgument(name, value);
387 config->parse(launchFilePath, onlyArguments);
388 config->evaluateParameters();
392 fmt::print(stderr,
"Could not load launch file: {}\n", e.
what());
401 for(
const auto&
arg : config->arguments())
402 std::cout <<
arg.first << std::endl;
409 if(config->disableUI())
419 name = config->rosmonNodeName();
430 ros::init(dummyArgc, argv, name, init_options);
438 fmt::print(
"roscore is already running.\n");
442 fmt::print(
"roscore is not runnning.\n");
443 fmt::print(
"Waiting until it is up (abort with CTRL+C)...\n");
448 fmt::print(
"roscore is running now.\n");
462 if(config->nodes().empty())
464 fmt::print(
"No ROS nodes to be launched. Finished...\n");
473 boost::scoped_ptr<rosmon::UI> ui;
501 watcher->wait(waitDuration);
508 ui->log({
"[rosmon]",
"Shutting down..."});
515 watcher->wait(waitDuration);
530 watcher->wait(waitDuration);
537 bool coredumpsAvailable = std::any_of(monitor.
nodes().begin(), monitor.
nodes().end(),
541 if(ui && coredumpsAvailable)
543 ui->log({
"[rosmon]",
"\n"});
544 ui->log({
"[rosmon]",
"If you want to debug one of the crashed nodes, you can use the following commands"});
545 for(
auto& node : monitor.
nodes())
547 if(node->coredumpAvailable())
551 fmt::format(
"{:20}: # {}", node->name(), node->debuggerCommand())
virtual const char * what() const noexcept
const std::vector< NodeMonitor::Ptr > & nodes() const
ROSCPP_DECL const std::string & getURI()
boost::signals2::signal< void(LogEvent)> logMessageSignal
void(* function_print)(void *,::ros::console::Level, const char *, const char *, const char *, int)
int main(int argc, char **argv)
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
ROSCPP_DECL const std::string & getName()
std::shared_ptr< LaunchConfig > Ptr
static fs::path findFile(const fs::path &base, const std::string &name)
Write log messages into a log file.
constexpr double DEFAULT_STOP_TIMEOUT
void log(const LogEvent &event)
Log message.
constexpr uint64_t DEFAULT_MEMORY_LIMIT
static const struct option OPTIONS[]
std::string arg(const std::string &name, const ParseContext &context)
constexpr double DEFAULT_CPU_LIMIT
static int get_max_node_name(const rosmon::monitor::Monitor &monitor)
std::tuple< uint64_t, bool > parseMemory(const std::string &memory)
ROSCPP_DECL void spinOnce()
void logToStdout(const rosmon::LogEvent &event, const int max_width)
std::shared_ptr< NodeMonitor > Ptr