moveit2
The MoveIt Motion Planning Framework for ROS 2.
package_settings_config.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2021, PickNik Robotics, 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 PickNik Robotics 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  *********************************************************************/
36 #include <regex>
37 
38 namespace moveit_setup
39 {
40 const std::unordered_map<std::string, std::string>
41  BACKWARDS_KEY_LOOKUP({ { "urdf", "URDF" }, { "srdf", "SRDF" }, { "package_settings", "CONFIG" } });
42 
43 const std::regex MAIL_REGEX("\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,63}\\b", std::regex::icase);
44 
45 void PackageSettingsConfig::loadPrevious(const std::filesystem::path& /*config_package_path*/, const YAML::Node& node)
46 {
47  getYamlProperty(node, "author_name", author_name_);
48  getYamlProperty(node, "author_email", author_email_);
49  unsigned long int timestamp_i;
50  getYamlProperty(node, "generated_timestamp", timestamp_i);
52 }
53 
55 {
56  YAML::Node node;
57  node["author_name"] = author_name_;
58  node["author_email"] = author_email_;
59  node["generated_timestamp"] = toEpoch(config_pkg_generated_timestamp_);
60  return node;
61 }
62 
63 void PackageSettingsConfig::setPackagePath(const std::filesystem::path& package_path)
64 {
65  config_pkg_path_ = package_path;
66 }
67 
69 {
71 }
72 
73 void PackageSettingsConfig::loadExisting(const std::string& package_path_or_name)
74 {
75  if (package_path_or_name.empty())
76  {
77  throw std::runtime_error("Please specify a configuration package path to load.");
78  }
79  // Check if it is a path that exists
80  if (std::filesystem::is_directory(package_path_or_name))
81  {
82  // they inputted a full path
83  setPackagePath(package_path_or_name);
84  }
85  else
86  {
87  // Determine the path from a name
88  /* TODO(dlu): Ideally, the package path is in source so that when we write back to it,
89  * the changes will be reflected and then we can check them into git.
90  * However, there's no easy way to determine the source folder from C++.
91  * You could run colcon list -p --packages-select $PACKAGE_NAME but the
92  * results are dependent on what folder you are in and opening an external
93  * process is messy. For now, we just use the share path and rely on the user
94  * to write back to the proper directory in the ConfigurationFiles step
95  */
96  auto share_dir = getSharePath(package_path_or_name);
97 
98  // check that the folder exists
99  if (!std::filesystem::is_directory(share_dir))
100  {
101  throw std::runtime_error("The specified path is not a directory or is not accessible.");
102  }
103 
104  setPackagePath(share_dir);
105  }
106 
107  // Load the package name from the package.xml
108  std::filesystem::path relative_path; // we don't use this output value
110 
111  // Load Config Yaml
112  std::filesystem::path config_path = config_pkg_path_ / SETUP_ASSISTANT_FILE;
113  if (!std::filesystem::is_regular_file(config_path))
114  {
115  throw std::runtime_error("The chosen package location exists but was not created using MoveIt Setup Assistant. "
116  "If this is a mistake, provide the missing file: " +
117  config_path.string());
118  }
119 
120  std::ifstream input_stream(config_path);
121  if (!input_stream.good())
122  {
123  throw std::runtime_error("Unable to open file for reading " + config_path.string());
124  }
125 
126  // Begin parsing
127  try
128  {
129  const YAML::Node& doc = YAML::Load(input_stream);
130  // Get title node
131  const YAML::Node& title_node = doc["moveit_setup_assistant_config"];
132 
133  for (const std::string& name : config_data_->getRegisteredNames())
134  {
135  std::string yaml_key = name;
136  /* Generally speaking, we use each config's name as the key to read the yaml from
137  *
138  * However, for backwards compatibility, we also allow for the three legacy keys, which we translate here.
139  * If the name is found in BACKWARDS_KEY_LOOKUP and the legacy key is present in the yaml dictionary,
140  * we load using the legacy key instead.
141  */
142  auto backwards_match = BACKWARDS_KEY_LOOKUP.find(name);
143  if (backwards_match != BACKWARDS_KEY_LOOKUP.end() && title_node[backwards_match->second].IsDefined())
144  {
145  yaml_key = backwards_match->second;
146  }
147 
148  // We load the previous regardless of whether the title_node[yaml_key] is actually defined
149  config_data_->get(name)->loadPrevious(config_pkg_path_, title_node[yaml_key]);
150  }
151  }
152  catch (YAML::ParserException& e) // Catch errors, translate to runtime_error
153  {
154  throw std::runtime_error("Error parsing " + config_path.string() + ": " + e.what());
155  }
156 }
157 
159 {
160  emitter << YAML::BeginMap;
161  // Output every available planner ---------------------------------------------------
162  emitter << YAML::Key << "moveit_setup_assistant_config";
163  emitter << YAML::Value << YAML::BeginMap;
164 
165  for (const auto& config : parent_.config_data_->getConfigured())
166  {
167  YAML::Node node = config->saveToYaml();
168  if (!node.size())
169  {
170  continue;
171  }
172  emitter << YAML::Key << config->getName();
173  emitter << YAML::Value << node;
174  }
175  emitter << YAML::EndMap;
176  return true;
177 }
178 
180 {
181  package_dependencies_.clear();
182  for (const auto& config : config_data_->getConfigured())
183  {
184  config->collectDependencies(package_dependencies_);
185  }
186 }
187 
188 void PackageSettingsConfig::collectVariables(std::vector<TemplateVariable>& variables)
189 {
190  variables.push_back(TemplateVariable("GENERATED_PACKAGE_NAME", new_package_name_));
191 
192  // TODO: Add new variables for other fields existing in the package.xml
193  // i.e. read the version so that the version is not overwritten
194  variables.push_back(TemplateVariable("AUTHOR_NAME", author_name_));
195  variables.push_back(TemplateVariable("AUTHOR_EMAIL", author_email_));
196 
197  std::stringstream deps;
198  for (const auto& dependency : package_dependencies_)
199  {
200  deps << " <exec_depend>" << dependency << "</exec_depend>\n";
201  }
202  variables.push_back(TemplateVariable("OTHER_DEPENDENCIES", deps.str()));
203 }
204 
206 {
207  // Make sure there is something that isn't just whitespace
208  return author_name_.find_first_not_of(' ') != std::string::npos;
209 }
210 
212 {
213  return std::regex_match(author_email_, MAIL_REGEX);
214 }
215 
217 {
218  config_pkg_generated_timestamp_ = GeneratedTime::clock::now();
219 }
220 
221 } // namespace moveit_setup
222 
223 #include <pluginlib/class_list_macros.hpp> // NOLINT
PLUGINLIB_EXPORT_CLASS(cached_ik_kinematics_plugin::CachedIKKinematicsPlugin< kdl_kinematics_plugin::KDLKinematicsPlugin >, kinematics::KinematicsBase)
void collectVariables(std::vector< TemplateVariable > &variables) override
Collect key/value pairs for use in templates.
std::string author_name_
Name of the author of this config.
GeneratedTime config_pkg_generated_timestamp_
Timestamp when configuration package was generated, if it was previously generated.
void setPackagePath(const std::filesystem::path &package_path)
void loadPrevious(const std::filesystem::path &package_path, const YAML::Node &node) override
Overridden method to load THIS config's data variables.
void setPackageName(const std::string &package_name)
std::string author_email_
Email of the author of this config.
YAML::Node saveToYaml() const override
Optionally save "meta" information for saving in the .setup_assistant yaml file.
void loadExisting(const std::string &package_path_or_name)
Method for loading the contents of the .setup_assistant file into all the configs.
std::string new_package_name_
Name of the new package that is being (or going) to be generated, based on user specified save path.
std::filesystem::path config_pkg_path_
Loaded configuration package path - if an existing package was loaded, holds that path.
where all the data for each part of the configuration is stored.
Definition: config.hpp:58
std::shared_ptr< DataWarehouse > config_data_
Definition: config.hpp:161
long int toEpoch(const GeneratedTime &t)
Convert a GeneratedTime to an integral epoch (using system_clock)
bool getYamlProperty(const YAML::Node &node, const std::string &key, T &storage, const T &default_value=T())
Definition: utilities.hpp:110
std::filesystem::path getSharePath(const std::string &package_name)
Return a path for the given package's share folder.
Definition: utilities.hpp:50
const std::regex MAIL_REGEX("\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,63}\\b", std::regex::icase)
bool extractPackageNameFromPath(const std::filesystem::path &path, std::string &package_name, std::filesystem::path &relative_filepath)
Definition: utilities.cpp:41
GeneratedTime fromEpoch(long int epoch)
Convert an integral epoch to GeneratedTime (using system_clock)
const std::unordered_map< std::string, std::string > BACKWARDS_KEY_LOOKUP({ { "urdf", "URDF" }, { "srdf", "SRDF" }, { "package_settings", "CONFIG" } })
string package_name
Definition: setup.py:4
name
Definition: setup.py:7
Simple Key/value pair for templates.
Definition: templates.hpp:47