configuration_files_widget.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2012, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: Dave Coleman */
36 
37 // Qt
38 #include <QVBoxLayout>
39 #include <QPushButton>
40 #include <QMessageBox>
41 #include <QApplication>
42 #include <QSplitter>
43 #include <QRegExp>
44 // ROS
46 #include <srdfdom/model.h> // use their struct datastructures
47 #include <ros/ros.h>
48 // Boost
49 #include <boost/algorithm/string.hpp> // for trimming whitespace from user input
50 #include <boost/filesystem.hpp> // for creating folders/files
51 // Read write files
52 #include <iostream> // For writing yaml and launch files
53 #include <fstream>
54 
55 namespace moveit_setup_assistant
56 {
57 // Boost file system
58 namespace fs = boost::filesystem;
59 
60 const std::string SETUP_ASSISTANT_FILE = ".setup_assistant";
61 
62 // ******************************************************************************************
63 // Outer User Interface for MoveIt! Configuration Assistant
64 // ******************************************************************************************
66  moveit_setup_assistant::MoveItConfigDataPtr config_data)
67  : SetupScreenWidget(parent), config_data_(config_data), has_generated_pkg_(false), first_focusGiven_(true)
68 {
69  // Basic widget container
70  QVBoxLayout* layout = new QVBoxLayout();
71 
72  // Top Header Area ------------------------------------------------
73 
75  new HeaderWidget("Generate Configuration Files",
76  "Create or update the configuration files package needed to run your robot with MoveIt. Uncheck "
77  "files to disable them from being generated - this is useful if you have made custom changes to "
78  "them. Files in orange have been automatically detected as changed.",
79  this);
80  layout->addWidget(header);
81 
82  // Path Widget ----------------------------------------------------
83 
84  // Stack Path Dialog
85  stack_path_ = new LoadPathWidget("Configuration Package Save Path",
86  "Specify the desired directory for the MoveIt! configuration package to be "
87  "generated. Overwriting an existing configuration package directory is acceptable. "
88  "Example: <i>/u/robot/ros/pr2_moveit_config</i>",
89  this, true); // is directory
90  layout->addWidget(stack_path_);
91 
92  // Pass the package path from start screen to configuration files screen
93  stack_path_->setPath(config_data_->config_pkg_path_);
94 
95  // Generated Files List -------------------------------------------
96  QLabel* generated_list = new QLabel("Files to be generated: (checked)", this);
97  layout->addWidget(generated_list);
98 
99  QSplitter* splitter = new QSplitter(Qt::Horizontal, this);
100  splitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
101 
102  // List Box
103  action_list_ = new QListWidget(this);
104  action_list_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
105  connect(action_list_, SIGNAL(currentRowChanged(int)), this, SLOT(changeActionDesc(int)));
106 
107  // Description
108  action_label_ = new QLabel(this);
109  action_label_->setFrameShape(QFrame::StyledPanel);
110  action_label_->setFrameShadow(QFrame::Raised);
111  action_label_->setLineWidth(1);
112  action_label_->setMidLineWidth(0);
113  action_label_->setWordWrap(true);
114  action_label_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
115  action_label_->setMinimumWidth(100);
116  action_label_->setAlignment(Qt::AlignTop);
117  action_label_->setOpenExternalLinks(true); // open with web browser
118 
119  // Add to splitter
120  splitter->addWidget(action_list_);
121  splitter->addWidget(action_label_);
122 
123  // Add Layout
124  layout->addWidget(splitter);
125 
126  // Progress bar and generate buttons ---------------------------------------------------
127  QHBoxLayout* hlayout1 = new QHBoxLayout();
128 
129  // Progress Bar
130  progress_bar_ = new QProgressBar(this);
131  progress_bar_->setMaximum(100);
132  progress_bar_->setMinimum(0);
133  hlayout1->addWidget(progress_bar_);
134  // hlayout1->setContentsMargins( 20, 30, 20, 30 );
135 
136  // Generate Package Button
137  btn_save_ = new QPushButton("&Generate Package", this);
138  // btn_save_->setMinimumWidth(180);
139  btn_save_->setMinimumHeight(40);
140  connect(btn_save_, SIGNAL(clicked()), this, SLOT(savePackage()));
141  hlayout1->addWidget(btn_save_);
142 
143  // Add Layout
144  layout->addLayout(hlayout1);
145 
146  // Bottom row --------------------------------------------------
147 
148  QHBoxLayout* hlayout3 = new QHBoxLayout();
149 
150  // Success label
151  success_label_ = new QLabel(this);
152  QFont success_label_font(QFont().defaultFamily(), 12, QFont::Bold);
153  success_label_->setFont(success_label_font);
154  success_label_->hide(); // only show once the files have been generated
155  success_label_->setText("Configuration package generated successfully!");
156  hlayout3->addWidget(success_label_);
157  hlayout3->setAlignment(success_label_, Qt::AlignRight);
158 
159  // Exit button
160  QPushButton* btn_exit = new QPushButton("E&xit Setup Assistant", this);
161  btn_exit->setMinimumWidth(180);
162  connect(btn_exit, SIGNAL(clicked()), this, SLOT(exitSetupAssistant()));
163  hlayout3->addWidget(btn_exit);
164  hlayout3->setAlignment(btn_exit, Qt::AlignRight);
165 
166  layout->addLayout(hlayout3);
167 
168  // Finish Layout --------------------------------------------------
169  this->setLayout(layout);
170 }
171 
172 // ******************************************************************************************
173 // Populate the 'Files to be generated' list
174 // ******************************************************************************************
176 {
177  GenerateFile file; // re-used
178  std::string template_path; // re-used
179  const std::string robot_name = config_data_->srdf_->robot_name_;
180 
181  gen_files_.clear(); // reset vector
182 
183  // Get template package location ----------------------------------------------------------------------
184  fs::path template_package_path = config_data_->setup_assistant_path_;
185  template_package_path /= "templates";
186  template_package_path /= "moveit_config_pkg_template";
187  config_data_->template_package_path_ = template_package_path.make_preferred().native().c_str();
188 
189  if (!fs::is_directory(config_data_->template_package_path_))
190  {
191  QMessageBox::critical(
192  this, "Error Generating",
193  QString("Unable to find package template directory: ").append(config_data_->template_package_path_.c_str()));
194  return false;
195  }
196 
197  // -------------------------------------------------------------------------------------------------------------------
198  // ROS PACKAGE FILES AND FOLDERS ----------------------------------------------------------------------------
199  // -------------------------------------------------------------------------------------------------------------------
200 
201  // package.xml --------------------------------------------------------------------------------------
202  // Note: we call the file package.xml.template so that it isn't automatically indexed by rosprofile
203  // in the scenario where we want to disabled the setup_assistant by renaming its root package.xml
204  file.file_name_ = "package.xml";
205  file.rel_path_ = file.file_name_;
206  template_path = config_data_->appendPaths(config_data_->template_package_path_, "package.xml.template");
207  file.description_ = "Defines a ROS package";
208  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
210  gen_files_.push_back(file);
211 
212  // CMakeLists.txt --------------------------------------------------------------------------------------
213  file.file_name_ = "CMakeLists.txt";
214  file.rel_path_ = file.file_name_;
215  template_path = config_data_->appendPaths(config_data_->template_package_path_, file.file_name_);
216  file.description_ = "CMake build system configuration file";
217  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
218  file.write_on_changes = 0;
219  gen_files_.push_back(file);
220 
221  // -------------------------------------------------------------------------------------------------------------------
222  // CONIG FILES -------------------------------------------------------------------------------------------------------
223  // -------------------------------------------------------------------------------------------------------------------
224  std::string config_path = "config";
225 
226  // config/ --------------------------------------------------------------------------------------
227  file.file_name_ = "config/";
228  file.rel_path_ = file.file_name_;
229  file.description_ = "Folder containing all MoveIt! configuration files for your robot. This folder is required and "
230  "cannot be disabled.";
231  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::createFolder, this, _1);
232  file.write_on_changes = 0;
233  gen_files_.push_back(file);
234 
235  // robot.srdf ----------------------------------------------------------------------------------------------
236  file.file_name_ = config_data_->srdf_pkg_relative_path_.empty() ? config_data_->urdf_model_->getName() + ".srdf" :
237  config_data_->srdf_pkg_relative_path_;
238  file.rel_path_ = config_data_->srdf_pkg_relative_path_.empty() ?
239  config_data_->appendPaths(config_path, file.file_name_) :
240  config_data_->srdf_pkg_relative_path_;
241  file.description_ = "SRDF (<a href='http://www.ros.org/wiki/srdf'>Semantic Robot Description Format</a>) is a "
242  "representation of semantic information about robots. This format is intended to represent "
243  "information about the robot that is not in the URDF file, but it is useful for a variety of "
244  "applications. The intention is to include information that has a semantic aspect to it.";
245  file.gen_func_ = boost::bind(&srdf::SRDFWriter::writeSRDF, config_data_->srdf_, _1);
247  gen_files_.push_back(file);
248  // special step required so the generated .setup_assistant yaml has this value
249  config_data_->srdf_pkg_relative_path_ = file.rel_path_;
250 
251  // ompl_planning.yaml --------------------------------------------------------------------------------------
252  file.file_name_ = "ompl_planning.yaml";
253  file.rel_path_ = config_data_->appendPaths(config_path, file.file_name_);
254  file.description_ = "Configures the OMPL (<a href='http://ompl.kavrakilab.org/'>Open Motion Planning Library</a>) "
255  "planning plugin. For every planning group defined in the SRDF, a number of planning "
256  "configurations are specified (under planner_configs). Additionally, default settings for the "
257  "state space to plan in for a particular group can be specified, such as the collision checking "
258  "resolution. Each planning configuration specified for a group must be defined under the "
259  "planner_configs tag. While defining a planner configuration, the only mandatory parameter is "
260  "'type', which is the name of the motion planner to be used. Any other planner-specific "
261  "parameters can be defined but are optional.";
264  gen_files_.push_back(file);
265 
266  // chomp_planning.yaml --------------------------------------------------------------------------------------
267  file.file_name_ = "chomp_planning.yaml";
268  file.rel_path_ = config_data_->appendPaths(config_path, file.file_name_);
269  file.description_ = "Specifies which chomp planning plugin parameters to be used for the CHOMP planner";
271  file.write_on_changes = MoveItConfigData::GROUPS; // need to double check if this is actually correct!
272  gen_files_.push_back(file);
273 
274  // kinematics.yaml --------------------------------------------------------------------------------------
275  file.file_name_ = "kinematics.yaml";
276  file.rel_path_ = config_data_->appendPaths(config_path, file.file_name_);
277  file.description_ = "Specifies which kinematic solver plugin to use for each planning group in the SRDF, as well as "
278  "the kinematic solver search resolution.";
281  gen_files_.push_back(file);
282 
283  // joint_limits.yaml --------------------------------------------------------------------------------------
284  file.file_name_ = "joint_limits.yaml";
285  file.rel_path_ = config_data_->appendPaths(config_path, file.file_name_);
286  file.description_ = "Contains additional information about joints that appear in your planning groups that is not "
287  "contained in the URDF, as well as allowing you to set maximum and minimum limits for velocity "
288  "and acceleration than those contained in your URDF. This information is used by our trajectory "
289  "filtering system to assign reasonable velocities and timing for the trajectory before it is "
290  "passed to the robots controllers.";
292  file.write_on_changes = 0; // Can they be changed?
293  gen_files_.push_back(file);
294 
295  // fake_controllers.yaml --------------------------------------------------------------------------------------
296  file.file_name_ = "fake_controllers.yaml";
297  file.rel_path_ = config_data_->appendPaths(config_path, file.file_name_);
298  file.description_ = "Creates dummy configurations for controllers that correspond to defined groups. This is mostly "
299  "useful for testing.";
302  gen_files_.push_back(file);
303 
304  // ros_controllers.yaml --------------------------------------------------------------------------------------
305  file.file_name_ = "ros_controllers.yaml";
306  file.rel_path_ = config_data_->appendPaths(config_path, file.file_name_);
307  file.description_ = "Creates configurations for ros_controllers.";
310  gen_files_.push_back(file);
311 
312  // sensors_3d.yaml --------------------------------------------------------------------------------------
313  file.file_name_ = "sensors_3d.yaml";
314  file.rel_path_ = config_data_->appendPaths(config_path, file.file_name_);
315  file.description_ = "Creates configurations 3d sensors.";
318  gen_files_.push_back(file);
319 
320  // -------------------------------------------------------------------------------------------------------------------
321  // LAUNCH FILES ------------------------------------------------------------------------------------------------------
322  // -------------------------------------------------------------------------------------------------------------------
323  std::string launch_path = "launch";
324  const std::string template_launch_path = config_data_->appendPaths(config_data_->template_package_path_, launch_path);
325 
326  // launch/ --------------------------------------------------------------------------------------
327  file.file_name_ = "launch/";
328  file.rel_path_ = file.file_name_;
329  file.description_ =
330  "Folder containing all MoveIt! launch files for your robot. This folder is required and cannot be "
331  "disabled.";
332  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::createFolder, this, _1);
333  file.write_on_changes = 0;
334  gen_files_.push_back(file);
335 
336  // move_group.launch --------------------------------------------------------------------------------------
337  file.file_name_ = "move_group.launch";
338  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
339  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
340  file.description_ = "Launches the move_group node that provides the MoveGroup action and other parameters <a "
341  "href='http://moveit.ros.org/doxygen/"
342  "classmoveit_1_1planning__interface_1_1MoveGroup.html#details'>MoveGroup action</a>";
343  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
344  file.write_on_changes = 0;
345  gen_files_.push_back(file);
346 
347  // planning_context.launch --------------------------------------------------------------------------------------
348  file.file_name_ = "planning_context.launch";
349  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
350  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
351  file.description_ = "Loads settings for the ROS parameter server, required for running MoveIt. This includes the "
352  "SRDF, joints_limits.yaml file, ompl_planning.yaml file, optionally the URDF, etc";
353  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
354  file.write_on_changes = 0;
355  gen_files_.push_back(file);
356 
357  // moveit_rviz.launch --------------------------------------------------------------------------------------
358  file.file_name_ = "moveit_rviz.launch";
359  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
360  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
361  file.description_ = "Visualize in Rviz the robot's planning groups running with interactive markers that allow goal "
362  "states to be set.";
363  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
364  file.write_on_changes = 0;
365  gen_files_.push_back(file);
366 
367  // ompl_planning_pipeline.launch
368  // --------------------------------------------------------------------------------------
369  file.file_name_ = "ompl_planning_pipeline.launch.xml";
370  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
371  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
372  file.description_ = "Intended to be included in other launch files that require the OMPL planning plugin. Defines "
373  "the proper plugin name on the parameter server and a default selection of planning request "
374  "adapters.";
375  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
376  file.write_on_changes = 0;
377  gen_files_.push_back(file);
378 
379  // chomp_planning_pipeline.launch
380  // --------------------------------------------------------------------------------------
381  file.file_name_ = "chomp_planning_pipeline.launch.xml";
382  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
383  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
384  file.description_ = "Intended to be included in other launch files that require the CHOMP planning plugin. Defines "
385  "the proper plugin name on the parameter server and a default selection of planning request "
386  "adapters.";
387  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
388  file.write_on_changes = 0;
389  gen_files_.push_back(file);
390 
391  // planning_pipeline.launch --------------------------------------------------------------------------------------
392  file.file_name_ = "planning_pipeline.launch.xml";
393  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
394  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
395  file.description_ = "Helper launch file that can choose between different planning pipelines to be loaded.";
396  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
397  file.write_on_changes = 0;
398  gen_files_.push_back(file);
399 
400  // warehouse_settings.launch --------------------------------------------------------------------------------------
401  file.file_name_ = "warehouse_settings.launch.xml";
402  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
403  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
404  file.description_ = "Helper launch file that specifies default settings for MongoDB.";
405  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
406  file.write_on_changes = 0;
407  gen_files_.push_back(file);
408 
409  // warehouse.launch --------------------------------------------------------------------------------------
410  file.file_name_ = "warehouse.launch";
411  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
412  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
413  file.description_ = "Launch file for starting MongoDB.";
414  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
415  file.write_on_changes = 0;
416  gen_files_.push_back(file);
417 
418  // default_warehouse_db.launch --------------------------------------------------------------------------------------
419  file.file_name_ = "default_warehouse_db.launch";
420  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
421  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
422  file.description_ = "Launch file for starting the warehouse with a default MongoDB.";
423  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
424  file.write_on_changes = 0;
425  gen_files_.push_back(file);
426 
427  // run_benchmark_ompl.launch --------------------------------------------------------------------------------------
428  file.file_name_ = "run_benchmark_ompl.launch";
429  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
430  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
431  file.description_ = "Launch file for benchmarking OMPL planners";
432  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
433  file.write_on_changes = 0;
434  gen_files_.push_back(file);
435 
436  // sensor_manager.launch --------------------------------------------------------------------------------------
437  file.file_name_ = "sensor_manager.launch.xml";
438  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
439  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
440  file.description_ = "Helper launch file that can choose between different sensor managers to be loaded.";
441  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
442  file.write_on_changes = 0;
443  gen_files_.push_back(file);
444 
445  // robot_moveit_controller_manager.launch ------------------------------------------------------------------
446  file.file_name_ = robot_name + "_moveit_controller_manager.launch.xml";
447  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
448  template_path = config_data_->appendPaths(template_launch_path, "moveit_controller_manager.launch.xml");
449  file.description_ = "Placeholder for settings specific to the MoveIt! controller manager implemented for you robot.";
450  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
451  file.write_on_changes = 0;
452  gen_files_.push_back(file);
453 
454  // robot_moveit_sensor_manager.launch ------------------------------------------------------------------
455  file.file_name_ = robot_name + "_moveit_sensor_manager.launch.xml";
456  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
457  template_path = config_data_->appendPaths(template_launch_path, "moveit_sensor_manager.launch.xml");
458  file.description_ = "Placeholder for settings specific to the MoveIt! sensor manager implemented for you robot.";
459  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
460  file.write_on_changes = 0;
461  gen_files_.push_back(file);
462 
463  // trajectory_execution.launch ------------------------------------------------------------------
464  file.file_name_ = "trajectory_execution.launch.xml";
465  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
466  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
467  file.description_ = "Loads settings for the ROS parameter server required for executing trajectories using the "
468  "trajectory_execution_manager::TrajectoryExecutionManager.";
469  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
470  file.write_on_changes = 0;
471  gen_files_.push_back(
472  file); // trajectory_execution.launch ------------------------------------------------------------------
473 
474  file.file_name_ = "fake_moveit_controller_manager.launch.xml";
475  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
476  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
477  file.description_ = "Loads a fake controller plugin.";
478  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
479  file.write_on_changes = 0;
480  gen_files_.push_back(file);
481 
482  // demo.launch ------------------------------------------------------------------
483  file.file_name_ = "demo.launch";
484  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
485  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
486  file.description_ = "Run a demo of MoveIt.";
487  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
488  file.write_on_changes = 0;
489  gen_files_.push_back(file);
490 
491  // gazebo.launch ------------------------------------------------------------------
492  file.file_name_ = "gazebo.launch";
493  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
494  template_path = config_data_->appendPaths(template_launch_path, "gazebo.launch");
495  file.description_ = "Gazebo launch file which also launches ros_controllers and sends robot urdf to param server, "
496  "then using gazebo_ros pkg the robot is spawned to Gazebo";
497  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
498  gen_files_.push_back(file);
499 
500  // demo_gazebo.launch ------------------------------------------------------------------
501  file.file_name_ = "demo_gazebo.launch";
502  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
503  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
504  file.description_ = "Run a demo of MoveIt with Gazebo and Rviz";
505  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
506  file.write_on_changes = 0;
507  gen_files_.push_back(file);
508 
509  // joystick_control.launch ------------------------------------------------------------------
510  file.file_name_ = "joystick_control.launch";
511  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
512  template_path = config_data_->appendPaths(template_launch_path, file.file_name_);
513  file.description_ = "Control the Rviz Motion Planning Plugin with a joystick";
514  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
515  file.write_on_changes = 0;
516  gen_files_.push_back(file);
517 
518  // setup_assistant.launch ------------------------------------------------------------------
519  file.file_name_ = "setup_assistant.launch";
520  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
521  template_path = config_data_->appendPaths(
522  template_launch_path, "edit_configuration_package.launch"); // named this so that this launch file is not mixed
523  // up with the SA's real launch file
524  file.description_ = "Launch file for easily re-starting the MoveIt! Setup Assistant to edit this robot's generated "
525  "configuration package.";
526  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
527  file.write_on_changes = 0;
528  gen_files_.push_back(file);
529 
530  // ros_controllers.launch ------------------------------------------------------------------
531  file.file_name_ = "ros_controllers.launch";
532  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
533  template_path = config_data_->appendPaths(template_launch_path, "ros_controllers.launch");
534  file.description_ = "ros_controllers launch file";
535  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
537  gen_files_.push_back(file);
538 
539  // moveit.rviz ------------------------------------------------------------------
540  file.file_name_ = "moveit.rviz";
541  file.rel_path_ = config_data_->appendPaths(launch_path, file.file_name_);
542  template_path = config_data_->appendPaths(template_launch_path, "moveit.rviz");
543  file.description_ = "Configuration file for Rviz with the Motion Planning Plugin already setup. Used by passing "
544  "roslaunch moveit_rviz.launch config:=true";
545  file.gen_func_ = boost::bind(&ConfigurationFilesWidget::copyTemplate, this, template_path, _1);
546  file.write_on_changes = 0;
547  gen_files_.push_back(file);
548 
549  // -------------------------------------------------------------------------------------------------------------------
550  // OTHER FILES -------------------------------------------------------------------------------------------------------
551  // -------------------------------------------------------------------------------------------------------------------
552 
553  // .setup_assistant ------------------------------------------------------------------
555  file.rel_path_ = file.file_name_;
556  file.description_ = "MoveIt! Setup Assistant's hidden settings file. You should not need to edit this file.";
558  file.write_on_changes = -1; // write on any changes
559  gen_files_.push_back(file);
560 
561  return true;
562 }
563 
564 // ******************************************************************************************
565 // Verify with user if certain screens have not been completed
566 // ******************************************************************************************
568 {
569  QStringList dependencies;
570  bool requiredActions = false;
571 
572  // Check that at least 1 planning group exists
573  if (!config_data_->srdf_->groups_.size())
574  {
575  dependencies << "No robot model planning groups have been created";
576  }
577 
578  // Check that at least 1 link pair is disabled from collision checking
579  if (!config_data_->srdf_->disabled_collisions_.size())
580  {
581  dependencies << "No self-collisions have been disabled";
582  }
583 
584  // Check that there is at least 1 end effector added
585  if (!config_data_->srdf_->end_effectors_.size())
586  {
587  dependencies << "No end effectors have been added";
588  }
589 
590  // Check that there is at least 1 virtual joint added
591  if (!config_data_->srdf_->virtual_joints_.size())
592  {
593  dependencies << "No virtual joints have been added";
594  }
595 
596  // Check that there is a author name
597  if (config_data_->author_name_.find_first_not_of(' ') == std::string::npos)
598  {
599  // There is no name or it consists of whitespaces only
600  dependencies << "<b>No author name added</b>";
601  requiredActions = true;
602  }
603 
604  // Check that email information is filled
605  QRegExp mailRegex("\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b");
606  mailRegex.setCaseSensitivity(Qt::CaseInsensitive);
607  mailRegex.setPatternSyntax(QRegExp::RegExp);
608  QString testEmail = QString::fromStdString(config_data_->author_email_);
609  if (!mailRegex.exactMatch(testEmail))
610  {
611  dependencies << "<b>No valid email address added</b>";
612  requiredActions = true;
613  }
614 
615  // Display all accumumlated errors:
616  if (dependencies.size())
617  {
618  // Create a dependency message
619  QString dep_message;
620  if (!requiredActions)
621  {
622  dep_message = "Some setup steps have not been completed. None of the steps are required, but here is a reminder "
623  "of what was not filled in, just in case something was forgotten:<br /><ul>";
624  }
625  else
626  {
627  dep_message = "Some setup steps have not been completed. Please fix the required steps (printed in bold), "
628  "otherwise the setup cannot be completed:<br /><ul>";
629  }
630 
631  for (int i = 0; i < dependencies.size(); ++i)
632  {
633  dep_message.append("<li>").append(dependencies.at(i)).append("</li>");
634  }
635 
636  if (!requiredActions)
637  {
638  dep_message.append("</ul><br/>Press Ok to continue generating files.");
639  if (QMessageBox::question(this, "Incomplete MoveIt! Setup Assistant Steps", dep_message,
640  QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
641  {
642  return false; // abort
643  }
644  }
645  else
646  {
647  QMessageBox::warning(this, "Incomplete MoveIt! Setup Assistant Steps", dep_message);
648  return false;
649  }
650  }
651 
652  return true;
653 }
654 
655 // ******************************************************************************************
656 // A function for showing progress and user feedback about what happened
657 // ******************************************************************************************
659 {
660  action_num_++;
661 
662  // Calc percentage
663  progress_bar_->setValue(double(action_num_) / gen_files_.size() * 100);
664 
665  // allow the progress bar to be shown
666  QApplication::processEvents();
667 }
668 
669 // ******************************************************************************************
670 // Display the selected action in the desc box
671 // ******************************************************************************************
673 {
674  // Only allow event if list is not empty
675  if (id >= 0)
676  {
677  // Show the selected text
678  action_label_->setText(action_desc_.at(id));
679  }
680 }
681 
682 // ******************************************************************************************
683 // Disable or enable item in gen_files_ array
684 // ******************************************************************************************
686 {
687  std::size_t index = item->data(Qt::UserRole).toUInt();
688  bool generate = (item->checkState() == Qt::Checked);
689 
690  if (!generate && (gen_files_[index].write_on_changes & config_data_->changes))
691  {
692  QMessageBox::warning(this, "Package Generation", "You should generate this file to ensure your changes will take "
693  "effect.");
694  }
695 
696  // Enable/disable file
697  gen_files_[index].generate_ = generate;
698 }
699 
700 // ******************************************************************************************
701 // Called when setup assistant navigation switches to this screen
702 // ******************************************************************************************
704 {
705  if (first_focusGiven_)
706  {
707  // only generate list once
708  first_focusGiven_ = false;
709 
710  // Load this list of all files to be generated
711  loadGenFiles();
712  }
713 
714  // Which files have been modified outside the Setup Assistant?
715  bool files_already_modified = checkGenFiles();
716 
717  // disable reaction to checkbox changes
718  disconnect(action_list_, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(changeCheckedState(QListWidgetItem*)));
719 
720  // Show files in GUI
721  bool have_conflicting_changes = showGenFiles();
722 
723  // react to manual changes only (not programmatic ones)
724  connect(action_list_, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(changeCheckedState(QListWidgetItem*)));
725 
726  // Allow list box to populate
727  QApplication::processEvents();
728 
729  if (files_already_modified)
730  {
731  // Some were found to be modified
732  QString msg("Some files have been modified outside of the Setup Assistant (according to timestamp). "
733  "The Setup Assistant will not overwrite these changes by default because often changing configuration "
734  "files manually is necessary, "
735  "but we recommend you check the list and enable the checkbox next to files you would like to "
736  "overwrite. ");
737  if (have_conflicting_changes)
738  msg += "<br/><font color='red'>Attention:</font> Some files (<font color='red'>marked red</font>) are changed "
739  "both, externally and in Setup Assistant.";
740  QMessageBox::information(this, "Files Modified", msg);
741  }
742 }
743 
744 // ******************************************************************************************
745 // Check the list of files to be generated for modification
746 // Returns true if files were detected as modified
747 // ******************************************************************************************
749 {
750  // Check if we are 'editing' a prev generated config pkg
751  if (config_data_->config_pkg_path_.empty())
752  return false; // this is a new package
753 
754  // Check if we have the previous modification timestamp to compare agains
755  if (config_data_->config_pkg_generated_timestamp_ == 0)
756  return false; // this package has not been generated with a timestamp, backwards compatible.
757 
758  static const std::time_t TIME_MOD_TOLERANCE = 10;
759 
760  // Check all old file's modification time
761  bool found_modified = false;
762  for (std::size_t i = 0; i < gen_files_.size(); ++i)
763  {
764  GenerateFile* file = &gen_files_[i];
765 
766  fs::path file_path = config_data_->appendPaths(config_data_->config_pkg_path_, file->rel_path_);
767 
768  // Don't disable folders from being generated
769  if (fs::is_directory(file_path))
770  continue;
771 
772  if (fs::is_regular_file(file_path))
773  {
774  std::time_t mod_time = fs::last_write_time(file_path);
775 
776  // ROS_DEBUG_STREAM("File " << file->file_name_ << " file modified " << mod_time << " pkg modified " <<
777  // config_data_->config_pkg_generated_timestamp_);
778 
779  if (mod_time > config_data_->config_pkg_generated_timestamp_ + TIME_MOD_TOLERANCE ||
780  mod_time < config_data_->config_pkg_generated_timestamp_ - TIME_MOD_TOLERANCE)
781  {
782  ROS_INFO_STREAM("Manual editing detected: not over-writing by default file " << file->file_name_);
783 
784  if (file->write_on_changes & config_data_->changes)
785  ROS_WARN_STREAM("Editing in Setup Assistant conflicts with external editing of file " << file->file_name_);
786 
787  file->generate_ = false; // do not overwrite by default
788  file->modified_ = true;
789  found_modified = true;
790  }
791  else
792  {
793  file->modified_ = false;
794  }
795  }
796  }
797 
798  // Warn user if files have been modified outside Setup Assistant
799  return found_modified;
800 }
801 
802 // ******************************************************************************************
803 // Show the list of files to be generated
804 // ******************************************************************************************
806 {
807  bool have_conflicting_changes = false;
808  action_list_->clear();
809 
810  // Display this list in the GUI
811  for (std::size_t i = 0; i < gen_files_.size(); ++i)
812  {
813  GenerateFile* file = &gen_files_[i];
814 
815  // Create a formatted row
816  QListWidgetItem* item = new QListWidgetItem(QString(file->rel_path_.c_str()), action_list_, 0);
817 
818  fs::path file_path = config_data_->appendPaths(config_data_->config_pkg_path_, file->rel_path_);
819 
820  // Checkbox
821  item->setCheckState(file->generate_ ? Qt::Checked : Qt::Unchecked);
822  // externally modified?
823  if (file->modified_)
824  {
825  if (file->write_on_changes & config_data_->changes)
826  {
827  have_conflicting_changes = true;
828  item->setForeground(QBrush(QColor(255, 0, 0)));
829  }
830  else
831  item->setForeground(QBrush(QColor(255, 135, 0)));
832  }
833 
834  // Don't allow folders to be disabled
835  if (fs::is_directory(file_path))
836  {
837  item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
838  }
839 
840  // Link the gen_files_ index to this item
841  item->setData(Qt::UserRole, QVariant(static_cast<qulonglong>(i)));
842 
843  // Add actions to list
844  action_list_->addItem(item);
845  action_desc_.append(QString(file->description_.c_str()));
846  }
847 
848  // Select the first item in the list so that a description is visible
849  action_list_->setCurrentRow(0);
850  return have_conflicting_changes;
851 }
852 
853 // ******************************************************************************************
854 // Save configuration click event
855 // ******************************************************************************************
857 {
858  // Feedback
859  success_label_->hide();
860 
861  // Reset the progress bar counter and GUI stuff
862  action_num_ = 0;
863  progress_bar_->setValue(0);
864 
865  if (!generatePackage())
866  {
867  ROS_ERROR_STREAM("Failed to generate entire configuration package");
868  return;
869  }
870 
871  // Alert user it completed successfully --------------------------------------------------
872  progress_bar_->setValue(100);
873  success_label_->show();
874  has_generated_pkg_ = true;
875 }
876 
877 // ******************************************************************************************
878 // Save package using default path
879 // ******************************************************************************************
881 {
882  // Get path name
883  std::string new_package_path = stack_path_->getPath();
884 
885  // Check that a valid stack package name has been given
886  if (new_package_path.empty())
887  {
888  QMessageBox::warning(this, "Error Generating", "No package path provided. Please choose a directory location to "
889  "generate the MoveIt! configuration files.");
890  return false;
891  }
892 
893  // Check setup assist deps
894  if (!checkDependencies())
895  return false; // canceled
896 
897  // Check that all groups have components
898  if (!noGroupsEmpty())
899  return false; // not ready
900 
901  // Trim whitespace from user input
902  boost::trim(new_package_path);
903 
904  // Get the package name ---------------------------------------------------------------------------------
905  new_package_name_ = getPackageName(new_package_path);
906 
907  const std::string setup_assistant_file = config_data_->appendPaths(new_package_path, SETUP_ASSISTANT_FILE);
908 
909  // Make sure old package is correct package type and verify over write
910  if (fs::is_directory(new_package_path) && !fs::is_empty(new_package_path))
911  {
912  // Check if the old package is a setup assistant package. If it is not, quit
913  if (!fs::is_regular_file(setup_assistant_file))
914  {
915  QMessageBox::warning(
916  this, "Incorrect Folder/Package",
917  QString("The chosen package location already exists but was not previously created using this MoveIt! Setup "
918  "Assistant. "
919  "If this is a mistake, add the missing file: ")
920  .append(setup_assistant_file.c_str()));
921  return false;
922  }
923 
924  // Confirm overwrite
925  if (QMessageBox::question(
926  this, "Confirm Package Update",
927  QString("Are you sure you want to overwrite this existing package with updated configurations?<br /><i>")
928  .append(new_package_path.c_str())
929  .append("</i>"),
930  QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
931  {
932  return false; // abort
933  }
934  }
935  else // this is a new package (but maybe the folder already exists)
936  {
937  // Create new directory ------------------------------------------------------------------
938  try
939  {
940  fs::create_directory(new_package_path) && !fs::is_directory(new_package_path);
941  }
942  catch (...)
943  {
944  QMessageBox::critical(this, "Error Generating Files",
945  QString("Unable to create directory ").append(new_package_path.c_str()));
946  return false;
947  }
948  }
949 
950  // Begin to create files and folders ----------------------------------------------------------------------
951  std::string absolute_path;
952 
953  for (std::size_t i = 0; i < gen_files_.size(); ++i)
954  {
955  GenerateFile* file = &gen_files_[i];
956 
957  // Check if we should skip this file
958  if (!file->generate_)
959  {
960  continue;
961  }
962 
963  // Create the absolute path
964  absolute_path = config_data_->appendPaths(new_package_path, file->rel_path_);
965  ROS_DEBUG_STREAM("Creating file " << absolute_path);
966 
967  // Run the generate function
968  if (!file->gen_func_(absolute_path))
969  {
970  // Error occured
971  QMessageBox::critical(this, "Error Generating File", QString("Failed to generate folder or file: '")
972  .append(file->rel_path_.c_str())
973  .append("' at location:\n")
974  .append(absolute_path.c_str()));
975  return false;
976  }
977  updateProgress(); // Increment and update GUI
978  }
979 
980  return true;
981 }
982 
983 // ******************************************************************************************
984 // Quit the program because we are done
985 // ******************************************************************************************
987 {
988  if (has_generated_pkg_ ||
989  QMessageBox::question(this, "Exit Setup Assistant",
990  QString("Are you sure you want to exit the MoveIt! Setup Assistant?"),
991  QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok)
992  {
993  QApplication::quit();
994  }
995 }
996 
997 // ******************************************************************************************
998 // Get the last folder name in a directory path
999 // ******************************************************************************************
1000 const std::string ConfigurationFilesWidget::getPackageName(std::string package_path)
1001 {
1002  // Remove end slash if there is one
1003  if (!package_path.compare(package_path.size() - 1, 1, "/"))
1004  {
1005  package_path = package_path.substr(0, package_path.size() - 1);
1006  }
1007 
1008  // Get the last directory name
1009  std::string package_name;
1010  fs::path fs_package_path = package_path;
1011 
1012  package_name = fs_package_path.filename().c_str();
1013 
1014  // check for empty
1015  if (package_name.empty())
1016  package_name = "unknown";
1017 
1018  return package_name;
1019 }
1020 
1021 // ******************************************************************************************
1022 // Check that no group is empty (without links/joints/etc)
1023 // ******************************************************************************************
1025 {
1026  // Loop through all groups
1027  for (std::vector<srdf::Model::Group>::const_iterator group_it = config_data_->srdf_->groups_.begin();
1028  group_it != config_data_->srdf_->groups_.end(); ++group_it)
1029  {
1030  // Whenever 1 of the 4 component types are found, stop checking this group
1031  if (group_it->joints_.size())
1032  continue;
1033  if (group_it->links_.size())
1034  continue;
1035  if (group_it->chains_.size())
1036  continue;
1037  if (group_it->subgroups_.size())
1038  continue;
1039 
1040  // This group has no contents, bad
1041  QMessageBox::warning(
1042  this, "Empty Group",
1043  QString("The planning group '")
1044  .append(group_it->name_.c_str())
1045  .append("' is empty and has no subcomponents associated with it (joints/links/chains/subgroups). You must "
1046  "edit or remove this planning group before this configuration package can be saved."));
1047  return false;
1048  }
1049 
1050  return true; // good
1051 }
1052 
1053 // ******************************************************************************************
1054 // Load the strings that will be replaced in all templates
1055 // ******************************************************************************************
1057 {
1058  // Pair 1
1059  addTemplateString("[GENERATED_PACKAGE_NAME]", new_package_name_);
1060 
1061  // Pair 2
1062  std::string urdf_location = config_data_->urdf_pkg_name_.empty() ? config_data_->urdf_path_ :
1063  "$(find " + config_data_->urdf_pkg_name_ + ")/" +
1064  config_data_->urdf_pkg_relative_path_;
1065  addTemplateString("[URDF_LOCATION]", urdf_location);
1066 
1067  // Pair 3
1068  if (config_data_->urdf_from_xacro_)
1069  addTemplateString("[URDF_LOAD_ATTRIBUTE]",
1070  "command=\"xacro " + config_data_->xacro_args_ + " '" + urdf_location + "'\"");
1071  else
1072  addTemplateString("[URDF_LOAD_ATTRIBUTE]", "textfile=\"" + urdf_location + "\"");
1073 
1074  // Pair 4
1075  addTemplateString("[ROBOT_NAME]", config_data_->srdf_->robot_name_);
1076 
1077  // Pair 5
1078  addTemplateString("[ROBOT_ROOT_LINK]", config_data_->getRobotModel()->getRootLinkName());
1079 
1080  // Pair 6
1081  addTemplateString("[PLANNING_FRAME]", config_data_->getRobotModel()->getModelFrame());
1082 
1083  // Pair 7
1084  std::stringstream vjb;
1085  for (std::size_t i = 0; i < config_data_->srdf_->virtual_joints_.size(); ++i)
1086  {
1087  const srdf::Model::VirtualJoint& vj = config_data_->srdf_->virtual_joints_[i];
1088  if (vj.type_ != "fixed")
1089  vjb << " <node pkg=\"tf\" type=\"static_transform_publisher\" name=\"virtual_joint_broadcaster_" << i
1090  << "\" args=\"0 0 0 0 0 0 " << vj.parent_frame_ << " " << vj.child_link_ << " 100\" />" << std::endl;
1091  }
1092  addTemplateString("[VIRTUAL_JOINT_BROADCASTER]", vjb.str());
1093 
1094  // Pair 8 - Add dependencies to package.xml if the robot.urdf file is relative to a ROS package
1095  if (config_data_->urdf_pkg_name_.empty())
1096  {
1097  addTemplateString("[OTHER_DEPENDENCIES", ""); // not relative to a ROS package
1098  }
1099  else
1100  {
1101  std::stringstream deps;
1102  deps << "<build_depend>" << config_data_->urdf_pkg_name_ << "</build_depend>\n";
1103  deps << " <run_depend>" << config_data_->urdf_pkg_name_ << "</run_depend>\n";
1104  addTemplateString("[OTHER_DEPENDENCIES]", deps.str()); // not relative to a ROS package
1105  }
1106 
1107  // Pair 9 - Add ROS Controllers to ros_controllers.launch file
1108  if (config_data_->getROSControllers().empty())
1109  {
1110  addTemplateString("[ROS_CONTROLLERS]", "");
1111  }
1112  else
1113  {
1114  std::stringstream controllers;
1115  for (std::vector<ROSControlConfig>::iterator controller_it = config_data_->getROSControllers().begin();
1116  controller_it != config_data_->getROSControllers().end(); ++controller_it)
1117  {
1118  // Check if the controller belongs to controller_list namespace
1119  if (controller_it->type_ != "FollowJointTrajectory")
1120  controllers << controller_it->name_ << " ";
1121  }
1122  addTemplateString("[ROS_CONTROLLERS]", controllers.str());
1123  }
1124 
1125  addTemplateString("[AUTHOR_NAME]", config_data_->author_name_);
1126  addTemplateString("[AUTHOR_EMAIL]", config_data_->author_email_);
1127 }
1128 
1129 // ******************************************************************************************
1130 // Insert a string pair into the template_strings_ datastructure
1131 // ******************************************************************************************
1132 bool ConfigurationFilesWidget::addTemplateString(const std::string& key, const std::string& value)
1133 {
1134  template_strings_.push_back(std::pair<std::string, std::string>(key, value));
1135 
1136  return true;
1137 }
1138 
1139 // ******************************************************************************************
1140 // Copy a template from location <template_path> to location <output_path> and replace package name
1141 // ******************************************************************************************
1142 bool ConfigurationFilesWidget::copyTemplate(const std::string& template_path, const std::string& output_path)
1143 {
1144  // Check if template strings have been loaded yet
1145  if (template_strings_.empty())
1146  {
1148  }
1149 
1150  // Error check file
1151  if (!fs::is_regular_file(template_path))
1152  {
1153  ROS_ERROR_STREAM("Unable to find template file " << template_path);
1154  return false;
1155  }
1156 
1157  // Load file
1158  std::ifstream template_stream(template_path.c_str());
1159  if (!template_stream.good()) // File not found
1160  {
1161  ROS_ERROR_STREAM("Unable to load file " << template_path);
1162  return false;
1163  }
1164 
1165  // Load the file to a string using an efficient memory allocation technique
1166  std::string template_string;
1167  template_stream.seekg(0, std::ios::end);
1168  template_string.reserve(template_stream.tellg());
1169  template_stream.seekg(0, std::ios::beg);
1170  template_string.assign((std::istreambuf_iterator<char>(template_stream)), std::istreambuf_iterator<char>());
1171  template_stream.close();
1172 
1173  // Replace keywords in string ------------------------------------------------------------
1174  for (std::size_t i = 0; i < template_strings_.size(); ++i)
1175  {
1176  boost::replace_all(template_string, template_strings_[i].first, template_strings_[i].second);
1177  }
1178 
1179  // Save string to new location -----------------------------------------------------------
1180  std::ofstream output_stream(output_path.c_str(), std::ios_base::trunc);
1181  if (!output_stream.good())
1182  {
1183  ROS_ERROR_STREAM("Unable to open file for writing " << output_path);
1184  return false;
1185  }
1186 
1187  output_stream << template_string.c_str();
1188  output_stream.close();
1189 
1190  return true; // file created successfully
1191 }
1192 
1193 // ******************************************************************************************
1194 // Create a folder
1195 // ******************************************************************************************
1196 bool ConfigurationFilesWidget::createFolder(const std::string& output_path)
1197 {
1198  if (!fs::is_directory(output_path))
1199  {
1200  if (!fs::create_directory(output_path))
1201  {
1202  QMessageBox::critical(this, "Error Generating Files",
1203  QString("Unable to create directory ").append(output_path.c_str()));
1204  return false;
1205  }
1206  }
1207  return true;
1208 }
1209 
1210 } // namespace
std::string new_package_name_
Name of the new package that is being (or going) to be generated, based on user specified save path...
void updateProgress()
A function for showing progress and user feedback about what happened.
const std::string getPackageName(std::string package_path)
Get the last folder name in a directory path.
void loadTemplateStrings()
Load the strings that will be replaced in all templates.
moveit_setup_assistant::MoveItConfigDataPtr config_data_
Contains all the configuration data for the setup assistant.
bool createFolder(const std::string &output_path)
Create a folder.
bool has_generated_pkg_
Has the package been generated yet this program execution? Used for popping up exit warning...
def generate(srv_path)
bool copyTemplate(const std::string &template_path, const std::string &output_path)
StringPairVector template_strings_
Vector of all strings to search for in templates, and their replacements.
bool outputKinematicsYAML(const std::string &file_path)
void changeCheckedState(QListWidgetItem *item)
Disable or enable item in gen_files_ array.
bool outputSetupAssistantFile(const std::string &file_path)
bool checkDependencies()
Verify with user if certain screens have not been completed.
bool outputCHOMPPlanningYAML(const std::string &file_path)
bool noGroupsEmpty()
Check that no group is empty (without links/joints/etc)
void changeActionDesc(int id)
Display the selected action in the desc box.
void setPath(const QString &path)
Set the path with QString.
unsigned int index
bool outputFakeControllersYAML(const std::string &file_path)
bool output3DSensorPluginYAML(const std::string &file_path)
virtual void focusGiven()
Received when this widget is chosen from the navigation menu.
bool first_focusGiven_
Populate the &#39;Files to be Generated&#39; list just once.
bool writeSRDF(const std::string &file_path)
bool outputJointLimitsYAML(const std::string &file_path)
bool showGenFiles()
Show the list of files to be generated.
ConfigurationFilesWidget(QWidget *parent, moveit_setup_assistant::MoveItConfigDataPtr config_data)
#define ROS_WARN_STREAM(args)
void exitSetupAssistant()
Quit the program because we are done.
#define ROS_DEBUG_STREAM(args)
bool outputROSControllersYAML(const std::string &file_path)
bool outputOMPLPlanningYAML(const std::string &file_path)
std::string getPath() const
Returns the file path in std::string format.
bool addTemplateString(const std::string &key, const std::string &value)
Insert a string pair into the template_strings_ datastructure.
#define ROS_INFO_STREAM(args)
ROSCPP_DECL std::string append(const std::string &left, const std::string &right)
std::vector< GenerateFile > gen_files_
Vector of all files to be generated.
const std::string header
#define ROS_ERROR_STREAM(args)
boost::function< bool(std::string)> gen_func_
robot_name
bool loadGenFiles()
Populate the &#39;Files to be generated&#39; list.


moveit_setup_assistant
Author(s): Dave Coleman
autogenerated on Wed Jul 10 2019 04:04:33