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" 90 "rosmon also obeys some environment variables:\n" 91 " ROSMON_COLOR_MODE Can be set to 'truecolor', '256colors', 'ansi'\n" 92 " to force a specific color mode\n" 93 " If unset, rosmon tries to detect the best\n" 94 " available color mode.\n" 103 void logToStdout(
const std::string& channel,
const std::string& str)
105 std::string
clean = str;
106 unsigned int len = clean.length();
107 while(len != 0 && (clean[len-1] ==
'\n' || clean[len-1] ==
'\r'))
112 fmt::print(
"{:>20}: {}\n", channel, clean);
120 {
"disable-ui", no_argument,
nullptr,
'd'},
121 {
"benchmark", no_argument,
nullptr,
'b'},
122 {
"flush-log", no_argument,
nullptr,
'f'},
123 {
"flush-stdout", no_argument,
nullptr,
'F'},
124 {
"help", no_argument,
nullptr,
'h'},
125 {
"list-args", no_argument,
nullptr,
'L'},
126 {
"log", required_argument,
nullptr,
'l'},
127 {
"name", required_argument,
nullptr,
'n'},
128 {
"no-start", no_argument,
nullptr,
'S'},
129 {
"stop-timeout", required_argument,
nullptr,
's'},
130 {
"disable-diagnostics", no_argument,
nullptr,
'D'},
131 {
"cpu-limit", required_argument,
nullptr,
'c'},
132 {
"memory-limit", required_argument,
nullptr,
'm'},
133 {
"diagnostics-prefix", required_argument,
nullptr,
'p'},
134 {
nullptr, 0,
nullptr, 0}
143 int main(
int argc,
char** argv)
147 std::string launchFilePath;
150 bool enableUI =
true;
151 bool flushLog =
false;
152 bool startNodes =
true;
156 bool disableDiagnostics =
false;
157 std::string diagnosticsPrefix;
163 int c = getopt_long(argc, argv,
"h",
OPTIONS, &option_index);
200 stopTimeout = boost::lexical_cast<
double>(optarg);
202 catch(boost::bad_lexical_cast&)
204 fmt::print(stderr,
"Bad value for --stop-timeout argument: '{}'\n", optarg);
210 fmt::print(stderr,
"Stop timeout cannot be negative\n");
215 disableDiagnostics =
true;
220 cpuLimit = boost::lexical_cast<
float>(optarg);
222 catch(boost::bad_lexical_cast&)
224 fmt::print(stderr,
"Bad value for --cpu-limit argument: '{}'\n", optarg);
230 fmt::print(stderr,
"CPU Limit cannot be negative\n");
240 fmt::print(stderr,
"Bad value for --memory-limit argument: '{}'\n", optarg);
246 fmt::print(stderr,
"Prefix : {}", optarg);
247 diagnosticsPrefix = std::string(optarg);
260 int firstArg = optind + 1;
261 for(; firstArg < argc; ++firstArg)
263 if(strstr(argv[firstArg],
":="))
270 if(firstArg - optind == 1)
272 launchFilePath = argv[optind];
274 else if(firstArg - optind == 2)
276 const char* packageName = argv[optind];
277 const char* fileName = argv[optind + 1];
279 std::string
package = rosmon::PackageRegistry::getPath(packageName);
282 fmt::print(stderr,
"Could not find path of package '{}'\n", packageName);
289 fmt::print(stderr,
"Could not find launch file '{}' in package '{}'\n",
290 fileName, packageName
295 launchFilePath = path.string();
304 boost::scoped_ptr<rosmon::Logger> logger;
307 setenv(
"ROSCONSOLE_FORMAT",
"[${function}]: ${message}", 0);
317 time_t t = time(
nullptr);
319 memset(¤tTime, 0,
sizeof(currentTime));
320 localtime_r(&t, ¤tTime);
323 strftime(buf,
sizeof(buf),
"/tmp/rosmon_%Y_%m_%d_%H_%M_%S.log", ¤tTime);
334 config->setDefaultStopTimeout(stopTimeout);
335 config->setDefaultCPULimit(cpuLimit);
336 config->setDefaultMemoryLimit(memoryLimit);
339 for(
int i = firstArg; i < argc; ++i)
341 char*
arg = strstr(argv[i],
":=");
345 fmt::print(stderr,
"You specified a non-argument after an argument\n");
349 char* name = argv[i];
353 char* value = arg + 2;
355 config->setArgument(name, value);
362 config->parse(launchFilePath, onlyArguments);
363 config->evaluateParameters();
367 fmt::print(stderr,
"Could not load launch file: {}\n", e.
what());
376 for(
const auto&
arg : config->arguments())
377 std::cout <<
arg.first << std::endl;
391 name = config->rosmonNodeName();
402 ros::init(dummyArgc, argv, name, init_options);
410 fmt::print(
"roscore is already running.\n");
414 fmt::print(
"roscore is not runnning.\n");
415 fmt::print(
"Waiting until it is up (abort with CTRL+C)...\n");
420 fmt::print(
"roscore is running now.\n");
434 if(config->nodes().empty())
436 fmt::print(
"No ROS nodes to be launched. Finished...\n");
445 boost::scoped_ptr<rosmon::UI> ui;
469 watcher->wait(waitDuration);
476 ui->log(
"[rosmon]",
"Shutting down...");
483 watcher->wait(waitDuration);
498 watcher->wait(waitDuration);
505 bool coredumpsAvailable = std::any_of(monitor.
nodes().begin(), monitor.
nodes().end(),
509 if(ui && coredumpsAvailable)
511 ui->log(
"[rosmon]",
"\n");
512 ui->log(
"[rosmon]",
"If you want to debug one of the crashed nodes, you can use the following commands");
513 for(
auto& node : monitor.
nodes())
515 if(node->coredumpAvailable())
519 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()
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()
ROSCPP_DECL std::string clean(const std::string &name)
std::shared_ptr< LaunchConfig > Ptr
boost::signals2::signal< void(std::string, std::string)> logMessageSignal
static fs::path findFile(const fs::path &base, const std::string &name)
static constexpr float DEFAULT_CPU_LIMIT
void logToStdout(const std::string &channel, const std::string &str)
Write log messages into a log file.
static constexpr uint64_t DEFAULT_MEMORY_LIMIT
void log(const std::string &source, const std::string &msg)
Log message.
static constexpr float DEFAULT_STOP_TIMEOUT
static const struct option OPTIONS[]
std::string arg(const std::string &name, const ParseContext &context)
std::tuple< uint64_t, bool > parseMemory(const std::string &memory)
ROSCPP_DECL void spinOnce()
std::shared_ptr< NodeMonitor > Ptr