yaml_params.cpp
Go to the documentation of this file.
1 // Methods for converting YAML into XmlRpcValues for the parameter server
2 // Author: Max Schwarz <max.schwarz@ais.uni-bonn.de>
3 
4 #include "yaml_params.h"
5 #include "launch_config.h"
6 #include "substitution.h"
7 #include "substitution_python.h"
8 #include "string_utils.h"
9 
10 #include <boost/algorithm/string/predicate.hpp>
11 
12 namespace rosmon
13 {
14 namespace launch
15 {
16 
17 namespace
18 {
19  class XmlRpcValueCreator : public XmlRpc::XmlRpcValue
20  {
21  public:
22  static XmlRpcValueCreator createArray(const std::vector<XmlRpcValue>& values)
23  {
24  XmlRpcValueCreator ret;
25  ret._type = TypeArray;
26  ret._value.asArray = new ValueArray(values);
27 
28  return ret;
29  }
30 
31  static XmlRpcValueCreator createStruct(const std::map<std::string, XmlRpcValue>& members)
32  {
33  XmlRpcValueCreator ret;
34  ret._type = TypeStruct;
35  ret._value.asStruct = new std::map<std::string, XmlRpcValue>(members);
36  return ret;
37  }
38  };
39 }
40 
41 XmlRpc::XmlRpcValue yamlToXmlRpc(const ParseContext& ctx, const YAML::Node& n)
42 {
43  if(n.Type() != YAML::NodeType::Scalar)
44  {
45  switch(n.Type())
46  {
47  case YAML::NodeType::Map:
48  {
49  std::map<std::string, XmlRpc::XmlRpcValue> members;
50 
51  // Take care of merge keys first
52  for(YAML::const_iterator it = n.begin(); it != n.end(); ++it)
53  {
54  auto key = it->first.as<std::string>();
55  if(key == "<<")
56  {
57  auto merger = yamlToXmlRpc(ctx, it->second);
58  if(merger.getType() != XmlRpc::XmlRpcValue::TypeStruct)
59  throw std::runtime_error{"Expected a struct here"};
60 
61  for(auto& pair : merger)
62  members[pair.first] = pair.second;
63  }
64  }
65 
66  // Now everything else
67  for(YAML::const_iterator it = n.begin(); it != n.end(); ++it)
68  {
69  auto key = it->first.as<std::string>();
70  if(key == "<<")
71  continue;
72 
73  members[key] = yamlToXmlRpc(ctx, it->second);
74  }
75 
76  return XmlRpcValueCreator::createStruct(members);
77  }
78  case YAML::NodeType::Sequence:
79  {
80  std::vector<XmlRpc::XmlRpcValue> values;
81  for(YAML::const_iterator it = n.begin(); it != n.end(); ++it)
82  {
83  values.push_back(yamlToXmlRpc(ctx, *it));
84  }
85  return XmlRpcValueCreator::createArray(values);
86  }
87  default:
88  throw ctx.error("Invalid YAML node type");
89  }
90  }
91 
92  // Scalars are tricky, as XmlRpcValue is strongly typed. So we need to
93  // figure out the data type...
94 
95  // Check if a YAML tag is present
96  if(n.Tag() == "tag:yaml.org,2002:int")
97  return XmlRpc::XmlRpcValue(n.as<int>());
98  if(n.Tag() == "tag:yaml.org,2002:float")
99  return XmlRpc::XmlRpcValue(n.as<double>());
100  if(n.Tag() == "tag:yaml.org,2002:bool")
101  return XmlRpc::XmlRpcValue(n.as<bool>());
102  if(n.Tag() == "tag:yaml.org,2002:str")
103  return XmlRpc::XmlRpcValue(n.as<std::string>());
104  if(n.Tag() == "tag:yaml.org,2002:binary")
105  {
106  auto binary = n.as<YAML::Binary>();
107 
108  // XmlRpc API is stupid here and expects a mutable void*, so to keep
109  // things clean we do a copy.
110  std::vector<unsigned char> copy(binary.data(), binary.data()+binary.size());
111 
112  return XmlRpc::XmlRpcValue(copy.data(), copy.size());
113  }
114 
115  // rosparam supports !!degrees and !!radians...
116  if(n.Tag() == "!degrees")
117  {
118  try
119  {
120  return XmlRpc::XmlRpcValue(evaluateROSParamPython(n.as<std::string>()) * M_PI / 180.0);
121  }
122  catch(SubstitutionException& e)
123  {
124  throw ctx.error("{}", e.what());
125  }
126  }
127  if(n.Tag() == "!radians")
128  {
129  try
130  {
131  return XmlRpc::XmlRpcValue(evaluateROSParamPython(n.as<std::string>()));
132  }
133  catch(SubstitutionException& e)
134  {
135  throw ctx.error("{}", e.what());
136  }
137  }
138 
139  // If we have a "non-specific" tag '!', this means that the YAML scalar
140  // is non-plain, i.e. of type seq, map, or str. Since seq and map are
141  // handled above, we assume str in this case.
142  if(n.Tag() == "!")
143  return XmlRpc::XmlRpcValue(n.as<std::string>());
144 
145  // Otherwise, we simply have to try things one by one...
146  try { return XmlRpc::XmlRpcValue(n.as<bool>()); }
147  catch(YAML::Exception&) {}
148 
149  try { return XmlRpc::XmlRpcValue(n.as<int>()); }
150  catch(YAML::Exception&) {}
151 
152  try { return XmlRpc::XmlRpcValue(n.as<double>()); }
153  catch(YAML::Exception&) {}
154 
155  try
156  {
157  std::string str = n.as<std::string>();
158 
159  // rosparam supports deg(...) and rad(...) expressions, so we have to
160  // parse them here.
161 
162  if(boost::starts_with(str, "deg(") && boost::ends_with(str, ")"))
163  {
164  std::string arg = string_utils::strip(str.substr(4, str.size() - 5));
165  try
166  {
167  return XmlRpc::XmlRpcValue(
168  evaluateROSParamPython(arg) * M_PI / 180.0
169  );
170  }
171  catch(SubstitutionException& e)
172  {
173  throw ctx.error("{}", e.what());
174  }
175  }
176 
177  if(boost::starts_with(str, "rad(") && boost::ends_with(str, ")"))
178  {
179  std::string arg = string_utils::strip(str.substr(4, str.size() - 5));
180  try
181  {
183  }
184  catch(SubstitutionException& e)
185  {
186  throw ctx.error("{}", e.what());
187  }
188  }
189 
190  return XmlRpc::XmlRpcValue(str);
191  }
192  catch(YAML::Exception&) {}
193 
194  throw ctx.error("Could not convert YAML value");
195 }
196 
197 }
198 }
virtual const char * what() const noexcept override
Definition: substitution.h:29
ParseException error(const char *fmt, const Args &... args) const
double evaluateROSParamPython(const std::string &input)
Evaluate a deg(...) rosparam expression.
XmlRpc::XmlRpcValue yamlToXmlRpc(const ParseContext &ctx, const YAML::Node &n)
Definition: yaml_params.cpp:41
std::string arg(const std::string &name, const ParseContext &context)
std::string strip(const std::string &input)
float values[3]
Definition: husl.c:29


rosmon_core
Author(s): Max Schwarz
autogenerated on Fri Jun 16 2023 02:15:06