12 #include <mrpt/core/bits_math.h>
13 #include <mrpt/core/format.h>
14 #include <mrpt/core/get_env.h>
15 #include <mrpt/img/TColor.h>
16 #include <mrpt/io/vector_loadsave.h>
17 #include <mrpt/math/TPolygon2D.h>
18 #include <mrpt/poses/CPose2D.h>
19 #include <mrpt/poses/CPose3D.h>
20 #include <mrpt/system/COutputLogger.h>
21 #include <mrpt/system/filesystem.h>
22 #include <mrpt/system/string_utils.h>
34 using namespace mvsim;
41 const std::string& inStr,
const std::string& varName,
42 const std::map<std::string, std::string>& variableNamesValues,
43 const char* functionNameContext)
const
45 const std::string str =
mvsim::parse(inStr, variableNamesValues);
49 if (std::string(frmt) == std::string(
"%s"))
51 std::string& val2 = *
reinterpret_cast<std::string*
>(val);
55 else if (std::string(frmt) == std::string(
"%lf_deg"))
57 if (1 != ::sscanf(str.c_str(), frmt, val))
58 throw std::runtime_error(mrpt::format(
59 "%s Error parsing attribute '%s'='%s' (Expected "
61 functionNameContext, varName.c_str(), str.c_str(), frmt));
62 double& ang = *
reinterpret_cast<double*
>(val);
63 ang = mrpt::DEG2RAD(ang);
66 else if (std::string(frmt) == std::string(
"%bool"))
68 bool& bool_val = *
reinterpret_cast<bool*
>(val);
70 const std::string sStr = mrpt::system::lowerCase(
mrpt::system::trim(std::string(str)));
71 if (sStr ==
"1" || sStr ==
"true")
73 else if (sStr ==
"0" || sStr ==
"false")
76 throw std::runtime_error(mrpt::format(
77 "%s Error parsing 'bool' attribute '%s'='%s' (Expected "
79 functionNameContext, varName.c_str(), str.c_str()));
82 else if (std::string(frmt) == std::string(
"%color"))
85 if (!(str.size() > 1 && str[0] ==
'#'))
86 throw std::runtime_error(mrpt::format(
87 "%s Error parsing '%s'='%s' (Expected "
88 "format:'#RRGGBB[AA]')",
89 functionNameContext, varName.c_str(), str.c_str()));
91 unsigned int r, g, b, a = 0xff;
92 int ret = ::sscanf(str.c_str() + 1,
"%2x%2x%2x%2x", &r, &g, &b, &a);
93 if (ret != 3 && ret != 4)
94 throw std::runtime_error(mrpt::format(
95 "%s Error parsing '%s'='%s' (Expected "
96 "format:'#RRGGBB[AA]')",
97 functionNameContext, varName.c_str(), str.c_str()));
98 mrpt::img::TColor& col = *
reinterpret_cast<mrpt::img::TColor*
>(val);
99 col = mrpt::img::TColor(r, g, b, a);
103 else if (!strncmp(frmt,
"%pose2d", strlen(
"%pose2d")))
106 int ret = ::sscanf(str.c_str(),
"%lf %lf %lf", &x, &y, &yaw);
108 throw std::runtime_error(mrpt::format(
109 "%s Error parsing '%s'='%s' (Expected format:'X Y "
111 functionNameContext, varName.c_str(), str.c_str()));
114 yaw = mrpt::DEG2RAD(yaw);
116 const mrpt::poses::CPose2D p(x, y, yaw);
119 if (!strcmp(frmt,
"%pose2d"))
121 mrpt::poses::CPose2D& pp = *
reinterpret_cast<mrpt::poses::CPose2D*
>(val);
124 else if (!strcmp(frmt,
"%pose2d_ptr3d"))
126 mrpt::poses::CPose3D& pp = *
reinterpret_cast<mrpt::poses::CPose3D*
>(val);
127 pp = mrpt::poses::CPose3D(p);
130 throw std::runtime_error(
131 mrpt::format(
"%s Error: Unknown format specifier '%s'", functionNameContext, frmt));
134 else if (!strncmp(frmt,
"%point3d", strlen(
"%point3d")))
136 double x = 0, y = 0, z = 0;
137 int ret = ::sscanf(str.c_str(),
"%lf %lf %lf", &x, &y, &z);
138 if (ret != 2 && ret != 3)
139 throw std::runtime_error(mrpt::format(
140 "%s Error parsing '%s'='%s' (Expected format:'X Y [Z]')", functionNameContext,
141 varName.c_str(), str.c_str()));
143 mrpt::math::TPoint3D& pp = *
reinterpret_cast<mrpt::math::TPoint3D*
>(val);
150 else if (!strncmp(frmt,
"%pose3d", strlen(
"%pose3d")))
152 double x, y, z, yawDeg, pitchDeg, rollDeg;
154 str.c_str(),
"%lf %lf %lf %lf %lf %lf", &x, &y, &z, &yawDeg, &pitchDeg, &rollDeg);
156 throw std::runtime_error(mrpt::format(
157 "%s Error parsing '%s'='%s' (Expected format:'X Y Z"
158 "YAW_DEG PITCH_DEG ROLL_DEG')",
159 functionNameContext, varName.c_str(), str.c_str()));
162 const auto yaw = mrpt::DEG2RAD(yawDeg);
163 const auto pitch = mrpt::DEG2RAD(pitchDeg);
164 const auto roll = mrpt::DEG2RAD(rollDeg);
166 const mrpt::poses::CPose3D p(x, y, yaw);
168 mrpt::poses::CPose3D& pp = *
reinterpret_cast<mrpt::poses::CPose3D*
>(val);
169 pp = mrpt::poses::CPose3D(x, y, z, yaw, pitch, roll);
174 if (1 != ::sscanf(str.c_str(), frmt, val))
175 throw std::runtime_error(mrpt::format(
176 "%s Error parsing attribute '%s'='%s' (Expected "
178 functionNameContext, varName.c_str(), str.c_str(), frmt));
184 const std::map<std::string, std::string>& variableNamesValues,
const char* functionNameContext)
186 for (
const auto&
param : params)
191 attr->value(), attr->name(), variableNamesValues, functionNameContext);
198 const std::map<std::string, std::string>& variableNamesValues,
const char* functionNameContext)
200 if (
auto it_param = params.find(
xml_node.name()); it_param != params.end())
203 it_param->second.parse(
204 xml_node.value(),
xml_node.name(), variableNamesValues, functionNameContext);
217 const std::map<std::string, std::string>& variableNamesValues,
const char* functionNameContext,
218 mrpt::system::COutputLogger* logger)
225 if (!recognized && logger)
228 mrpt::system::LVL_WARN,
"Unrecognized tag '<%s>' in %s", node->name(),
229 functionNameContext ? functionNameContext :
"(none)");
236 const std::string& sOrg,
bool allow_missing_angle,
double default_angle_radians,
237 const std::map<std::string, std::string>& variableNamesValues)
239 mrpt::math::TPose2D v;
240 v.phi = mrpt::RAD2DEG(default_angle_radians);
242 const auto s =
parse(sOrg, variableNamesValues);
243 int na = ::sscanf(
s.c_str(),
"%lf %lf %lf", &v.x, &v.y, &v.phi);
246 v.phi = mrpt::DEG2RAD(v.phi);
248 if ((na != 3 && !allow_missing_angle) || (na != 2 && na != 3 && allow_missing_angle))
249 throw std::runtime_error(mrpt::format(
"Malformed pose string: '%s'",
s.c_str()));
260 const char* functionNameContext)
265 pt_node = pt_node->next_sibling(
"pt"))
267 if (!pt_node->value())
268 throw std::runtime_error(
269 mrpt::format(
"%s Error: <pt> node seems empty.", functionNameContext));
271 mrpt::math::TPoint2D pt;
272 const char* str_val = pt_node->value();
273 if (2 != ::sscanf(str_val,
"%lf %lf", &pt.x, &pt.y))
274 throw std::runtime_error(mrpt::format(
275 "%s Error parsing <pt> node: '%s' (Expected format:'<pt>X "
277 functionNameContext, str_val));
279 out_poly.push_back(pt);
282 if (out_poly.size() < 3)
283 throw std::runtime_error(mrpt::format(
284 "%s Error: <shape> node requires 3 or more <pt>X Y</pt> "
286 functionNameContext));
293 using namespace std::string_literals;
296 char* input_str =
const_cast<char*
>(xmlData.c_str());
297 auto xml = std::make_shared<rapidxml::xml_document<>>();
300 xml->parse<0>(input_str);
304 unsigned int line =
static_cast<long>(std::count(input_str, e.
where<
char>(),
'\n') + 1);
305 throw std::runtime_error(
306 mrpt::format(
"XML parse error (Line %u): %s",
static_cast<unsigned>(line), e.
what()));
311 ASSERTMSG_(
root,
"XML parse error: No root node found (empty file '"s + pathToFile +
"'?)"s);
316 const std::string& pathToFile,
const std::map<std::string, std::string>& variables,
317 const std::set<std::string>& varsRetain)
320 using namespace std::string_literals;
322 ASSERT_FILE_EXISTS_(pathToFile);
324 auto xmlDoc = std::make_shared<XML_Doc_Data>();
327 std::map<std::string, std::string> childVariables = variables;
328 childVariables[
"MVSIM_CURRENT_FILE_DIRECTORY"] = mrpt::system::toAbsolutePath(
329 mrpt::system::extractFileDirectory(pathToFile),
false );
331 xmlDoc->documentData =
336 xmlDoc->doc = std::move(xml);
338 return {xmlDoc,
root};
342 size_t pos,
const std::string& s,
const char searchEndChar,
const char otherStartChar)
345 for (; pos <
s.size(); pos++)
347 const char ch =
s[pos];
348 if (ch == otherStartChar)
350 else if (ch == searchEndChar)
361 return std::string::npos;
367 const auto posBar =
s.find(
"|");
368 if (posBar == std::string::npos)
return {
s, {}};
370 return {
s.substr(0, posBar),
s.substr(posBar + 1)};
374 const std::string& text,
const std::map<std::string, std::string>& variables,
375 const std::set<std::string>& varsRetain,
const size_t searchStartPos = 0)
379 const auto start = text.find(
"${", searchStartPos);
380 if (
start == std::string::npos)
return text;
382 const std::string pre = text.substr(0,
start);
383 const std::string post = text.substr(
start + 2);
385 const auto post_end =
findClosing(0, post,
'}',
'{');
386 if (post_end == std::string::npos)
389 "Column=%u: Cannot find matching `}` for `${` in: `%s`",
390 static_cast<unsigned int>(
start), text.c_str());
393 const auto varnameOrg = post.substr(0, post_end);
397 if (varsRetain.count(varname) != 0)
403 std::string varvalue;
404 if (
auto itVal = variables.find(varname); itVal != variables.end())
406 varvalue = itVal->second;
408 else if (
const char* v = ::getenv(varname.c_str()); v !=
nullptr)
410 varvalue = std::string(v);
414 if (!defaultValue.empty())
416 varvalue = defaultValue;
420 THROW_EXCEPTION_FMT(
"mvsim::parseVars(): Undefined variable: ${%s}", varname.c_str());
424 return parseVars(pre + varvalue + post.substr(post_end + 1), variables, varsRetain);
429 const std::string& in,
const std::map<std::string, std::string>& variables,
430 const std::set<std::string>& varsRetain)
432 const auto ret =
parseVars(in, variables, varsRetain);
434 thread_local
const bool MVSIM_VERBOSE_PARSE = mrpt::get_env<bool>(
"MVSIM_VERBOSE_PARSE",
false);
437 std::cout <<
"[parse_variables] Input:\n" << in <<
"\n";
438 std::cout <<
"[parse_variables] Output:\n" << ret <<
"\n";
439 std::cout <<
"[parse_variables] variables: ";
440 for (
const auto& kv : variables) std::cout << kv.first <<
",";
449 std::stringstream& ss)
452 if (strcmp(n->name(),
"include") == 0)
455 ASSERTMSG_(fileAttrb,
"XML tag '<include />' must have a 'file=\"xxx\"' attribute)");
457 const std::string relFile =
461 mrpt::system::LVL_DEBUG,
462 mrpt::format(
"XML parser: including file: '%s'", absFile.c_str()));
464 std::map<std::string, std::string> vars;
470 if (strcmp(attr->name(),
"file") == 0)
continue;
471 vars[attr->name()] = attr->value();
477 ss <<
"<!-- INCLUDE: '" << absFile <<
"' -->\n";
481 else if (strcmp(n->name(),
"if") == 0)
500 const std::set<std::string>& varsRetain)
505 std::stringstream ss;
511 ss <<
" " << a->name() <<
"=\"" << a->value() <<
"\"";
521 ss <<
"</" <<
xml_node->name() <<
">\n";