XMLScenarioParser.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5  *
6  * ArmarX is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * ArmarX is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * @package ArmarXCore::core
19  * @author Cedric Seehausen (usdnr at kit dot edu)
20  * @date 2016
21  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22  * GNU General Public License
23  */
24 
25 
26 #include "XMLScenarioParser.h"
27 
28 #include <ctime>
29 #include <filesystem>
30 #include <fstream>
31 #include <iomanip>
32 #include <iostream>
33 #include <string>
34 #include <vector>
35 
44 
45 #include "../data_structure/Application.h"
46 #include "../data_structure/ApplicationInstance.h"
47 #include "StringUtil.hpp"
48 
49 #define SCENARIOMIMETYPE ".scx"
50 
51 using namespace rapidxml;
52 using namespace ScenarioManager;
53 using namespace Data_Structure;
54 using namespace Parser;
55 using namespace armarx;
56 
57 // Get current date/time, format is YYYY-MM-DD.HH:mm:ss
58 std::string
60 {
61  std::time_t now = 0;
62  struct tm tstruct;
63  tstruct = *localtime(&now);
64 
65  std::stringstream ss;
66  ss << std::put_time(&tstruct, "%Y-%m-%d.%X");
67  return ss.str();
68 }
69 
70 template <class T>
71 std::string
72 formatTTime(T chronotp)
73 {
74  auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
75  chronotp - T::clock::now() + std::chrono::system_clock::now());
76  time_t time = std::chrono::system_clock::to_time_t(sctp);
77  struct tm tstruct;
78  tstruct = *localtime(&time);
79 
80  std::stringstream ss;
81  ss << std::put_time(&tstruct, "%Y-%m-%d.%X");
82  return ss.str();
83 }
84 
85 std::vector<std::string>
86 XMLScenarioParser::getScenariosFromFolder(const std::string& folder)
87 {
88  namespace fs = std::filesystem;
89  const fs::path scenarioDir = remove_trailing_separator(folder);
90  std::vector<std::string> result;
91  if (exists(scenarioDir))
92  {
93  try
94  {
95  for (std::filesystem::recursive_directory_iterator end, dir(scenarioDir); dir != end;
96  ++dir)
97  {
98  if (dir->path().extension() == ".scx")
99  {
100  result.push_back(fs::relative(dir->path(), scenarioDir).parent_path().string());
101  }
102  }
103  }
104  catch (std::exception& e)
105  {
106  ARMARX_WARNING_S << "Invalid filepath: " << e.what();
107  }
108  }
109  return result;
110 }
111 
113 XMLScenarioParser::parseScenario(PackagePtr package, std::string scenName, std::string subfolder)
114 {
115  if (package == nullptr)
116  {
117  return ScenarioPtr();
118  }
119 
120 
121  std::string appFolder = package->getScenarioPath();
122  appFolder.append("/");
123  if (!subfolder.empty())
124  {
125  appFolder.append(subfolder + "/");
126  }
127  appFolder.append(scenName);
128  appFolder.append("/");
129 
130  std::string path = appFolder;
131  path.append(scenName);
132  path.append(SCENARIOMIMETYPE);
133 
134  if (!std::filesystem::exists(std::filesystem::path(path)))
135  {
136  return ScenarioPtr(nullptr);
137  }
138 
139  RapidXmlReaderPtr doc = RapidXmlReader::FromFile(path);
140 
141  RapidXmlReaderNode root_node = doc->getRoot();
142 
143  std::string name = root_node.attribute_value("name");
144  std::string creation = root_node.attribute_value("creation");
145  std::string globalConfigName;
146  try
147  {
148  globalConfigName = root_node.attribute_value("globalConfigName");
149  }
150  catch (...)
151  {
152  globalConfigName = "./config/global.cfg";
153  }
154 
155  auto nodeName =
156  root_node.has_attribute("nodeName") ? root_node.attribute_value("nodeName") : "";
157  //string package = root_node->first_attribute("package")->value();
158  std::string lastWriteTime =
159  formatTTime(std::filesystem::last_write_time(std::filesystem::path(path)));
160 
161  //Build Ice Env Variables if there are some
162  std::map<std::string, std::string> iceEnvVariables;
163  if (root_node.has_node("iceEnvVariablesNode"))
164  {
165  RapidXmlReaderNode iceEnvVariablesNode = root_node.first_node("iceEnvVariablesNode");
166  for (RapidXmlReaderNode iceEnvVariableNode : iceEnvVariablesNode.nodes("variable"))
167  {
168  std::string varName = iceEnvVariableNode.attribute_value("name");
169  std::string value = iceEnvVariableNode.value();
170  iceEnvVariables[varName] = value;
171  }
172  }
173 
174  ScenarioPtr result(
175  new Scenario(name, creation, lastWriteTime, package, globalConfigName, subfolder));
176  for (RapidXmlReaderNode application_node : root_node.nodes("application"))
177  {
178  std::string appInstanceStr = application_node.attribute_value("instance");
179  std::string appName = application_node.attribute_value("name");
180  std::string packageName = application_node.attribute_value("package");
181  std::string appNodeName;
182  try
183  {
184  appNodeName = application_node.attribute_value("appNodeName");
185  }
186  catch (...)
187  {
188  appNodeName = "";
189  }
190 
191  bool enabled;
192  try
193  {
194  enabled = application_node.attribute_as_bool("enabled", "true", "false");
195  }
196  catch (...)
197  {
198  enabled = true;
199  }
200 
201  CMakePackageFinder pFinder = CMakePackageFinderCache::GlobalCache.findPackage(packageName);
202 
203  namespace fs = std::filesystem;
204 
205  const fs::path executableDir = pFinder.getBinaryDir();
206 
207  // attribute executableName might not be available
208  const std::string appExecutableName = [&]() -> std::string
209  {
210  try
211  {
212  std::string executableName = application_node.attribute_value(
213  "executableName"); // throws if attribute does not exist
214  return executableName;
215  }
216  catch (...)
217  {
218  if (fs::exists(executableDir / (appName + "Run")))
219  {
220  return appName + "Run";
221  }
222 
223  if (fs::exists(executableDir / (appName + "AppRun")))
224  {
225  return appName + "AppRun";
226  }
227 
228  if (fs::exists(executableDir / (appName + "_run")))
229  {
230  return appName + "_run";
231  }
232 
233  ARMARX_WARNING << "Scenario '" + name + "': App `" + appName +
234  "` could not be found.";
235  ARMARX_WARNING << executableDir / (appName + "Run");
236  return appName;
237  }
238  }();
239 
240  ApplicationPtr application(
241  new Application(appExecutableName, pFinder.getBinaryDir(), packageName));
242 
243  ApplicationInstancePtr appInstance;
244  if (appInstanceStr.empty())
245  {
246  appInstance =
247  std::make_shared<ApplicationInstance>(*application,
248  appInstanceStr,
249  appFolder + "config/" + appName + ".cfg",
250  result,
251  appNodeName,
252  enabled);
253  }
254  else
255  {
256  appInstance = std::make_shared<ApplicationInstance>(*application,
257  appInstanceStr,
258  appFolder + "config/" + appName +
259  "." + appInstanceStr + ".cfg",
260  result,
261  appNodeName,
262  enabled);
263  }
264  appInstance->setNodeName(appNodeName);
265 
266  appInstance->updateFound();
267  result->addApplication(appInstance);
268  }
269 
270  //load Global conf
271  Ice::PropertiesPtr cfgProperties = IceProperties::create();
272  auto* cfgInternal = dynamic_cast<armarx::IceProperties*>(cfgProperties.get());
273  cfgInternal->setInheritanceSolver(nullptr);
274 
275  if (result->isGlobalConfigFileexistent())
276  {
277  cfgProperties->load(result->getGlobalConfigPath());
278  }
279 
280  Ice::PropertyDict dict = cfgProperties->getPropertiesForPrefix("");
281 
282  for (auto const& property : dict)
283  {
284  result->getGlobalConfig()->defineOptionalProperty<std::string>(
285  property.first, "::NOT_DEFINED::", "Custom Property");
286  result->getGlobalConfig()->getProperties()->setProperty(property.first, property.second);
287  }
288 
289  return result;
290 }
291 
293 XMLScenarioParser::parseScenario(ScenarioPtr scenario)
294 {
295  return parseScenario(scenario->getPackage(), scenario->getName(), scenario->getSubfolder());
296 }
297 
298 bool
299 XMLScenarioParser::isScenarioexistent(const std::string& name,
300  PackagePtr package,
301  const std::string& subPath)
302 {
303  std::vector<std::string> paths = getScenariosFromFolder(package->getScenarioPath());
304 
305  return std::find(paths.begin(), paths.end(), subPath + name) != paths.end();
306 }
307 
309 XMLScenarioParser::createNewScenario(const std::string& name, PackagePtr package)
310 {
311  std::string scenarioDir = package->getScenarioPath();
312  if (scenarioDir.empty())
313  {
314  throw LocalException("The scenarios directory variable of " + package->getName() +
315  " is empty - did you run CMake successfully?");
316  }
317  if (!package->isScenarioPathWritable())
318  {
320  << "Warning: Scenario Path is not writable. Unable to create a new Scenario.";
321  return ScenarioPtr(nullptr);
322  }
323 
324  RapidXmlWriter doc;
325 
326  RapidXmlWriterNode root_node = doc.createRootNode("scenario");
327 
328  root_node.append_attribute("name", name);
329  root_node.append_attribute("package", package->getName());
330 
331  std::string currentDateStr = currentDateTime();
332  root_node.append_attribute("creation", currentDateStr);
333 
334  if (!std::filesystem::create_directories(scenarioDir.append("/").append(name)) ||
335  !std::filesystem::create_directories(scenarioDir + "/config"))
336  {
337  ARMARX_ERROR_S << "Can not create Scenario in package " << package->getName();
338  return ScenarioPtr(nullptr);
339  }
340 
341  doc.saveToFile(scenarioDir + "/" + name + SCENARIOMIMETYPE, true);
342 
343  ARMARX_INFO_S << "Saved scenario file: " << scenarioDir + "/" + name + SCENARIOMIMETYPE;
344 
345 
346  std::ofstream out2(scenarioDir.append("/config/global.cfg"));
347  out2 << "";
348  out2.close();
349 
350  return parseScenario(package, name);
351 }
352 
353 void
354 XMLScenarioParser::saveScenario(const ScenarioWPtr& wScenario, bool saveApplications)
355 {
356  ScenarioPtr scenario = wScenario.lock();
357 
358  RapidXmlWriter doc;
359 
360  RapidXmlWriterNode scenarioNode = doc.createRootNode("scenario");
361 
362  scenarioNode.append_attribute("name", scenario->getName());
363 
364 
365  std::string currentDateStr = currentDateTime();
366  scenario->setLastChangedTime(currentDateStr);
367 
368  scenarioNode.append_attribute("creation", scenario->getCreationTime());
369  scenarioNode.append_attribute("globalConfigName", scenario->getGlobalConfigName());
370  scenarioNode.append_attribute("package", scenario->getPackage()->getName());
371  scenarioNode.append_attribute("nodeName", scenario->getNodeName());
372 
373 
374  saveScenarioApplications(scenario, scenarioNode, saveApplications);
375 
376 
377  std::string path = scenario->getPath();
378  if (scenario->isScenarioFileWriteable())
379  {
380  doc.saveToFile(path, true);
381  }
382 
383 
384  saveScenarioGlobalConfig(scenario);
385 }
386 
388 XMLScenarioParser::getScenarioPackage(const ScenarioPtr& scenario, const PackageVectorPtr& packages)
389 {
390  for (auto package : *packages)
391  {
392  CMakePackageFinder pFinder =
393  CMakePackageFinderCache::GlobalCache.findPackage(package->getName());
394  std::string scenarioDir = pFinder.getScenariosDir();
395  if (scenario->getPath().find(scenarioDir) != std::string::npos)
396  {
397  return package;
398  }
399  }
400  return PackagePtr();
401 }
402 
403 std::string
404 XMLScenarioParser::getPackageNameFromScx(const std::string& path)
405 {
406  RapidXmlReaderPtr reader = RapidXmlReader::FromFile(path);
407 
408  if (!reader->getRoot("scenario").has_attribute("package"))
409  {
410  return "";
411  }
412 
413  return reader->getRoot("scenario").attribute_value("package");
414 }
415 
416 void
417 XMLScenarioParser::saveScenarioApplications(ScenarioPtr scenario,
418  armarx::RapidXmlWriterNode& scenarioNode,
419  bool saveApplications)
420 {
421  std::vector<ApplicationInstancePtr> applicationInstances = *scenario->getApplications();
422  for (const auto& app : applicationInstances)
423  {
424  if (saveApplications && app->isConfigWritable())
425  {
426  app->save();
427  }
428 
429  RapidXmlWriterNode applicationNode = scenarioNode.append_node("application");
430  applicationNode.append_attribute("name", app->getName());
431  // applicationNode.append_attribute("executable", app->getExecutableName());
432  applicationNode.append_attribute("instance", app->getInstanceName());
433  applicationNode.append_attribute("package", app->getPackageName());
434  applicationNode.append_attribute("nodeName", app->getNodeName());
435  applicationNode.append_bool_attribute("enabled", "true", "false", app->getEnabled());
436  }
437 }
438 
439 void
440 XMLScenarioParser::saveScenarioGlobalConfig(ScenarioPtr scenario)
441 {
442  // Don't overwrite if config is linked
443  if (std::filesystem::is_symlink(scenario->getGlobalConfigPath()))
444  {
445  return;
446  }
447 
448  PropertyDefinitionsPtr props = scenario->getGlobalConfig();
449  armarx::IceProperties* propsInternals =
450  dynamic_cast<armarx::IceProperties*>(props->getProperties().get());
451  propsInternals->setInheritanceSolver(nullptr);
452 
453  std::string resultStr;
454 
456  PropertyDefinitionContainerFormatter pdcFormatter(defFormatter);
457  pdcFormatter.setProperties(props->getProperties());
458 
459  resultStr += pdcFormatter.formatPropertyDefinitionContainer(props);
460 
461  //filter Ice.Config
462  size_t begin = resultStr.rfind("# Ice.Config:");
463  std::string ice_set_value = "Ice.Config = <set value!>";
464  std::string ice_not_defined = "Ice.Config = ::NOT_DEFINED::";
465  size_t end = resultStr.rfind(ice_set_value) + ice_set_value.length();
466 
467  if (begin != std::string::npos)
468  {
469  if (end - ice_set_value.length() != std::string::npos)
470  {
471  resultStr.erase(begin, end - begin);
472  }
473  else
474  {
475  end = resultStr.rfind(ice_not_defined) + ice_not_defined.length();
476  if (end - ice_not_defined.length() != std::string::npos)
477  {
478  resultStr.erase(begin, end - begin);
479  }
480  }
481  }
482 
483  std::ofstream cfgFile;
484  cfgFile.open(scenario->getGlobalConfigPath(), std::ofstream::out | std::ofstream::trunc);
485  if (cfgFile.fail() && scenario->isGlobalConfigWritable())
486  {
487  ARMARX_INFO_S << "Failed to write to Global Cfg file at "
488  << scenario->getGlobalConfigPath();
489  return;
490  }
491  cfgFile << resultStr;
492  cfgFile.close();
493 }
armarx::RapidXmlReaderPtr
std::shared_ptr< RapidXmlReader > RapidXmlReaderPtr
Definition: RapidXmlReader.h:67
StringUtil.hpp
armarx::RapidXmlWriter
Definition: RapidXmlWriter.h:122
ScenarioManager::Data_Structure::ScenarioPtr
std::shared_ptr< Scenario > ScenarioPtr
Definition: Scenario.h:35
armarx::CMakePackageFinder::getScenariosDir
std::string getScenariosDir() const
Definition: CMakePackageFinder.h:200
PropertyDefinitionContainerFormatter.h
armarx::remove_trailing_separator
fs::path remove_trailing_separator(fs::path p)
Definition: filesystem.h:34
armarx::RapidXmlReaderNode::attribute_value
std::string attribute_value(const char *attrName) const
Definition: RapidXmlReader.h:230
armarx::CMakePackageFinder
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
Definition: CMakePackageFinder.h:52
filesystem.h
RapidXmlWriter.h
armarx::PropertyDefinitionContainerFormatter
PropertyDefinitionContainerFormatter.
Definition: PropertyDefinitionContainerFormatter.h:41
PropertyDefinitionFormatter.h
armarx::RapidXmlWriterNode::append_bool_attribute
RapidXmlWriterNode & append_bool_attribute(const std::string &name, const std::string &trueValue, const std::string &falseValue, bool value)
Definition: RapidXmlWriter.h:67
ScenarioManager::Data_Structure::PackageVectorPtr
std::shared_ptr< std::vector< ScenarioManager::Data_Structure::PackagePtr > > PackageVectorPtr
Definition: Package.h:124
ApplicationInstancePtr
std::shared_ptr< ScenarioManager::Data_Structure::ApplicationInstance > ApplicationInstancePtr
Definition: StopStrategy.h:8
formatTTime
std::string formatTTime(T chronotp)
Definition: XMLScenarioParser.cpp:72
IceInternal::Handle<::Ice::Properties >
armarx::RapidXmlReaderNode::has_node
bool has_node(const char *nodeName) const
Definition: RapidXmlReader.h:236
armarx::RapidXmlReaderNode::has_attribute
bool has_attribute(const char *attrName) const
Definition: RapidXmlReader.h:243
currentDateTime
std::string currentDateTime()
Definition: XMLScenarioParser.cpp:59
cxxopts::value
std::shared_ptr< Value > value()
Definition: cxxopts.hpp:855
ScenarioManager::Data_Structure::PackagePtr
std::shared_ptr< Package > PackagePtr
Definition: Package.h:122
rapidxml
Definition: rapidxml.hpp:62
ARMARX_ERROR_S
#define ARMARX_ERROR_S
Definition: Logging.h:216
armarx::CMakePackageFinder::getBinaryDir
std::string getBinaryDir() const
Definition: CMakePackageFinder.h:174
PropertyDefinitionConfigFormatter.h
enabled
std::atomic< bool > * enabled
Definition: RemoteGuiWidgetController.cpp:75
armarx::RapidXmlReaderNode
Definition: RapidXmlReader.h:69
XMLScenarioParser.h
armarx::IceProperties::setInheritanceSolver
virtual void setInheritanceSolver(const InheritanceSolverPtr &inheritanceSolver)
Sets an inheritance solver in case of testing or using a non-default solver.
Definition: IceProperties.cpp:233
armarx::PropertyDefinitionConfigFormatter
PropertyDefinitionConfigFormatter.
Definition: PropertyDefinitionConfigFormatter.h:40
armarx::Application
Baseclass for all ArmarX applications.
Definition: Application.h:193
ARMARX_WARNING_S
#define ARMARX_WARNING_S
Definition: Logging.h:213
armarx::RapidXmlWriterNode::append_attribute
RapidXmlWriterNode & append_attribute(const std::string &name, const std::string &value)
Definition: RapidXmlWriter.h:59
armarx::IceProperties
IceProperties stores ice properties and resolves property inheritance.
Definition: IceProperties.h:120
CMakePackageFinder.h
ScenarioManager
Definition: Application.cpp:180
ScenarioManager::Data_Structure::ScenarioWPtr
std::weak_ptr< Scenario > ScenarioWPtr
Definition: Scenario.h:36
armarx::RapidXmlWriter::saveToFile
void saveToFile(const std::string &path, bool indent)
Definition: RapidXmlWriter.h:154
IceUtil::Handle< Application >
armarx::RapidXmlReaderNode::nodes
std::vector< RapidXmlReaderNode > nodes(const char *name=nullptr) const
Definition: RapidXmlReader.h:181
RapidXmlReader.h
armarx::RapidXmlWriter::createRootNode
RapidXmlWriterNode createRootNode(const std::string &name)
Definition: RapidXmlWriter.h:137
armarx::RapidXmlWriterNode::append_node
RapidXmlWriterNode append_node(const std::string &name)
Definition: RapidXmlWriter.h:91
ARMARX_INFO_S
#define ARMARX_INFO_S
Definition: Logging.h:202
Logging.h
T
float T
Definition: UnscentedKalmanFilterTest.cpp:38
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:193
armarx::RapidXmlWriterNode
Definition: RapidXmlWriter.h:35
armarx::RapidXmlReaderNode::first_node
RapidXmlReaderNode first_node(const char *name=nullptr) const
Definition: RapidXmlReader.h:156
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
SCENARIOMIMETYPE
#define SCENARIOMIMETYPE
Definition: XMLScenarioParser.cpp:49