World_load_xml.cpp
Go to the documentation of this file.
1 /*+-------------------------------------------------------------------------+
2  | MultiVehicle simulator (libmvsim) |
3  | |
4  | Copyright (C) 2014-2024 Jose Luis Blanco Claraco |
5  | Copyright (C) 2017 Borys Tymchenko (Odessa Polytechnic University) |
6  | Distributed under 3-clause BSD License |
7  | See COPYING |
8  +-------------------------------------------------------------------------+ */
9 #include <mrpt/core/format.h>
10 #include <mrpt/core/get_env.h>
11 #include <mrpt/core/lock_helper.h>
12 #include <mrpt/maps/CSimplePointsMap.h>
13 #include <mrpt/system/filesystem.h> // extractFileDirectory()
14 #include <mrpt/topography/conversions.h>
15 #include <mvsim/World.h>
16 
17 #include <algorithm> // count()
18 #include <map>
19 #include <rapidxml.hpp>
20 #include <rapidxml_print.hpp>
21 #include <rapidxml_utils.hpp>
22 #include <sstream>
23 #include <stdexcept>
24 
25 #include "parse_utils.h"
26 #include "xml_utils.h"
27 
28 using namespace mvsim;
29 using namespace std;
30 
31 void World::load_from_XML_file(const std::string& xmlFileNamePath)
32 {
33  rapidxml::file<> fil_xml(xmlFileNamePath.c_str());
34  load_from_XML(fil_xml.data(), xmlFileNamePath.c_str());
35 }
36 
37 void World::load_from_XML(const std::string& xml_text, const std::string& fileNameForPath)
38 {
39  using namespace std;
40  using namespace rapidxml;
41 
42  std::string fileDir = mrpt::system::extractFileDirectory(fileNameForPath);
43  if (fileDir.empty()) fileDir = ".";
44 
45  // Extract base path of file:
46  basePath_ = mrpt::system::toAbsolutePath(fileDir, false /*canonical*/);
47  // printf("[World] INFO: Using base path='%s'\n",basePath_.c_str());
48 
49  // Special variables:
50  userDefinedVariables_["MVSIM_CURRENT_FILE_DIRECTORY"] = basePath_;
51 
52  auto lck = mrpt::lockHelper(world_cs_); // Protect multithread access
53 
54  // Clear the existing world.
55  this->clear_all();
56 
57  // Create anchor object for standalone (environment-attached) sensors:
58  DummyInvisibleBlock::Ptr standaloneSensorHost = std::make_shared<DummyInvisibleBlock>(this);
59 
60  simulableObjectsMtx_.lock();
61  simulableObjects_.emplace("__standaloneSensorHost", standaloneSensorHost);
62  simulableObjectsMtx_.unlock();
63 
64  // Parse the XML input:
65  const auto [xml, root] = readXmlTextAndGetRoot(xml_text, fileNameForPath);
66  (void)xml; // unused
67 
68  if (0 != strcmp(root->name(), "mvsim_world"))
69  throw runtime_error(
70  mrpt::format("XML root element is '%s' ('mvsim_world' expected)", root->name()));
71 
72  // Optional: format version attrib:
73  const xml_attribute<>* attrb_version = root->first_attribute("version");
74  int version_major = 1, version_min = 0;
75  if (attrb_version)
76  {
77  int ret = sscanf(attrb_version->value(), "%i.%i", &version_major, &version_min);
78  if (ret != 2)
79  throw runtime_error(mrpt::format(
80  "Error parsing version attribute: '%s' ('%%i.%%i' "
81  "expected)",
82  attrb_version->value()));
83  }
84 
85  // register tags:
86  register_standard_xml_tag_parsers();
87 
88  // ------------------------------------------------
89  // Process all xml nodes
90  // ------------------------------------------------
91  xml_node<>* node = root->first_node();
92  while (node)
93  {
94  internal_recursive_parse_XML({node, basePath_});
95 
96  // Move on to next node:
97  node = node->next_sibling(nullptr);
98  }
99 
100  internal_initialize();
101 }
102 
104 {
105  if (!xmlParsers_.empty()) return;
106 
107  register_tag_parser("vehicle", &World::parse_tag_vehicle);
108  register_tag_parser("vehicle:class", &World::parse_tag_vehicle_class);
109 
110  register_tag_parser("block", &World::parse_tag_block);
111  register_tag_parser("block:class", &World::parse_tag_block_class);
112 
113  register_tag_parser("element", &World::parse_tag_element);
114  register_tag_parser("sensor", &World::parse_tag_sensor);
115  register_tag_parser("gui", &World::parse_tag_gui);
116  register_tag_parser("georeference", &World::parse_tag_georeference);
117  register_tag_parser("lights", &World::parse_tag_lights);
118  register_tag_parser("walls", &World::parse_tag_walls);
119  register_tag_parser("include", &World::parse_tag_include);
120  register_tag_parser("variable", &World::parse_tag_variable);
121  register_tag_parser("for", &World::parse_tag_for);
122  register_tag_parser("if", &World::parse_tag_if);
123  register_tag_parser("marker", &World::parse_tag_marker);
124 }
125 
127 {
128  using namespace rapidxml;
129  using namespace std::string_literals;
130 
131  const rapidxml::xml_node<>* node = ctx.node;
132  ASSERT_(node);
133 
134  // push relative directory state:
135  const auto savedBasePath = basePath_;
136  basePath_ = ctx.currentBasePath;
137  // Special variables:
138  userDefinedVariables_["MVSIM_CURRENT_FILE_DIRECTORY"] = basePath_;
139 
140  // Known tag parser?
141  if (auto itParser = xmlParsers_.find(node->name()); itParser != xmlParsers_.end())
142  {
143  itParser->second(ctx);
144  }
145  else
146  { // Default: Check if it's a parameter:
147  if (!parse_xmlnode_as_param(*node, otherWorldParams_))
148  {
149  // Unknown element!!
150  MRPT_LOG_WARN_STREAM(
151  "[World::load_from_XML] *Warning* Ignoring unknown XML node type '" << node->name()
152  << "'");
153  }
154  }
155 
156  // pop relative directory state:
157  basePath_ = savedBasePath;
158  // Special variables:
159  userDefinedVariables_["MVSIM_CURRENT_FILE_DIRECTORY"] = basePath_;
160 }
161 
163 {
164  // <element class='*'> entries:
166  worldElements_.emplace_back(e);
167 
168  auto lckListObjs = mrpt::lockHelper(getListOfSimulableObjectsMtx());
169  simulableObjects_.emplace(e->getName(), std::dynamic_pointer_cast<Simulable>(e));
170 }
171 
173 {
174  // <vehicle> entries:
175  VehicleBase::Ptr veh = VehicleBase::factory(this, ctx.node);
176  // Assign each vehicle a unique "index" number
177  veh->setVehicleIndex(vehicles_.size());
178 
179  ASSERTMSG_(
180  vehicles_.count(veh->getName()) == 0,
181  mrpt::format("Duplicated vehicle name: '%s'", veh->getName().c_str()));
182 
183  vehicles_.insert(VehicleList::value_type(veh->getName(), veh));
184 
185  auto lckListObjs = mrpt::lockHelper(getListOfSimulableObjectsMtx());
186  simulableObjects_.emplace(veh->getName(), std::dynamic_pointer_cast<Simulable>(veh));
187 }
188 
190 {
191  // <vehicle:class> entries:
193 }
194 
196 {
197  // top-level <sensor> entries:
198  auto lckListObjs = mrpt::lockHelper(getListOfSimulableObjectsMtx());
199 
200  DummyInvisibleBlock::Ptr standaloneSensorHost = std::dynamic_pointer_cast<DummyInvisibleBlock>(
201  simulableObjects_.find("__standaloneSensorHost")->second);
202  ASSERT_(standaloneSensorHost);
203 
204  SensorBase::Ptr sensor = SensorBase::factory(*standaloneSensorHost, ctx.node);
205  standaloneSensorHost->add_sensor(sensor);
206 }
207 
209 {
210  // <block> entries:
211  Block::Ptr block = Block::factory(this, ctx.node);
212  insertBlock(block);
213  lut2d_objects_is_up_to_date_ = false;
214 }
215 
217 {
218  //
219  Block::register_block_class(*this, ctx.node);
220 }
221 
223 {
224  //
225  guiOptions_.parse_from(*ctx.node, *this);
226 }
227 
229 {
230  lightOptions_.parse_from(*ctx.node, *this);
231 }
232 
234 {
235  georeferenceOptions_.parse_from(*ctx.node, *this);
236 
237  // handle UTM:
238  auto& g = georeferenceOptions_;
239 
240  if (g.world_is_utm)
241  {
242  ASSERTMSG_(
243  g.world_to_enu_rotation == 0,
244  "Cannot define both, <world_to_enu_rotation> and <world_is_utm>");
245 
246  ASSERTMSG_(
247  !g.georefCoord.isClear(),
248  "<world_is_utm> requires defining a valid reference geodetic coordinates too.");
249 
250  mrpt::topography::GeodeticToUTM(g.georefCoord, g.utmRef, g.utm_zone, g.utm_band);
251 
252  MRPT_LOG_INFO_STREAM(
253  "Using UTM georeference: utm_zone=" << g.utm_zone << " geoRef: utmRef=" << g.utmRef);
254  }
255 }
256 
257 void World::parse_tag_walls(const XmlParserContext& ctx) { process_load_walls(*ctx.node); }
258 
260 {
261  auto fileAttrb = ctx.node->first_attribute("file");
262  ASSERTMSG_(fileAttrb, "XML tag '<include />' must have a 'file=\"xxx\"' attribute)");
263 
264  const std::string relFile = mvsim::parse(fileAttrb->value(), user_defined_variables());
265 
266  const auto absFile = this->local_to_abs_path(relFile);
267  MRPT_LOG_DEBUG_STREAM("XML parser: including file: '" << absFile << "'");
268 
269  std::map<std::string, std::string> vars;
270  // Inherit the user-defined variables from parent scope
271  vars = user_defined_variables();
272  // Plus new variables as XML attributes, local only:
273  for (auto attr = ctx.node->first_attribute(); attr; attr = attr->next_attribute())
274  {
275  if (strcmp(attr->name(), "file") == 0) continue;
276  vars[attr->name()] = attr->value();
277  }
278 
279  const auto [xml, root] = readXmlAndGetRoot(absFile, vars);
280  (void)xml; // unused
281 
282  // recursive parse:
283  const auto newBasePath = mrpt::system::extractFileDirectory(absFile);
284  internal_recursive_parse_XML({root, newBasePath});
285 }
286 
288 {
289  auto nameAttr = ctx.node->first_attribute("name");
290  ASSERTMSG_(nameAttr, "XML tag '<variable />' must have a 'name=\"xxx\"' attribute)");
291  const auto name = nameAttr->value();
292 
293  auto valueAttr = ctx.node->first_attribute("value");
294  ASSERTMSG_(valueAttr, "XML tag '<variable />' must have a 'value=\"xxx\"' attribute)");
295 
296  const std::string finalValue = mvsim::parse(valueAttr->value(), userDefinedVariables_);
297 
298  thread_local const bool MVSIM_VERBOSE_PARSE = mrpt::get_env<bool>("MVSIM_VERBOSE_PARSE", false);
299 
301  {
302  printf(
303  "[mvsim] Parsed <variable>: name='%s' value='%s' (original "
304  "expression='%s')\n",
305  name, finalValue.c_str(), valueAttr->value());
306  }
307 
308  userDefinedVariables_[name] = finalValue;
309 }
310 
312 {
313  auto varAttr = ctx.node->first_attribute("var");
314  ASSERTMSG_(varAttr, "XML tag '<for />' must have a 'var=\"xxx\"' attribute)");
315  const auto varName = varAttr->value();
316 
317  auto varFrom = ctx.node->first_attribute("from");
318  ASSERTMSG_(varFrom, "XML tag '<for />' must have a 'from=\"xxx\"' attribute)");
319  const auto fromStr = mvsim::parse(varFrom->value(), userDefinedVariables_);
320 
321  auto varTo = ctx.node->first_attribute("to");
322  ASSERTMSG_(varTo, "XML tag '<for />' must have a 'to=\"xxx\"' attribute)");
323  const auto toStr = mvsim::parse(varTo->value(), userDefinedVariables_);
324 
325  bool forBodyEmpty = true;
326 
327  for (auto childNode = ctx.node->first_node(); childNode; childNode = childNode->next_sibling())
328  {
329  forBodyEmpty = false;
330  for (int curVal = std::stoi(fromStr); curVal <= std::stoi(toStr); curVal++)
331  {
332  userDefinedVariables_[varName] = std::to_string(curVal);
333  internal_recursive_parse_XML({childNode, basePath_});
334  }
335  }
336 
337  if (forBodyEmpty)
338  {
339  MRPT_LOG_WARN_STREAM(
340  "[World::load_from_XML] *Warning* <for ...> </for> loop has no "
341  "contents (!): '"
342  << ctx.node->value() << "'");
343  }
344 }
345 
347 {
348  bool isTrue = evaluate_tag_if(*ctx.node);
349  if (!isTrue) return;
350 
351  for (auto childNode = ctx.node->first_node(); childNode; childNode = childNode->next_sibling())
352  {
353  internal_recursive_parse_XML({childNode, basePath_});
354  }
355 }
356 
358 {
359  auto varCond = node.first_attribute("condition");
360  ASSERTMSG_(varCond, "XML tag '<if />' must have a 'condition=\"xxx\"' attribute)");
361  const auto str = mvsim::parse(varCond->value(), userDefinedVariables_);
362 
363  // is it "true"?
364  std::optional<int> intVal;
365  char* retStr = nullptr;
366  const long long ret = std::strtoll(str.c_str(), &retStr, 0 /*auto base*/);
367  if (retStr != 0 && retStr != str.c_str()) intVal = ret;
368 
369  bool isTrue = str == "y" || str == "Y" || str == "yes" || str == "Yes" || str == "YES" ||
370  str == "true" || str == "True" || str == "TRUE" || str == "on" || str == "ON" ||
371  str == "On" || (intVal.has_value() && intVal.value() != 0);
372 
373  return isTrue;
374 }
375 
377 {
378  auto typeAttr = ctx.node->first_attribute("type");
379  ASSERTMSG_(typeAttr, "XML tag '<marker />' must have a 'type=\"xxx\"' attribute)");
380  const std::string type = typeAttr->value();
381 
382  mrpt::img::TColor color{0xff, 0xff, 0xff, 0xff};
383 
384  TParameterDefinitions params;
385  mrpt::math::TPoint3D translation = {0, 0, 0};
386  params["color"] = TParamEntry("%color", &color);
387  params["translation"] = TParamEntry("%point3d", &translation);
388 
389  // Parse XML params:
390  parse_xmlnode_children_as_param(*ctx.node, params, user_defined_variables());
391 
392  if (type == "line_strip")
393  {
394  // line_strip
395  // ---------------
396  // Walls shape can come from external model file, or from a "shape" entry:
397  const auto* xmlPts = ctx.node->first_node("points");
398  ASSERTMSG_(xmlPts, "<marker type='line_strip'> requires tag <points>x y z...</points>");
399 
400  mrpt::maps::CSimplePointsMap pts;
401  std::string parseError;
402  std::stringstream ss(mvsim::trim(xmlPts->value()));
403  bool parsedOk = pts.load3D_from_text_stream(ss, parseError);
404  ASSERTMSG_(
405  parsedOk, "Error parsing XYZ data within <marker type='line_strip'>: "s + parseError);
406 
407  auto glObj = mrpt::opengl::CSetOfLines::Create();
408  glObj->setColor_u8(color);
409  glObj->setLocation(translation);
410 
411  for (size_t i = 0; i < pts.size(); i++)
412  {
413  mrpt::math::TPoint3D pt;
414  pts.getPoint(i, pt);
415  pt += worldRenderOffset();
416 
417  if (i == 0)
418  glObj->appendLine(pt, pt);
419  else
420  glObj->appendLineStrip(pt);
421  }
422 
423  auto lckPhys = mrpt::lockHelper(physical_objects_mtx());
424  worldVisual_->insert(glObj);
425  }
426  else
427  {
428  THROW_EXCEPTION_FMT("Unknown <marker> of type='%s'>", type.c_str());
429  }
430 }
431 
433  const rapidxml::xml_node<char>& node, mrpt::system::COutputLogger& logger)
434 {
435  parse_xmlnode_children_as_param(node, params, {}, "[World::GeoreferenceOptions]", &logger);
436 }
mvsim
Definition: Client.h:21
mvsim::World::parse_tag_block
void parse_tag_block(const XmlParserContext &ctx)
Definition: World_load_xml.cpp:208
mvsim::World::parse_tag_lights
void parse_tag_lights(const XmlParserContext &ctx)
Definition: World_load_xml.cpp:228
parse_utils.h
mvsim::World::XmlParserContext
Definition: World.h:816
mvsim::Block::factory
static Ptr factory(World *parent, const rapidxml::xml_node< char > *xml_node)
Definition: Block.cpp:64
MVSIM_VERBOSE_PARSE
const thread_local bool MVSIM_VERBOSE_PARSE
Definition: parse_utils.cpp:26
mvsim::TParamEntry
Definition: TParameterDefinitions.h:38
s
XmlRpcServer s
mvsim::parse_xmlnode_children_as_param
void parse_xmlnode_children_as_param(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="", mrpt::system::COutputLogger *logger=nullptr)
Definition: xml_utils.cpp:215
World.h
mvsim::World::parse_tag_gui
void parse_tag_gui(const XmlParserContext &ctx)
Definition: World_load_xml.cpp:222
mvsim::World::load_from_XML_file
void load_from_XML_file(const std::string &xmlFileNamePath)
Definition: World_load_xml.cpp:31
mvsim::World::evaluate_tag_if
bool evaluate_tag_if(const rapidxml::xml_node< char > &node) const
Definition: World_load_xml.cpp:357
mvsim::World::parse_tag_block_class
void parse_tag_block_class(const XmlParserContext &ctx)
Definition: World_load_xml.cpp:216
mvsim::WorldElementBase::factory
static Ptr factory(World *parent, const rapidxml::xml_node< char > *xml_node, const char *class_name=nullptr)
Definition: WorldElementBase.cpp:49
xml_utils.h
rapidxml::xml_attribute
Definition: rapidxml.hpp:138
rapidxml::file
Represents data loaded from a file.
Definition: rapidxml_utils.hpp:21
mvsim::World::parse_tag_georeference
void parse_tag_georeference(const XmlParserContext &ctx)
Definition: World_load_xml.cpp:233
mvsim::DummyInvisibleBlock::Ptr
std::shared_ptr< DummyInvisibleBlock > Ptr
Definition: Block.h:238
mvsim::World::GeoreferenceOptions::parse_from
void parse_from(const rapidxml::xml_node< char > &node, COutputLogger &logger)
Definition: World_load_xml.cpp:432
mvsim::World::XmlParserContext::currentBasePath
const std::string currentBasePath
Definition: World.h:824
void
typedef void(GLAD_API_PTR *GLDEBUGPROC)(GLenum source
rapidxml::file::data
Ch * data()
Definition: rapidxml_utils.hpp:65
rapidxml
Definition: rapidxml.hpp:57
mvsim::World::load_from_XML
void load_from_XML(const std::string &xml_text, const std::string &fileNameForPath=std::string("."))
Definition: World_load_xml.cpp:37
rapidxml::xml_node::first_node
xml_node< Ch > * first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:936
mvsim::TParameterDefinitions
std::map< std::string, TParamEntry > TParameterDefinitions
Definition: TParameterDefinitions.h:64
mvsim::World::parse_tag_vehicle
void parse_tag_vehicle(const XmlParserContext &ctx)
<vehicle>
Definition: World_load_xml.cpp:172
mvsim::World::parse_tag_vehicle_class
void parse_tag_vehicle_class(const XmlParserContext &ctx)
Definition: World_load_xml.cpp:189
mvsim::SensorBase::factory
static SensorBase::Ptr factory(Simulable &parent, const rapidxml::xml_node< char > *xml_node)
Definition: SensorBase.cpp:93
mvsim::Block::register_block_class
static void register_block_class(const World &parent, const rapidxml::xml_node< char > *xml_node)
Definition: Block.cpp:50
rapidxml_utils.hpp
mvsim::parse
std::string parse(const std::string &input, const std::map< std::string, std::string > &variableNamesValues={})
Definition: parse_utils.cpp:217
mvsim::World::parse_tag_element
void parse_tag_element(const XmlParserContext &ctx)
<element>
Definition: World_load_xml.cpp:162
mvsim::World::register_standard_xml_tag_parsers
void register_standard_xml_tag_parsers()
Definition: World_load_xml.cpp:103
mvsim::trim
std::string trim(const std::string &s)
Definition: parse_utils.cpp:251
mvsim::World::parse_tag_walls
void parse_tag_walls(const XmlParserContext &ctx)
Definition: World_load_xml.cpp:257
mvsim::readXmlAndGetRoot
std::tuple< XML_Doc_Data::Ptr, rapidxml::xml_node<> * > readXmlAndGetRoot(const std::string &pathToFile, const std::map< std::string, std::string > &variables, const std::set< std::string > &varsRetain={})
Definition: xml_utils.cpp:315
mvsim::SensorBase::Ptr
std::shared_ptr< SensorBase > Ptr
Definition: SensorBase.h:37
mvsim::World::internal_recursive_parse_XML
void internal_recursive_parse_XML(const XmlParserContext &ctx)
This will parse a main XML file, or its included.
Definition: World_load_xml.cpp:126
mvsim::World::parse_tag_if
void parse_tag_if(const XmlParserContext &ctx)
Definition: World_load_xml.cpp:346
rapidxml::xml_node
Definition: rapidxml.hpp:137
mvsim::World::parse_tag_sensor
void parse_tag_sensor(const XmlParserContext &ctx)
<sensor>
Definition: World_load_xml.cpp:195
mvsim::VehicleBase::register_vehicle_class
static void register_vehicle_class(const World &parent, const rapidxml::xml_node< char > *xml_node)
Definition: VehicleBase.cpp:96
std
mvsim::World::parse_tag_include
void parse_tag_include(const XmlParserContext &ctx)
Definition: World_load_xml.cpp:259
mvsim::WorldElementBase::Ptr
std::shared_ptr< WorldElementBase > Ptr
Definition: WorldElementBase.h:30
mvsim::World::XmlParserContext::node
const rapidxml::xml_node< char > * node
Definition: World.h:823
mvsim::Block::Ptr
std::shared_ptr< Block > Ptr
Definition: Block.h:53
mvsim::World::parse_tag_for
void parse_tag_for(const XmlParserContext &ctx)
Definition: World_load_xml.cpp:311
mvsim::World::parse_tag_marker
void parse_tag_marker(const XmlParserContext &ctx)
Definition: World_load_xml.cpp:376
mvsim::parse_xmlnode_as_param
bool parse_xmlnode_as_param(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="")
Definition: xml_utils.cpp:196
type
GLenum type
Definition: gl.h:1033
rapidxml::xml_attribute::next_attribute
xml_attribute< Ch > * next_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:856
mvsim::VehicleBase::Ptr
std::shared_ptr< VehicleBase > Ptr
Definition: VehicleBase.h:47
root
root
rapidxml.hpp
rapidxml::xml_node::first_attribute
xml_attribute< Ch > * first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1025
mvsim::World::parse_tag_variable
void parse_tag_variable(const XmlParserContext &ctx)
Definition: World_load_xml.cpp:287
mvsim::readXmlTextAndGetRoot
std::tuple< std::shared_ptr< rapidxml::xml_document<> >, rapidxml::xml_node<> * > readXmlTextAndGetRoot(const std::string &xmlData, const std::string &pathToFile)
Definition: xml_utils.cpp:290
mvsim::VehicleBase::factory
static Ptr factory(World *parent, const rapidxml::xml_node< char > *xml_node)
Definition: VehicleBase.cpp:118
rapidxml_print.hpp
rapidxml::xml_node::next_sibling
xml_node< Ch > * next_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1004


mvsim
Author(s):
autogenerated on Wed May 28 2025 02:13:08