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 
153  auto nodeName = root_node.has_attribute("nodeName") ? root_node.attribute_value("nodeName") : "";
154  //string package = root_node->first_attribute("package")->value();
155  std::string lastWriteTime = formatTTime(std::filesystem::last_write_time(std::filesystem::path(path)));
156 
157  //Build Ice Env Variables if there are some
158  std::map<std::string, std::string> iceEnvVariables;
159  if (root_node.has_node("iceEnvVariablesNode"))
160  {
161  RapidXmlReaderNode iceEnvVariablesNode = root_node.first_node("iceEnvVariablesNode");
162  for (RapidXmlReaderNode iceEnvVariableNode : iceEnvVariablesNode.nodes("variable"))
163  {
164  std::string varName = iceEnvVariableNode.attribute_value("name");
165  std::string value = iceEnvVariableNode.value();
166  iceEnvVariables[varName] = value;
167  }
168  }
169 
170  ScenarioPtr result(new Scenario(name, creation, lastWriteTime, package, globalConfigName, subfolder));
171  for (RapidXmlReaderNode application_node : root_node.nodes("application"))
172  {
173  std::string appInstanceStr = application_node.attribute_value("instance");
174  std::string appName = application_node.attribute_value("name");
175  std::string packageName = application_node.attribute_value("package");
176  std::string appNodeName;
177  try
178  {
179  appNodeName = application_node.attribute_value("appNodeName");
180  }
181  catch (...)
182  {
183  appNodeName = "";
184  }
185 
186  bool enabled;
187  try
188  {
189  enabled = application_node.attribute_as_bool("enabled", "true", "false");
190  }
191  catch (...)
192  {
193  enabled = true;
194  }
195 
196  CMakePackageFinder pFinder = CMakePackageFinderCache::GlobalCache.findPackage(packageName);
197 
198  namespace fs = std::filesystem;
199 
200  const fs::path executableDir = pFinder.getBinaryDir();
201 
202  // attribute executableName might not be available
203  const std::string appExecutableName = [&]() -> std::string {
204  try{
205  std::string executableName = application_node.attribute_value("executableName"); // throws if attribute does not exist
206  return executableName;
207  }catch(...)
208  {
209  if(fs::exists(executableDir / (appName + "Run")))
210  {
211  return appName + "Run";
212  }
213 
214  if(fs::exists(executableDir / (appName + "AppRun")))
215  {
216  return appName + "AppRun";
217  }
218 
219  if(fs::exists(executableDir / (appName + "_run")))
220  {
221  return appName + "_run";
222  }
223 
224  ARMARX_WARNING << "Scenario '" + name + "': App `" + appName + "` could not be found.";
225  ARMARX_WARNING << executableDir / (appName + "Run");
226  return appName;
227  }
228  }();
229 
230  ApplicationPtr application(new Application(appExecutableName, pFinder.getBinaryDir(), packageName));
231 
232  ApplicationInstancePtr appInstance;
233  if (appInstanceStr.empty())
234  {
235  appInstance = std::make_shared<ApplicationInstance>(*application, appInstanceStr, appFolder + "config/" + appName + ".cfg", result, appNodeName, enabled);
236  }
237  else
238  {
239  appInstance = std::make_shared<ApplicationInstance>(*application, appInstanceStr, appFolder + "config/" + appName + "." + appInstanceStr + ".cfg", result, appNodeName, enabled);
240  }
241  appInstance->setNodeName(appNodeName);
242 
243  appInstance->updateFound();
244  result->addApplication(appInstance);
245  }
246 
247  //load Global conf
248  Ice::PropertiesPtr cfgProperties = IceProperties::create();
249  auto* cfgInternal = dynamic_cast<armarx::IceProperties*>(cfgProperties.get());
250  cfgInternal->setInheritanceSolver(nullptr);
251 
252  if (result->isGlobalConfigFileexistent())
253  {
254  cfgProperties->load(result->getGlobalConfigPath());
255  }
256 
257  Ice::PropertyDict dict = cfgProperties->getPropertiesForPrefix("");
258 
259  for (auto const& property : dict)
260  {
261  result->getGlobalConfig()->defineOptionalProperty<std::string>(property.first, "::NOT_DEFINED::", "Custom Property");
262  result->getGlobalConfig()->getProperties()->setProperty(property.first, property.second);
263  }
264 
265  return result;
266 }
267 
268 ScenarioPtr XMLScenarioParser::parseScenario(ScenarioPtr scenario)
269 {
270  return parseScenario(scenario->getPackage(), scenario->getName(), scenario->getSubfolder());
271 }
272 
273 bool XMLScenarioParser::isScenarioexistent(const std::string &name, PackagePtr package, const std::string& subPath)
274 {
275  std::vector<std::string> paths = getScenariosFromFolder(package->getScenarioPath());
276 
277  return std::find(paths.begin(), paths.end(), subPath + name) != paths.end();
278 }
279 
280 ScenarioPtr XMLScenarioParser::createNewScenario(const std::string& name, PackagePtr package)
281 {
282  std::string scenarioDir = package->getScenarioPath();
283  if (scenarioDir.empty())
284  {
285  throw LocalException("The scenarios directory variable of " + package->getName() + " is empty - did you run CMake successfully?");
286  }
287  if (!package->isScenarioPathWritable())
288  {
289  ARMARX_WARNING_S << "Warning: Scenario Path is not writable. Unable to create a new Scenario.";
290  return ScenarioPtr(nullptr);
291  }
292 
293  RapidXmlWriter doc;
294 
295  RapidXmlWriterNode root_node = doc.createRootNode("scenario");
296 
297  root_node.append_attribute("name", name);
298  root_node.append_attribute("package", package->getName());
299 
300  std::string currentDateStr = currentDateTime();
301  root_node.append_attribute("creation", currentDateStr);
302 
303  if (!std::filesystem::create_directories(scenarioDir.append("/").append(name)) ||
304  !std::filesystem::create_directories(scenarioDir + "/config"))
305  {
306  ARMARX_ERROR_S << "Can not create Scenario in package " << package->getName();
307  return ScenarioPtr(nullptr);
308  }
309 
310  doc.saveToFile(scenarioDir + "/" + name + SCENARIOMIMETYPE, true);
311 
312  ARMARX_INFO_S << "Saved scenario file: " << scenarioDir + "/" + name + SCENARIOMIMETYPE;
313 
314 
315  std::ofstream out2(scenarioDir.append("/config/global.cfg"));
316  out2 << "";
317  out2.close();
318 
319  return parseScenario(package, name);
320 }
321 
322 void XMLScenarioParser::saveScenario(const ScenarioWPtr& wScenario, bool saveApplications)
323 {
324  ScenarioPtr scenario = wScenario.lock();
325 
326  RapidXmlWriter doc;
327 
328  RapidXmlWriterNode scenarioNode = doc.createRootNode("scenario");
329 
330  scenarioNode.append_attribute("name", scenario->getName());
331 
332 
333  std::string currentDateStr = currentDateTime();
334  scenario->setLastChangedTime(currentDateStr);
335 
336  scenarioNode.append_attribute("creation", scenario->getCreationTime());
337  scenarioNode.append_attribute("globalConfigName", scenario->getGlobalConfigName());
338  scenarioNode.append_attribute("package", scenario->getPackage()->getName());
339  scenarioNode.append_attribute("nodeName", scenario->getNodeName());
340 
341 
342  saveScenarioApplications(scenario, scenarioNode, saveApplications);
343 
344 
345  std::string path = scenario->getPath();
346  if (scenario->isScenarioFileWriteable())
347  {
348  doc.saveToFile(path, true);
349  }
350 
351 
352  saveScenarioGlobalConfig(scenario);
353 }
354 
355 PackagePtr XMLScenarioParser::getScenarioPackage(const ScenarioPtr& scenario, const PackageVectorPtr& packages)
356 {
357  for (auto package : *packages)
358  {
359  CMakePackageFinder pFinder = CMakePackageFinderCache::GlobalCache.findPackage(package->getName());
360  std::string scenarioDir = pFinder.getScenariosDir();
361  if (scenario->getPath().find(scenarioDir) != std::string::npos)
362  {
363  return package;
364  }
365  }
366  return PackagePtr();
367 }
368 
369 std::string XMLScenarioParser::getPackageNameFromScx(const std::string& path)
370 {
371  RapidXmlReaderPtr reader = RapidXmlReader::FromFile(path);
372 
373  if (!reader->getRoot("scenario").has_attribute("package"))
374  {
375  return "";
376  }
377 
378  return reader->getRoot("scenario").attribute_value("package");
379 }
380 
381 void XMLScenarioParser::saveScenarioApplications(ScenarioPtr scenario, armarx::RapidXmlWriterNode& scenarioNode, bool saveApplications)
382 {
383  std::vector<ApplicationInstancePtr> applicationInstances = *scenario->getApplications();
384  for (const auto &app : applicationInstances)
385  {
386  if (saveApplications && app->isConfigWritable())
387  {
388  app->save();
389  }
390 
391  RapidXmlWriterNode applicationNode = scenarioNode.append_node("application");
392  applicationNode.append_attribute("name", app->getName());
393  // applicationNode.append_attribute("executable", app->getExecutableName());
394  applicationNode.append_attribute("instance", app->getInstanceName());
395  applicationNode.append_attribute("package", app->getPackageName());
396  applicationNode.append_attribute("nodeName", app->getNodeName());
397  applicationNode.append_bool_attribute("enabled", "true", "false", app->getEnabled());
398  }
399 }
400 
401 void XMLScenarioParser::saveScenarioGlobalConfig(ScenarioPtr scenario)
402 {
403  // Don't overwrite if config is linked
404  if (std::filesystem::is_symlink(scenario->getGlobalConfigPath()))
405  {
406  return;
407  }
408 
409  PropertyDefinitionsPtr props = scenario->getGlobalConfig();
410  armarx::IceProperties* propsInternals = dynamic_cast<armarx::IceProperties*>(props->getProperties().get());
411  propsInternals->setInheritanceSolver(nullptr);
412 
413  std::string resultStr;
414 
416  PropertyDefinitionContainerFormatter pdcFormatter(defFormatter);
417  pdcFormatter.setProperties(props->getProperties());
418 
419  resultStr += pdcFormatter.formatPropertyDefinitionContainer(props);
420 
421  //filter Ice.Config
422  size_t begin = resultStr.rfind("# Ice.Config:");
423  std::string ice_set_value = "Ice.Config = <set value!>";
424  std::string ice_not_defined = "Ice.Config = ::NOT_DEFINED::";
425  size_t end = resultStr.rfind(ice_set_value) + ice_set_value.length();
426 
427  if (begin != std::string::npos)
428  {
429  if (end - ice_set_value.length() != std::string::npos)
430  {
431  resultStr.erase(begin, end - begin);
432  }
433  else
434  {
435  end = resultStr.rfind(ice_not_defined) + ice_not_defined.length();
436  if (end - ice_not_defined.length() != std::string::npos)
437  {
438  resultStr.erase(begin, end - begin);
439  }
440  }
441  }
442 
443  std::ofstream cfgFile;
444  cfgFile.open(scenario->getGlobalConfigPath(), std::ofstream::out | std::ofstream::trunc);
445  if (cfgFile.fail() && scenario->isGlobalConfigWritable())
446  {
447  ARMARX_INFO_S << "Failed to write to Global Cfg file at " << scenario->getGlobalConfigPath();
448  return;
449  }
450  cfgFile << resultStr;
451  cfgFile.close();
452 }
armarx::RapidXmlReaderPtr
std::shared_ptr< RapidXmlReader > RapidXmlReaderPtr
Definition: RapidXmlReader.h:66
StringUtil.hpp
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
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
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
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