World_load_xml.cpp
Go to the documentation of this file.
1 /*+-------------------------------------------------------------------------+
2  | MultiVehicle simulator (libmvsim) |
3  | |
4  | Copyright (C) 2014-2023 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/lock_helper.h>
11 #include <mrpt/system/filesystem.h> // extractFileDirectory()
12 #include <mvsim/World.h>
13 
14 #include <algorithm> // count()
15 #include <map>
16 #include <rapidxml.hpp>
17 #include <rapidxml_print.hpp>
18 #include <rapidxml_utils.hpp>
19 #include <sstream>
20 #include <stdexcept>
21 
22 #include "parse_utils.h"
23 #include "xml_utils.h"
24 
25 using namespace mvsim;
26 using namespace std;
27 
28 void World::load_from_XML_file(const std::string& xmlFileNamePath)
29 {
30  rapidxml::file<> fil_xml(xmlFileNamePath.c_str());
31  load_from_XML(fil_xml.data(), xmlFileNamePath.c_str());
32 }
33 
35  const std::string& xml_text, const std::string& fileNameForPath)
36 {
37  using namespace std;
38  using namespace rapidxml;
39 
40  // Extract base path of file:
41  basePath_ =
42  mrpt::system::trim(mrpt::system::extractFileDirectory(fileNameForPath));
43  // printf("[World] INFO: Using base path='%s'\n",basePath_.c_str());
44 
45  auto lck = mrpt::lockHelper(world_cs_); // Protect multithread access
46 
47  // Clear the existing world.
48  this->clear_all();
49 
50  // Create anchor object for standalone (environment-attached) sensors:
51  DummyInvisibleBlock::Ptr standaloneSensorHost =
52  std::make_shared<DummyInvisibleBlock>(this);
53 
54  simulableObjectsMtx_.lock();
55  simulableObjects_.emplace("__standaloneSensorHost", standaloneSensorHost);
56  simulableObjectsMtx_.unlock();
57 
58  // Parse the XML input:
59  const auto [xml, root] = readXmlTextAndGetRoot(xml_text, fileNameForPath);
60  (void)xml; // unused
61 
62  if (0 != strcmp(root->name(), "mvsim_world"))
63  throw runtime_error(mrpt::format(
64  "XML root element is '%s' ('mvsim_world' expected)", root->name()));
65 
66  // Optional: format version attrib:
67  const xml_attribute<>* attrb_version = root->first_attribute("version");
68  int version_major = 1, version_min = 0;
69  if (attrb_version)
70  {
71  int ret = sscanf(
72  attrb_version->value(), "%i.%i", &version_major, &version_min);
73  if (ret != 2)
74  throw runtime_error(mrpt::format(
75  "Error parsing version attribute: '%s' ('%%i.%%i' "
76  "expected)",
77  attrb_version->value()));
78  }
79 
80  // register tags:
81  register_standard_xml_tag_parsers();
82 
83  // ------------------------------------------------
84  // Process all xml nodes
85  // ------------------------------------------------
86  xml_node<>* node = root->first_node();
87  while (node)
88  {
89  internal_recursive_parse_XML({node, basePath_});
90 
91  // Move on to next node:
92  node = node->next_sibling(nullptr);
93  }
94 
95  internal_initialize();
96 }
97 
99 {
100  if (!xmlParsers_.empty()) return;
101 
102  register_tag_parser("vehicle", &World::parse_tag_vehicle);
103  register_tag_parser("vehicle:class", &World::parse_tag_vehicle_class);
104 
105  register_tag_parser("block", &World::parse_tag_block);
106  register_tag_parser("block:class", &World::parse_tag_block_class);
107 
108  register_tag_parser("element", &World::parse_tag_element);
109  register_tag_parser("sensor", &World::parse_tag_sensor);
110  register_tag_parser("gui", &World::parse_tag_gui);
111  register_tag_parser("lights", &World::parse_tag_lights);
112  register_tag_parser("walls", &World::parse_tag_walls);
113  register_tag_parser("include", &World::parse_tag_include);
114  register_tag_parser("variable", &World::parse_tag_variable);
115  register_tag_parser("for", &World::parse_tag_for);
116  register_tag_parser("if", &World::parse_tag_if);
117 }
118 
120 {
121  using namespace rapidxml;
122  using namespace std::string_literals;
123 
124  const rapidxml::xml_node<>* node = ctx.node;
125  ASSERT_(node);
126 
127  // push relative directory state:
128  const auto savedBasePath = basePath_;
129  basePath_ = ctx.currentBasePath;
130 
131  // Known tag parser?
132  if (auto itParser = xmlParsers_.find(node->name());
133  itParser != xmlParsers_.end())
134  {
135  itParser->second(ctx);
136  }
137  else
138  { // Default: Check if it's a parameter:
139  if (!parse_xmlnode_as_param(*node, otherWorldParams_))
140  {
141  // Unknown element!!
142  MRPT_LOG_WARN_STREAM(
143  "[World::load_from_XML] *Warning* Ignoring unknown XML node "
144  "type '"
145  << node->name() << "'");
146  }
147  }
148 
149  // pop relative directory state:
150  basePath_ = savedBasePath;
151 }
152 
154 {
155  // <element class='*'> entries:
157  worldElements_.emplace_back(e);
158 
159  auto lckListObjs = mrpt::lockHelper(getListOfSimulableObjectsMtx());
160  simulableObjects_.emplace(
161  e->getName(), std::dynamic_pointer_cast<Simulable>(e));
162 }
163 
165 {
166  // <vehicle> entries:
167  VehicleBase::Ptr veh = VehicleBase::factory(this, ctx.node);
168  // Assign each vehicle a unique "index" number
169  veh->setVehicleIndex(vehicles_.size());
170 
171  ASSERTMSG_(
172  vehicles_.count(veh->getName()) == 0,
173  mrpt::format("Duplicated vehicle name: '%s'", veh->getName().c_str()));
174 
175  vehicles_.insert(VehicleList::value_type(veh->getName(), veh));
176 
177  auto lckListObjs = mrpt::lockHelper(getListOfSimulableObjectsMtx());
178  simulableObjects_.emplace(
179  veh->getName(), std::dynamic_pointer_cast<Simulable>(veh));
180 }
181 
183 {
184  // <vehicle:class> entries:
186 }
187 
189 {
190  // top-level <sensor> entries:
191  auto lckListObjs = mrpt::lockHelper(getListOfSimulableObjectsMtx());
192 
193  DummyInvisibleBlock::Ptr standaloneSensorHost =
194  std::dynamic_pointer_cast<DummyInvisibleBlock>(
195  simulableObjects_.find("__standaloneSensorHost")->second);
196  ASSERT_(standaloneSensorHost);
197 
198  SensorBase::Ptr sensor =
199  SensorBase::factory(*standaloneSensorHost, ctx.node);
200  standaloneSensorHost->add_sensor(sensor);
201 }
202 
204 {
205  // <block> entries:
206  Block::Ptr block = Block::factory(this, ctx.node);
207  insertBlock(block);
208 }
209 
211 {
212  //
214 }
215 
217 {
218  //
219  guiOptions_.parse_from(*ctx.node, *this);
220 }
221 
223 {
224  //
225  lightOptions_.parse_from(*ctx.node, *this);
226 }
227 
229 {
230  process_load_walls(*ctx.node);
231 }
232 
234 {
235  auto fileAttrb = ctx.node->first_attribute("file");
236  ASSERTMSG_(
237  fileAttrb,
238  "XML tag '<include />' must have a 'file=\"xxx\"' attribute)");
239 
240  const std::string relFile =
241  mvsim::parse(fileAttrb->value(), user_defined_variables());
242 
243  const auto absFile = this->local_to_abs_path(relFile);
244  MRPT_LOG_DEBUG_STREAM("XML parser: including file: '" << absFile << "'");
245 
246  std::map<std::string, std::string> vars;
247  for (auto attr = ctx.node->first_attribute(); attr;
248  attr = attr->next_attribute())
249  {
250  if (strcmp(attr->name(), "file") == 0) continue;
251  vars[attr->name()] = attr->value();
252  }
253 
254  const auto [xml, root] = readXmlAndGetRoot(absFile, vars);
255  (void)xml; // unused
256 
257  // recursive parse:
258  const auto newBasePath =
259  mrpt::system::trim(mrpt::system::extractFileDirectory(absFile));
260  internal_recursive_parse_XML({root, newBasePath});
261 }
262 
264 {
265  auto nameAttr = ctx.node->first_attribute("name");
266  ASSERTMSG_(
267  nameAttr,
268  "XML tag '<variable />' must have a 'name=\"xxx\"' attribute)");
269  const auto name = nameAttr->value();
270 
271  auto valueAttr = ctx.node->first_attribute("value");
272  ASSERTMSG_(
273  valueAttr,
274  "XML tag '<variable />' must have a 'value=\"xxx\"' attribute)");
275 
276  const std::string finalValue =
277  mvsim::parse(valueAttr->value(), userDefinedVariables_);
278 
279  userDefinedVariables_[name] = finalValue;
280 }
281 
283 {
284  auto varAttr = ctx.node->first_attribute("var");
285  ASSERTMSG_(
286  varAttr, "XML tag '<for />' must have a 'var=\"xxx\"' attribute)");
287  const auto varName = varAttr->value();
288 
289  auto varFrom = ctx.node->first_attribute("from");
290  ASSERTMSG_(
291  varFrom, "XML tag '<for />' must have a 'from=\"xxx\"' attribute)");
292  const auto fromStr = mvsim::parse(varFrom->value(), userDefinedVariables_);
293 
294  auto varTo = ctx.node->first_attribute("to");
295  ASSERTMSG_(varTo, "XML tag '<for />' must have a 'to=\"xxx\"' attribute)");
296  const auto toStr = mvsim::parse(varTo->value(), userDefinedVariables_);
297 
298  bool forBodyEmpty = true;
299 
300  for (auto childNode = ctx.node->first_node(); childNode;
301  childNode = childNode->next_sibling())
302  {
303  forBodyEmpty = false;
304  for (int curVal = std::stoi(fromStr); curVal <= std::stoi(toStr);
305  curVal++)
306  {
307  userDefinedVariables_[varName] = std::to_string(curVal);
308  internal_recursive_parse_XML({childNode, basePath_});
309  }
310  }
311 
312  if (forBodyEmpty)
313  {
314  MRPT_LOG_WARN_STREAM(
315  "[World::load_from_XML] *Warning* <for ...> </for> loop has no "
316  "contents (!): '"
317  << ctx.node->value() << "'");
318  }
319 }
320 
322 {
323  auto varCond = ctx.node->first_attribute("condition");
324  ASSERTMSG_(
325  varCond, "XML tag '<if />' must have a 'condition=\"xxx\"' attribute)");
326  const auto str = mvsim::parse(varCond->value(), userDefinedVariables_);
327 
328  // is it "true"?
329  std::optional<int> intVal;
330  char* retStr = nullptr;
331  const long long ret = std::strtoll(str.c_str(), &retStr, 0 /*auto base*/);
332  if (retStr != 0 && retStr != str.c_str()) intVal = ret;
333 
334  bool isTrue = str == "y" || str == "Y" || str == "yes" || str == "Yes" ||
335  str == "YES" || str == "true" || str == "True" ||
336  str == "TRUE" || str == "on" || str == "ON" || str == "On" ||
337  (intVal.has_value() && intVal.value() != 0);
338 
339  if (!isTrue) return;
340 
341  for (auto childNode = ctx.node->first_node(); childNode;
342  childNode = childNode->next_sibling())
343  {
344  internal_recursive_parse_XML({childNode, basePath_});
345  }
346 }
This file contains rapidxml parser and DOM implementation.
void parse_tag_for(const XmlParserContext &ctx)
void parse_tag_include(const XmlParserContext &ctx)
static Ptr factory(World *parent, const rapidxml::xml_node< char > *xml_node, const char *class_name=nullptr)
void parse_tag_variable(const XmlParserContext &ctx)
Represents data loaded from a file.
static Ptr factory(World *parent, const rapidxml::xml_node< char > *xml_node)
void parse_tag_block_class(const XmlParserContext &ctx)
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:202
static Ptr factory(World *parent, const rapidxml::xml_node< char > *xml_node)
Definition: Block.cpp:72
static void register_block_class(const rapidxml::xml_node< char > *xml_node)
Definition: Block.cpp:52
void parse_tag_lights(const XmlParserContext &ctx)
void parse_tag_element(const XmlParserContext &ctx)
<element>
void internal_recursive_parse_XML(const XmlParserContext &ctx)
This will parse a main XML file, or its included.
Ch * name() const
Definition: rapidxml.hpp:673
void parse_tag_block(const XmlParserContext &ctx)
std::shared_ptr< Block > Ptr
Definition: Block.h:37
xml_node< Ch > * first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:936
void parse_tag_vehicle(const XmlParserContext &ctx)
<vehicle>
std::shared_ptr< WorldElementBase > Ptr
void register_standard_xml_tag_parsers()
void parse_tag_if(const XmlParserContext &ctx)
std::shared_ptr< SensorBase > Ptr
Definition: SensorBase.h:29
std::shared_ptr< VehicleBase > Ptr
Definition: VehicleBase.h:44
void parse_tag_walls(const XmlParserContext &ctx)
std::string parse(const std::string &input, const std::map< std::string, std::string > &variableNamesValues={})
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:333
static void register_vehicle_class(const rapidxml::xml_node< char > *xml_node)
const std::string currentBasePath
Definition: World.h:666
std::shared_ptr< DummyInvisibleBlock > Ptr
Definition: Block.h:194
void load_from_XML_file(const std::string &xmlFileNamePath)
void load_from_XML(const std::string &xml_text, const std::string &fileNameForPath=std::string("."))
typedef void(GLAD_API_PTR *GLDEBUGPROC)(GLenum source
Ch * value() const
Definition: rapidxml.hpp:692
void parse_tag_vehicle_class(const XmlParserContext &ctx)
This file contains rapidxml printer implementation.
xml_node< Ch > * next_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1004
static SensorBase::Ptr factory(Simulable &parent, const rapidxml::xml_node< char > *xml_node)
Definition: SensorBase.cpp:92
void parse_tag_sensor(const XmlParserContext &ctx)
<sensor>
void parse_tag_gui(const XmlParserContext &ctx)
xml_attribute< Ch > * first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1025
const rapidxml::xml_node< char > * node
Definition: World.h:665
std::tuple< std::shared_ptr< rapidxml::xml_document<> >, rapidxml::xml_node<> * > readXmlTextAndGetRoot(const std::string &xmlData, const std::string &pathToFile)
Definition: xml_utils.cpp:303
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
Author(s):
autogenerated on Tue Jul 4 2023 03:08:22