39 #include <QApplication>
42 #include <QListWidget>
43 #include <QMessageBox>
44 #include <QProgressBar>
45 #include <QPushButton>
46 #include <QPushButton>
47 #include <QRegularExpression>
49 #include <QVBoxLayout>
55 #include <boost/algorithm/string.hpp>
56 #include <boost/filesystem/path.hpp>
57 #include <boost/filesystem/operations.hpp>
65 namespace fs = boost::filesystem;
77 QVBoxLayout* layout =
new QVBoxLayout();
82 new HeaderWidget(
"Generate Configuration Files",
83 "Create or update the configuration files package needed to run your robot with MoveIt. Uncheck "
84 "files to disable them from being generated - this is useful if you have made custom changes to "
85 "them. Files in orange have been automatically detected as changed.",
87 layout->addWidget(header);
92 stack_path_ =
new LoadPathWidget(
"Configuration Package Save Path",
93 "Specify the desired directory for the MoveIt configuration package to be "
94 "generated. Overwriting an existing configuration package directory is acceptable. "
95 "Example: <i>/u/robot/ros/panda_moveit_config</i>",
97 layout->addWidget(stack_path_);
100 stack_path_->setPath(config_data_->config_pkg_path_);
103 QLabel* generated_list =
new QLabel(
"Check files you want to be generated:",
this);
104 layout->addWidget(generated_list);
106 QSplitter* splitter =
new QSplitter(Qt::Horizontal,
this);
107 splitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
110 action_list_ =
new QListWidget(
this);
111 action_list_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
112 action_list_->setSelectionMode(QAbstractItemView::ExtendedSelection);
113 connect(action_list_, SIGNAL(currentRowChanged(
int)),
this, SLOT(changeActionDesc(
int)));
115 action_list_->setContextMenuPolicy(Qt::ActionsContextMenu);
116 QAction* action =
new QAction(
"Check all selected files",
this);
117 connect(action, &QAction::triggered, [
this]() { setCheckSelected(
true); });
118 action_list_->addAction(action);
119 action =
new QAction(
"Uncheck all selected files",
this);
120 connect(action, &QAction::triggered, [
this]() { setCheckSelected(
false); });
121 action_list_->addAction(action);
124 action_label_ =
new QLabel(
this);
125 action_label_->setFrameShape(QFrame::StyledPanel);
126 action_label_->setFrameShadow(QFrame::Raised);
127 action_label_->setLineWidth(1);
128 action_label_->setMidLineWidth(0);
129 action_label_->setWordWrap(
true);
130 action_label_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
131 action_label_->setMinimumWidth(100);
132 action_label_->setAlignment(Qt::AlignTop);
133 action_label_->setOpenExternalLinks(
true);
136 splitter->addWidget(action_list_);
137 splitter->addWidget(action_label_);
140 layout->addWidget(splitter);
143 QHBoxLayout* hlayout1 =
new QHBoxLayout();
146 progress_bar_ =
new QProgressBar(
this);
147 progress_bar_->setMaximum(100);
148 progress_bar_->setMinimum(0);
149 hlayout1->addWidget(progress_bar_);
153 btn_save_ =
new QPushButton(
"&Generate Package",
this);
155 btn_save_->setMinimumHeight(40);
156 connect(btn_save_, SIGNAL(clicked()),
this, SLOT(savePackage()));
157 hlayout1->addWidget(btn_save_);
160 layout->addLayout(hlayout1);
164 QHBoxLayout* hlayout3 =
new QHBoxLayout();
167 success_label_ =
new QLabel(
this);
168 QFont success_label_font(QFont().defaultFamily(), 12, QFont::Bold);
169 success_label_->setFont(success_label_font);
170 success_label_->hide();
171 success_label_->setText(
"Configuration package generated successfully!");
172 hlayout3->addWidget(success_label_);
173 hlayout3->setAlignment(success_label_, Qt::AlignRight);
176 QPushButton* btn_exit =
new QPushButton(
"E&xit Setup Assistant",
this);
177 btn_exit->setMinimumWidth(180);
178 connect(btn_exit, SIGNAL(clicked()),
this, SLOT(exitSetupAssistant()));
179 hlayout3->addWidget(btn_exit);
180 hlayout3->setAlignment(btn_exit, Qt::AlignRight);
182 layout->addLayout(hlayout3);
185 this->setLayout(layout);
188 void ConfigurationFilesWidget::setCheckSelected(
bool checked)
190 for (
const QModelIndex& row : action_list_->selectionModel()->selectedRows())
191 action_list_->model()->setData(row, checked ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
197 bool ConfigurationFilesWidget::loadGenFiles()
200 std::string template_path;
201 const std::string robot_name = config_data_->srdf_->robot_name_;
206 fs::path template_package_path = config_data_->setup_assistant_path_;
207 template_package_path /=
"templates";
208 template_package_path /=
"moveit_config_pkg_template";
209 config_data_->template_package_path_ = template_package_path.make_preferred().string();
211 if (!fs::is_directory(config_data_->template_package_path_))
213 QMessageBox::critical(
214 this,
"Error Generating",
215 QString(
"Unable to find package template directory: ").
append(config_data_->template_package_path_.c_str()));
226 file.file_name_ =
"package.xml";
227 file.rel_path_ = file.file_name_;
228 template_path = config_data_->appendPaths(config_data_->template_package_path_,
"package.xml.template");
229 file.description_ =
"Defines a ROS package";
230 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
231 return copyTemplate(template_path, output_path);
233 file.write_on_changes = MoveItConfigData::AUTHOR_INFO;
234 gen_files_.push_back(file);
237 file.file_name_ =
"CMakeLists.txt";
238 file.rel_path_ = file.file_name_;
239 template_path = config_data_->appendPaths(config_data_->template_package_path_, file.file_name_);
240 file.description_ =
"CMake build system configuration file";
241 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
242 return copyTemplate(template_path, output_path);
244 file.write_on_changes = 0;
245 gen_files_.push_back(file);
252 file.file_name_ =
"config/";
253 file.rel_path_ = file.file_name_;
254 file.description_ =
"Folder containing all MoveIt configuration files for your robot. This folder is required and "
255 "cannot be disabled.";
256 file.gen_func_ = [
this](
const std::string& output_path) {
return createFolder(output_path); };
257 file.write_on_changes = 0;
258 gen_files_.push_back(file);
261 file.file_name_ = config_data_->srdf_pkg_relative_path_.empty() ? config_data_->urdf_model_->getName() +
".srdf" :
262 config_data_->srdf_pkg_relative_path_;
263 file.rel_path_ = config_data_->srdf_pkg_relative_path_.empty() ?
264 config_data_->appendPaths(
CONFIG_PATH, file.file_name_) :
265 config_data_->srdf_pkg_relative_path_;
266 file.description_ =
"SRDF (<a href='http://www.ros.org/wiki/srdf'>Semantic Robot Description Format</a>) is a "
267 "representation of semantic information about robots. This format is intended to represent "
268 "information about the robot that is not in the URDF file, but it is useful for a variety of "
269 "applications. The intention is to include information that has a semantic aspect to it.";
270 file.gen_func_ = [&writer = *config_data_->srdf_](
const std::string& output_path) {
271 return writer.writeSRDF(output_path);
273 file.write_on_changes = MoveItConfigData::SRDF;
274 gen_files_.push_back(file);
276 config_data_->srdf_pkg_relative_path_ = file.rel_path_;
279 if (!config_data_->gazebo_urdf_string_.empty())
281 file.file_name_ =
"gazebo_" + config_data_->urdf_model_->getName() +
".urdf";
282 file.rel_path_ = config_data_->appendPaths(
CONFIG_PATH, file.file_name_);
284 "This <a href='https://wiki.ros.org/urdf'>URDF</a> file comprises your original robot description "
285 "augmented with tags required for use with Gazebo, i.e. defining inertia and transmission properties. "
286 "Checkout the <a href='http://gazebosim.org/tutorials/?tut=ros_urdf'>URDF Gazebo documentation</a> "
288 file.gen_func_ = [
this](
const std::string& output_path) {
return config_data_->outputGazeboURDFFile(output_path); };
289 file.write_on_changes = MoveItConfigData::SIMULATION;
290 gen_files_.push_back(file);
294 file.file_name_ =
"ompl_planning.yaml";
295 file.rel_path_ = config_data_->appendPaths(
CONFIG_PATH, file.file_name_);
296 file.description_ =
"Configures the OMPL (<a href='http://ompl.kavrakilab.org/'>Open Motion Planning Library</a>) "
297 "planning plugin. For every planning group defined in the SRDF, a number of planning "
298 "configurations are specified (under planner_configs). Additionally, default settings for the "
299 "state space to plan in for a particular group can be specified, such as the collision checking "
300 "resolution. Each planning configuration specified for a group must be defined under the "
301 "planner_configs tag. While defining a planner configuration, the only mandatory parameter is "
302 "'type', which is the name of the motion planner to be used. Any other planner-specific "
303 "parameters can be defined but are optional.";
304 file.gen_func_ = [
this](
const std::string& output_path) {
return config_data_->outputOMPLPlanningYAML(output_path); };
305 file.write_on_changes = MoveItConfigData::GROUPS;
306 gen_files_.push_back(file);
309 file.file_name_ =
"chomp_planning.yaml";
310 file.rel_path_ = config_data_->appendPaths(
CONFIG_PATH, file.file_name_);
311 template_path = config_data_->appendPaths(config_data_->template_package_path_, file.rel_path_);
312 file.description_ =
"Specifies which chomp planning plugin parameters to be used for the CHOMP planner";
313 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
314 return copyTemplate(template_path, output_path);
316 gen_files_.push_back(file);
319 file.file_name_ =
"stomp_planning.yaml";
320 file.rel_path_ = config_data_->appendPaths(
CONFIG_PATH, file.file_name_);
321 file.description_ =
"Specifies which stomp planning plugin parameters to be used for the STOMP planner";
322 file.gen_func_ = [
this](
const std::string& output_path) {
return config_data_->outputSTOMPPlanningYAML(output_path); };
323 file.write_on_changes = MoveItConfigData::GROUPS;
324 gen_files_.push_back(file);
327 file.file_name_ =
"kinematics.yaml";
328 file.rel_path_ = config_data_->appendPaths(
CONFIG_PATH, file.file_name_);
329 file.description_ =
"Specifies which kinematic solver plugin to use for each planning group in the SRDF, as well as "
330 "the kinematic solver search resolution.";
331 file.gen_func_ = [
this](
const std::string& output_path) {
return config_data_->outputKinematicsYAML(output_path); };
332 file.write_on_changes = MoveItConfigData::GROUPS | MoveItConfigData::GROUP_KINEMATICS;
333 gen_files_.push_back(file);
336 file.file_name_ =
"joint_limits.yaml";
337 file.rel_path_ = config_data_->appendPaths(
CONFIG_PATH, file.file_name_);
338 file.description_ =
"Contains additional information about joints that appear in your planning groups that is not "
339 "contained in the URDF, as well as allowing you to set maximum and minimum limits for velocity "
340 "and acceleration than those contained in your URDF. This information is used by our trajectory "
341 "filtering system to assign reasonable velocities and timing for the trajectory before it is "
342 "passed to the robots controllers.";
343 file.gen_func_ = [
this](
const std::string& output_path) {
return config_data_->outputJointLimitsYAML(output_path); };
344 file.write_on_changes = 0;
345 gen_files_.push_back(file);
348 file.file_name_ =
"cartesian_limits.yaml";
349 file.rel_path_ = config_data_->appendPaths(
CONFIG_PATH, file.file_name_);
350 template_path = config_data_->appendPaths(config_data_->template_package_path_, file.rel_path_);
351 file.description_ =
"Cartesian velocity for planning in the workspace."
352 "The velocity is used by pilz industrial motion planner as maximum velocity for cartesian "
353 "planning requests scaled by the velocity scaling factor of an individual planning request.";
354 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
355 return copyTemplate(template_path, output_path);
357 file.write_on_changes = 0;
358 gen_files_.push_back(file);
361 file.file_name_ =
"fake_controllers.yaml";
362 file.rel_path_ = config_data_->appendPaths(
CONFIG_PATH, file.file_name_);
363 file.description_ =
"Creates dummy configurations for controllers that correspond to defined groups. This is mostly "
364 "useful for testing.";
365 file.gen_func_ = [
this](
const std::string& output_path) {
366 return config_data_->outputFakeControllersYAML(output_path);
368 file.write_on_changes = MoveItConfigData::GROUPS;
369 gen_files_.push_back(file);
372 file.file_name_ =
"simple_moveit_controllers.yaml";
373 file.rel_path_ = config_data_->appendPaths(
CONFIG_PATH, file.file_name_);
374 file.description_ =
"Creates controller configuration for SimpleMoveItControllerManager";
375 file.gen_func_ = [
this](
const std::string& output_path) {
376 return config_data_->outputSimpleControllersYAML(output_path);
378 file.write_on_changes = MoveItConfigData::GROUPS;
379 gen_files_.push_back(file);
382 file.file_name_ =
"gazebo_controllers.yaml";
383 file.rel_path_ = config_data_->appendPaths(
CONFIG_PATH, file.file_name_);
384 template_path = config_data_->appendPaths(config_data_->template_package_path_, file.rel_path_);
385 file.description_ =
"Configuration of Gazebo controllers";
386 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
387 return copyTemplate(template_path, output_path);
389 gen_files_.push_back(file);
392 file.file_name_ =
"ros_controllers.yaml";
393 file.rel_path_ = config_data_->appendPaths(
CONFIG_PATH, file.file_name_);
394 file.description_ =
"Creates controller configurations for ros_control.";
395 file.gen_func_ = [
this](
const std::string& output_path) {
396 return config_data_->outputROSControllersYAML(output_path);
398 file.write_on_changes = MoveItConfigData::GROUPS;
399 gen_files_.push_back(file);
402 file.file_name_ =
"sensors_3d.yaml";
403 file.rel_path_ = config_data_->appendPaths(
CONFIG_PATH, file.file_name_);
404 file.description_ =
"Creates configurations 3d sensors.";
405 file.gen_func_ = [
this](
const std::string& output_path) {
406 return config_data_->output3DSensorPluginYAML(output_path);
408 file.write_on_changes = MoveItConfigData::SENSORS_CONFIG;
409 gen_files_.push_back(file);
414 std::string launch_path =
"launch";
415 const std::string template_launch_path = config_data_->appendPaths(config_data_->template_package_path_, launch_path);
418 file.file_name_ =
"launch/";
419 file.rel_path_ = file.file_name_;
420 file.description_ =
"Folder containing all MoveIt launch files for your robot. "
421 "This folder is required and cannot be disabled.";
422 file.gen_func_ = [
this](
const std::string& output_path) {
return createFolder(output_path); };
423 file.write_on_changes = 0;
424 gen_files_.push_back(file);
427 file.file_name_ =
"move_group.launch";
428 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
429 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
430 file.description_ =
"Launches the move_group node that provides the MoveGroup action and other parameters <a "
431 "href='http://moveit.ros.org/doxygen/"
432 "classmoveit_1_1planning__interface_1_1MoveGroup.html#details'>MoveGroup action</a>";
433 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
434 return copyTemplate(template_path, output_path);
436 file.write_on_changes = 0;
437 gen_files_.push_back(file);
440 file.file_name_ =
"planning_context.launch";
441 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
442 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
443 file.description_ =
"Loads settings for the ROS parameter server, required for running MoveIt. This includes the "
444 "SRDF, joints_limits.yaml file, ompl_planning.yaml file, optionally the URDF, etc";
445 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
446 return copyTemplate(template_path, output_path);
448 file.write_on_changes = 0;
449 gen_files_.push_back(file);
452 file.file_name_ =
"moveit_rviz.launch";
453 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
454 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
455 file.description_ =
"Visualize in Rviz the robot's planning groups running with interactive markers that allow goal "
457 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
458 return copyTemplate(template_path, output_path);
460 file.write_on_changes = 0;
461 gen_files_.push_back(file);
465 file.file_name_ =
"ompl_planning_pipeline.launch.xml";
466 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
467 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
468 file.description_ =
"Intended to be included in other launch files that require the OMPL planning plugin. Defines "
469 "the proper plugin name on the parameter server and a default selection of planning request "
471 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
472 return copyTemplate(template_path, output_path);
474 file.write_on_changes = 0;
475 gen_files_.push_back(file);
479 file.file_name_ =
"pilz_industrial_motion_planner_planning_pipeline.launch.xml";
480 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
481 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
482 file.description_ =
"Intended to be included in other launch files that require the Pilz industrial motion planner "
483 "planning plugin. Defines the proper plugin name on the parameter server and a default selection "
484 "of planning request adapters.";
485 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
486 return copyTemplate(template_path, output_path);
488 file.write_on_changes = 0;
489 gen_files_.push_back(file);
493 file.file_name_ =
"chomp_planning_pipeline.launch.xml";
494 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
495 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
496 file.description_ =
"Intended to be included in other launch files that require the CHOMP planning plugin. Defines "
497 "the proper plugin name on the parameter server and a default selection of planning request "
499 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
500 return copyTemplate(template_path, output_path);
502 file.write_on_changes = 0;
503 gen_files_.push_back(file);
507 file.file_name_ =
"stomp_planning_pipeline.launch.xml";
508 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
509 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
510 file.description_ =
"Intended to be included in other launch files that require the STOMP planning plugin. Defines "
511 "the proper plugin name on the parameter server and a default selection of planning request "
513 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
514 return copyTemplate(template_path, output_path);
516 file.write_on_changes = 0;
517 gen_files_.push_back(file);
521 file.file_name_ =
"ompl-chomp_planning_pipeline.launch.xml";
522 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
523 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
525 "Intended to be included in other launch files that require the OMPL-CHOMP planning plugins. Defines "
526 "the proper plugin name on the parameter server and a default selection of planning request "
528 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
529 return copyTemplate(template_path, output_path);
531 file.write_on_changes = 0;
532 gen_files_.push_back(file);
535 file.file_name_ =
"planning_pipeline.launch.xml";
536 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
537 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
538 file.description_ =
"Helper launch file that can choose between different planning pipelines to be loaded.";
539 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
540 return copyTemplate(template_path, output_path);
542 file.write_on_changes = 0;
543 gen_files_.push_back(file);
546 file.file_name_ =
"warehouse_settings.launch.xml";
547 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
548 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
549 file.description_ =
"Helper launch file that specifies default settings for MongoDB.";
550 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
551 return copyTemplate(template_path, output_path);
553 file.write_on_changes = 0;
554 gen_files_.push_back(file);
557 file.file_name_ =
"warehouse.launch";
558 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
559 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
560 file.description_ =
"Launch file for starting MongoDB.";
561 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
562 return copyTemplate(template_path, output_path);
564 file.write_on_changes = 0;
565 gen_files_.push_back(file);
568 file.file_name_ =
"default_warehouse_db.launch";
569 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
570 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
571 file.description_ =
"Launch file for starting the warehouse with a default MongoDB.";
572 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
573 return copyTemplate(template_path, output_path);
575 file.write_on_changes = 0;
576 gen_files_.push_back(file);
579 file.file_name_ =
"run_benchmark_ompl.launch";
580 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
581 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
582 file.description_ =
"Launch file for benchmarking OMPL planners";
583 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
584 return copyTemplate(template_path, output_path);
586 file.write_on_changes = 0;
587 gen_files_.push_back(file);
590 file.file_name_ =
"sensor_manager.launch.xml";
591 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
592 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
593 file.description_ =
"Helper launch file that can choose between different sensor managers to be loaded.";
594 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
595 return copyTemplate(template_path, output_path);
597 file.write_on_changes = 0;
598 gen_files_.push_back(file);
601 file.file_name_ = robot_name +
"_moveit_sensor_manager.launch.xml";
602 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
603 template_path = config_data_->appendPaths(template_launch_path,
"moveit_sensor_manager.launch.xml");
604 file.description_ =
"Placeholder for settings specific to the MoveIt sensor manager implemented for you robot.";
605 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
606 return copyTemplate(template_path, output_path);
608 file.write_on_changes = 0;
609 gen_files_.push_back(file);
612 file.file_name_ =
"trajectory_execution.launch.xml";
613 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
614 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
615 file.description_ =
"Loads settings for the ROS parameter server required for executing trajectories using the "
616 "trajectory_execution_manager::TrajectoryExecutionManager.";
617 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
618 return copyTemplate(template_path, output_path);
620 file.write_on_changes = 0;
621 gen_files_.push_back(file);
623 file.file_name_ =
"fake_moveit_controller_manager.launch.xml";
624 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
625 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
626 file.description_ =
"Loads the fake controller plugin.";
627 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
628 return copyTemplate(template_path, output_path);
630 file.write_on_changes = 0;
631 gen_files_.push_back(file);
633 file.file_name_ =
"simple_moveit_controller_manager.launch.xml";
634 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
635 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
636 file.description_ =
"Loads the default controller plugin.";
637 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
638 return copyTemplate(template_path, output_path);
640 file.write_on_changes = 0;
641 gen_files_.push_back(file);
643 file.file_name_ =
"ros_control_moveit_controller_manager.launch.xml";
644 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
645 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
646 file.description_ =
"Loads the ros_control controller plugin.";
647 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
648 return copyTemplate(template_path, output_path);
650 file.write_on_changes = 0;
651 gen_files_.push_back(file);
654 file.file_name_ =
"demo.launch";
655 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
656 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
657 file.description_ =
"Run a demo of MoveIt.";
658 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
659 return copyTemplate(template_path, output_path);
661 file.write_on_changes = 0;
662 gen_files_.push_back(file);
665 file.file_name_ =
"demo_gazebo.launch";
666 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
667 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
668 file.description_ =
"Run a demo of MoveIt with Gazebo and Rviz";
669 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
670 return copyTemplate(template_path, output_path);
672 file.write_on_changes = 0;
673 gen_files_.push_back(file);
676 file.file_name_ =
"gazebo.launch";
677 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
678 template_path = config_data_->appendPaths(template_launch_path,
"gazebo.launch");
679 file.description_ =
"Gazebo launch file which also launches ros_controllers and sends robot urdf to param server, "
680 "then using gazebo_ros pkg the robot is spawned to Gazebo";
681 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
682 return copyTemplate(template_path, output_path);
684 file.write_on_changes = 0;
685 gen_files_.push_back(file);
688 file.file_name_ =
"joystick_control.launch";
689 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
690 template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
691 file.description_ =
"Control the Rviz Motion Planning Plugin with a joystick";
692 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
693 return copyTemplate(template_path, output_path);
695 file.write_on_changes = 0;
696 gen_files_.push_back(file);
699 file.file_name_ =
"setup_assistant.launch";
700 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
701 template_path = config_data_->appendPaths(
702 template_launch_path,
"edit_configuration_package.launch");
704 file.description_ =
"Launch file for easily re-starting the MoveIt Setup Assistant to edit this robot's generated "
705 "configuration package.";
706 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
707 return copyTemplate(template_path, output_path);
709 file.write_on_changes = 0;
710 gen_files_.push_back(file);
713 file.file_name_ =
"ros_controllers.launch";
714 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
715 template_path = config_data_->appendPaths(template_launch_path,
"ros_controllers.launch");
716 file.description_ =
"ros_controllers launch file";
717 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
718 return copyTemplate(template_path, output_path);
720 file.write_on_changes = MoveItConfigData::GROUPS;
721 gen_files_.push_back(file);
724 file.file_name_ =
"moveit.rviz";
725 file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
726 template_path = config_data_->appendPaths(template_launch_path,
"moveit.rviz");
727 file.description_ =
"Configuration file for Rviz with the Motion Planning Plugin already setup. Used by passing "
728 "roslaunch moveit_rviz.launch config:=true";
729 file.gen_func_ = [
this, template_path](
const std::string& output_path) {
730 return copyTemplate(template_path, output_path);
732 file.write_on_changes = 0;
733 gen_files_.push_back(file);
741 file.rel_path_ = file.file_name_;
742 file.description_ =
"MoveIt Setup Assistant's hidden settings file. You should not need to edit this file.";
743 file.gen_func_ = [
this](
const std::string& output_path) {
744 return config_data_->outputSetupAssistantFile(output_path);
746 file.write_on_changes = -1;
747 gen_files_.push_back(file);
755 bool ConfigurationFilesWidget::checkDependencies()
757 QStringList dependencies;
758 bool required_actions =
false;
761 if (config_data_->srdf_->groups_.empty())
763 dependencies <<
"No robot model planning groups have been created";
767 if (config_data_->srdf_->disabled_collision_pairs_.empty())
769 dependencies <<
"No self-collisions have been disabled";
773 if (config_data_->srdf_->end_effectors_.empty())
775 dependencies <<
"No end effectors have been added";
779 if (config_data_->srdf_->virtual_joints_.empty())
781 dependencies <<
"No virtual joints have been added";
785 if (config_data_->author_name_.find_first_not_of(
' ') == std::string::npos)
788 dependencies <<
"<b>No author name added</b>";
789 required_actions =
true;
793 QRegularExpression mail_regex(
"\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b");
794 mail_regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
795 QString test_email = QString::fromStdString(config_data_->author_email_);
796 if (!mail_regex.match(test_email).hasMatch())
798 dependencies <<
"<b>No valid email address added</b>";
799 required_actions =
true;
803 if (!dependencies.empty())
807 if (!required_actions)
809 dep_message =
"Some setup steps have not been completed. None of the steps are required, but here is a reminder "
810 "of what was not filled in, just in case something was forgotten:<br /><ul>";
814 dep_message =
"Some setup steps have not been completed. Please fix the required steps (printed in bold), "
815 "otherwise the setup cannot be completed:<br /><ul>";
818 for (
int i = 0; i < dependencies.size(); ++i)
820 dep_message.append(
"<li>").append(dependencies.at(i)).append(
"</li>");
823 if (!required_actions)
825 dep_message.append(
"</ul><br/>Press Ok to continue generating files.");
826 if (QMessageBox::question(
this,
"Incomplete MoveIt Setup Assistant Steps", dep_message,
827 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
834 QMessageBox::warning(
this,
"Incomplete MoveIt Setup Assistant Steps", dep_message);
845 void ConfigurationFilesWidget::updateProgress()
850 progress_bar_->setValue(
double(action_num_) / gen_files_.size() * 100);
853 QApplication::processEvents();
859 void ConfigurationFilesWidget::changeActionDesc(
int id)
865 action_label_->setText(action_desc_.at(
id));
872 void ConfigurationFilesWidget::changeCheckedState(QListWidgetItem* item)
874 std::size_t
index = item->data(Qt::UserRole).toUInt();
875 bool generate = (item->checkState() == Qt::Checked);
877 if (!generate && (gen_files_[index].write_on_changes & config_data_->changes))
879 QMessageBox::warning(
this,
"Package Generation",
880 "You should generate this file to ensure your changes will take "
891 void ConfigurationFilesWidget::focusGiven()
896 bool files_already_modified = checkGenFiles();
899 disconnect(action_list_, SIGNAL(itemChanged(QListWidgetItem*)),
this, SLOT(changeCheckedState(QListWidgetItem*)));
902 bool have_conflicting_changes = showGenFiles();
905 connect(action_list_, SIGNAL(itemChanged(QListWidgetItem*)),
this, SLOT(changeCheckedState(QListWidgetItem*)));
908 QApplication::processEvents();
910 if (files_already_modified)
913 QString msg(
"Some files have been modified outside of the Setup Assistant (according to timestamp). "
914 "The Setup Assistant will not overwrite these changes by default because often changing configuration "
915 "files manually is necessary, "
916 "but we recommend you check the list and enable the checkbox next to files you would like to "
918 if (have_conflicting_changes)
919 msg +=
"<br/><font color='red'>Attention:</font> Some files (<font color='red'>marked red</font>) are changed "
920 "both, externally and in Setup Assistant.";
921 QMessageBox::information(
this,
"Files Modified", msg);
929 bool ConfigurationFilesWidget::checkGenFiles()
932 if (config_data_->config_pkg_path_.empty())
936 if (config_data_->config_pkg_generated_timestamp_ == 0)
939 static const std::time_t TIME_MOD_TOLERANCE = 10;
942 bool found_modified =
false;
947 fs::path file_path = config_data_->appendPaths(config_data_->config_pkg_path_, file->
rel_path_);
950 if (fs::is_directory(file_path))
953 if (fs::is_regular_file(file_path))
955 std::time_t mod_time = fs::last_write_time(file_path);
960 if (mod_time > config_data_->config_pkg_generated_timestamp_ + TIME_MOD_TOLERANCE ||
961 mod_time < config_data_->config_pkg_generated_timestamp_ - TIME_MOD_TOLERANCE)
970 found_modified =
true;
980 return found_modified;
986 bool ConfigurationFilesWidget::showGenFiles()
988 bool have_conflicting_changes =
false;
989 action_list_->clear();
992 for (std::size_t i = 0; i < gen_files_.size(); ++i)
994 GenerateFile* file = &gen_files_[i];
997 QListWidgetItem* item =
new QListWidgetItem(QString(file->rel_path_.c_str()), action_list_, 0);
999 fs::path file_path = config_data_->appendPaths(config_data_->config_pkg_path_, file->rel_path_);
1002 item->setCheckState(file->generate_ ? Qt::Checked : Qt::Unchecked);
1004 if (file->modified_)
1006 if (file->write_on_changes & config_data_->changes)
1008 have_conflicting_changes =
true;
1009 item->setForeground(QBrush(QColor(255, 0, 0)));
1012 item->setForeground(QBrush(QColor(255, 135, 0)));
1016 if (fs::is_directory(file_path))
1018 item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
1022 item->setData(Qt::UserRole, QVariant(
static_cast<qulonglong
>(i)));
1025 action_list_->addItem(item);
1026 action_desc_.append(QString(file->description_.c_str()));
1030 action_list_->setCurrentRow(0);
1031 return have_conflicting_changes;
1037 void ConfigurationFilesWidget::savePackage()
1040 success_label_->hide();
1044 progress_bar_->setValue(0);
1046 if (!generatePackage())
1053 progress_bar_->setValue(100);
1054 success_label_->show();
1055 has_generated_pkg_ =
true;
1061 bool ConfigurationFilesWidget::generatePackage()
1064 std::string new_package_path = stack_path_->getPath();
1067 if (new_package_path.empty())
1069 QMessageBox::warning(
this,
"Error Generating",
1070 "No package path provided. Please choose a directory location to "
1071 "generate the MoveIt configuration files.");
1076 if (!checkDependencies())
1080 if (!noGroupsEmpty())
1084 boost::trim(new_package_path);
1087 new_package_name_ = getPackageName(new_package_path);
1089 const std::string setup_assistant_file = config_data_->appendPaths(new_package_path,
SETUP_ASSISTANT_FILE);
1092 if (fs::is_directory(new_package_path) && !fs::is_empty(new_package_path))
1095 if (!fs::is_regular_file(setup_assistant_file))
1097 QMessageBox::warning(
1098 this,
"Incorrect Folder/Package",
1099 QString(
"The chosen package location already exists but was not previously created using this MoveIt Setup "
1101 "If this is a mistake, add the missing file: ")
1102 .
append(setup_assistant_file.c_str()));
1107 if (QMessageBox::question(
this,
"Confirm Package Update",
1108 QString(
"Are you sure you want to overwrite this existing package with updated "
1109 "configurations?<br /><i>")
1110 .
append(new_package_path.c_str())
1112 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
1122 fs::create_directory(new_package_path) && !fs::is_directory(new_package_path);
1126 QMessageBox::critical(
this,
"Error Generating Files",
1127 QString(
"Unable to create directory ").
append(new_package_path.c_str()));
1132 loadTemplateStrings();
1135 std::string absolute_path;
1137 for (GenerateFile& gen_file : gen_files_)
1139 GenerateFile* file = &gen_file;
1142 if (!file->generate_)
1148 absolute_path = config_data_->appendPaths(new_package_path, file->rel_path_);
1152 if (!file->gen_func_(absolute_path))
1155 QMessageBox::critical(
this,
"Error Generating File",
1156 QString(
"Failed to generate folder or file: '")
1157 .
append(file->rel_path_.c_str())
1158 .append(
"' at location:\n")
1159 .append(absolute_path.c_str()));
1171 void ConfigurationFilesWidget::exitSetupAssistant()
1173 if (has_generated_pkg_ || QMessageBox::question(
this,
"Exit Setup Assistant",
1174 QString(
"Are you sure you want to exit the MoveIt Setup Assistant?"),
1175 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok)
1177 QApplication::quit();
1184 const std::string ConfigurationFilesWidget::getPackageName(std::string package_path)
1187 if (!package_path.compare(package_path.size() - 1, 1,
"/"))
1189 package_path = package_path.substr(0, package_path.size() - 1);
1193 std::string package_name;
1194 fs::path fs_package_path = package_path;
1196 package_name = fs_package_path.filename().string();
1199 if (package_name.empty())
1200 package_name =
"unknown";
1202 return package_name;
1208 bool ConfigurationFilesWidget::noGroupsEmpty()
1211 for (
const auto&
group : config_data_->srdf_->groups_)
1214 if (!
group.joints_.empty())
1218 if (!
group.chains_.empty())
1220 if (!
group.subgroups_.empty())
1224 QMessageBox::warning(
1225 this,
"Empty Group",
1226 QString(
"The planning group '")
1228 .append(
"' is empty and has no subcomponents associated with it (joints/links/chains/subgroups). You must "
1229 "edit or remove this planning group before this configuration package can be saved."));
1239 void ConfigurationFilesWidget::loadTemplateStrings()
1242 template_strings_.clear();
1245 addTemplateString(
"[GENERATED_PACKAGE_NAME]", new_package_name_);
1248 std::string urdf_location = config_data_->urdf_pkg_name_.empty() ? config_data_->urdf_path_ :
1249 "$(find " + config_data_->urdf_pkg_name_ +
")/" +
1250 config_data_->urdf_pkg_relative_path_;
1251 addTemplateString(
"[URDF_LOCATION]", urdf_location);
1254 if (config_data_->urdf_from_xacro_)
1255 addTemplateString(
"[URDF_LOAD_ATTRIBUTE]",
1256 "command=\"xacro " + config_data_->xacro_args_ +
" '" + urdf_location +
"'\"");
1258 addTemplateString(
"[URDF_LOAD_ATTRIBUTE]",
"textfile=\"" + urdf_location +
"\"");
1261 if (config_data_->changes & MoveItConfigData::SIMULATION && !config_data_->gazebo_urdf_string_.empty())
1263 std::string file_name =
"gazebo_" + config_data_->urdf_model_->getName() +
".urdf";
1264 std::string rel_path = config_data_->appendPaths(
CONFIG_PATH, file_name);
1265 addTemplateString(
"[GAZEBO_URDF_LOAD_ATTRIBUTE]",
"textfile=\"$(find " + new_package_name_ +
")/" + rel_path +
"\"");
1268 addTemplateString(
"[GAZEBO_URDF_LOAD_ATTRIBUTE]", template_strings_.back().second);
1271 addTemplateString(
"[ROBOT_NAME]", config_data_->srdf_->robot_name_);
1274 addTemplateString(
"[ROBOT_ROOT_LINK]", config_data_->getRobotModel()->getRootLinkName());
1277 addTemplateString(
"[PLANNING_FRAME]", config_data_->getRobotModel()->getModelFrame());
1280 std::stringstream vjb;
1281 for (std::size_t i = 0; i < config_data_->srdf_->virtual_joints_.size(); ++i)
1284 vjb <<
" <node pkg=\"tf2_ros\" type=\"static_transform_publisher\" name=\"virtual_joint_broadcaster_" << i
1287 addTemplateString(
"[VIRTUAL_JOINT_BROADCASTER]", vjb.str());
1290 if (config_data_->urdf_pkg_name_.empty())
1292 addTemplateString(
"[OTHER_DEPENDENCIES",
"");
1296 std::stringstream deps;
1297 deps <<
" <run_depend>" << config_data_->urdf_pkg_name_ <<
"</run_depend>\n";
1298 addTemplateString(
"[OTHER_DEPENDENCIES]", deps.str());
1302 if (config_data_->getControllers().empty())
1304 addTemplateString(
"[ROS_CONTROLLERS]",
"");
1308 std::stringstream controllers;
1309 for (ControllerConfig& controller : config_data_->getControllers())
1312 if (controller.type_ !=
"FollowJointTrajectory")
1313 controllers << controller.name_ <<
" ";
1315 addTemplateString(
"[ROS_CONTROLLERS]", controllers.str());
1320 std::string kinematics_parameters_files_block;
1321 for (
const auto& groups : config_data_->group_meta_data_)
1323 if (groups.second.kinematics_parameters_file_.empty())
1327 if (!kinematics_parameters_files_block.empty())
1328 kinematics_parameters_files_block +=
"\n";
1330 std::string line =
" <rosparam command=\"load\" ns=\"" + groups.first +
"\" file=\"" +
1331 groups.second.kinematics_parameters_file_ +
"\"/>";
1332 kinematics_parameters_files_block += line;
1334 addTemplateString(
"[KINEMATICS_PARAMETERS_FILE_NAMES_BLOCK]", kinematics_parameters_files_block);
1336 addTemplateString(
"[AUTHOR_NAME]", config_data_->author_name_);
1337 addTemplateString(
"[AUTHOR_EMAIL]", config_data_->author_email_);
1340 std::stringstream joints;
1341 for (
const auto& pair : config_data_->getInitialJoints())
1342 joints <<
" -J " << pair.first <<
" " << pair.second;
1343 addTemplateString(
"[GAZEBO_INITIAL_JOINTS]", joints.str());
1350 bool ConfigurationFilesWidget::addTemplateString(
const std::string& key,
const std::string& value)
1352 template_strings_.push_back(std::pair<std::string, std::string>(key, value));
1360 bool ConfigurationFilesWidget::copyTemplate(
const std::string& template_path,
const std::string& output_path)
1363 if (!fs::is_regular_file(template_path))
1370 std::ifstream template_stream(template_path.c_str());
1371 if (!template_stream.good())
1378 std::string template_string;
1379 template_stream.seekg(0, std::ios::end);
1380 template_string.reserve(template_stream.tellg());
1381 template_stream.seekg(0, std::ios::beg);
1382 template_string.assign((std::istreambuf_iterator<char>(template_stream)), std::istreambuf_iterator<char>());
1383 template_stream.close();
1386 for (std::pair<std::string, std::string>& it : template_strings_)
1388 boost::replace_all(template_string, it.first, it.second);
1392 std::ofstream output_stream(output_path.c_str(), std::ios_base::trunc);
1393 if (!output_stream.good())
1399 output_stream << template_string.c_str();
1400 output_stream.close();
1408 bool ConfigurationFilesWidget::createFolder(
const std::string& output_path)
1410 if (!fs::is_directory(output_path))
1412 if (!fs::create_directory(output_path))
1414 QMessageBox::critical(
this,
"Error Generating Files",
1415 QString(
"Unable to create directory ").
append(output_path.c_str()));