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