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
47#include "StringUtil.hpp"
48
49#define SCENARIOMIMETYPE ".scx"
50
51using namespace rapidxml;
52using namespace ScenarioManager;
53using namespace Data_Structure;
54using namespace Parser;
55using namespace armarx;
56
57// Get current date/time, format is YYYY-MM-DD.HH:mm:ss
58std::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
70template <class T>
71std::string
72formatTTime(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
85std::vector<std::string>
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
113XMLScenarioParser::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
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
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
294{
295 return parseScenario(scenario->getPackage(), scenario->getName(), scenario->getSubfolder());
296}
297
298bool
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
309XMLScenarioParser::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
353void
354XMLScenarioParser::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
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
403std::string
405{
407
408 if (!reader->getRoot("scenario").has_attribute("package"))
409 {
410 return "";
411 }
412
413 return reader->getRoot("scenario").attribute_value("package");
414}
415
416void
417XMLScenarioParser::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
439void
440XMLScenarioParser::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
455 PropertyDefinitionConfigFormatter defFormatter;
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}
std::string currentDateTime()
std::string formatTTime(T chronotp)
#define SCENARIOMIMETYPE
Class containing data about a scenario and its applications.
Definition Scenario.h:57
static std::string getPackageNameFromScx(const std::string &path)
static ScenarioManager::Data_Structure::ScenarioPtr parseScenario(Data_Structure::PackagePtr package, std::string name, std::string subfolder="")
Parses a .xml scenario file and creates a Scenario object out of it.
static void saveScenario(const Data_Structure::ScenarioWPtr &scenario, bool saveApplications)
Saves a Scenario by recreating its .xml file based on its data.
static ScenarioManager::Data_Structure::PackagePtr getScenarioPackage(const ScenarioManager::Data_Structure::ScenarioPtr &scenario, const ScenarioManager::Data_Structure::PackageVectorPtr &packages)
parseScenarioByFile parses an scenario file and returns the curresponding ScenarioPtr.
static ScenarioManager::Data_Structure::ScenarioPtr createNewScenario(const std::string &name, ScenarioManager::Data_Structure::PackagePtr package)
Creates a new scenario with the given name within the given package.
static bool isScenarioexistent(const std::string &name, ScenarioManager::Data_Structure::PackagePtr package, const std::string &subPath="")
isScenarioexistent
static std::vector< std::string > getScenariosFromFolder(const std::string &folder)
Finds all .xml scenario files in a folder and returns a list of paths to them.
Baseclass for all ArmarX applications.
static CMakePackageFinderCache GlobalCache
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
std::string getBinaryDir() const
std::string getScenariosDir() const
IceProperties stores ice properties and resolves property inheritance.
virtual void setInheritanceSolver(const InheritanceSolverPtr &inheritanceSolver)
Sets an inheritance solver in case of testing or using a non-default solver.
static Ice::PropertiesPtr create(const Ice::PropertiesPtr &iceProperties=nullptr)
RapidXmlReaderNode first_node(const char *name=nullptr) const
std::vector< RapidXmlReaderNode > nodes(const char *name=nullptr) const
bool has_attribute(const char *attrName) const
bool has_node(const char *nodeName) const
std::string attribute_value(const char *attrName) const
static RapidXmlReaderPtr FromFile(const std::string &path)
RapidXmlWriterNode & append_bool_attribute(const std::string &name, const std::string &trueValue, const std::string &falseValue, bool value)
RapidXmlWriterNode append_node(const std::string &name)
RapidXmlWriterNode & append_attribute(const std::string &name, const std::string &value)
void saveToFile(const std::string &path, bool indent)
RapidXmlWriterNode createRootNode(const std::string &name)
#define ARMARX_ERROR_S
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:216
#define ARMARX_INFO_S
Definition Logging.h:202
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_WARNING_S
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:213
::IceInternal::Handle<::Ice::Properties > PropertiesPtr
std::shared_ptr< Scenario > ScenarioPtr
Definition Scenario.h:35
std::shared_ptr< std::vector< ScenarioManager::Data_Structure::PackagePtr > > PackageVectorPtr
Definition Package.h:123
std::weak_ptr< Scenario > ScenarioWPtr
Definition Scenario.h:36
std::shared_ptr< Package > PackagePtr
Definition Package.h:122
std::shared_ptr< ApplicationInstance > ApplicationInstancePtr
This file offers overloads of toIce() and fromIce() functions for STL container types.
fs::path remove_trailing_separator(fs::path p)
Definition filesystem.h:34
IceUtil::Handle< Application > ApplicationPtr
Definition Application.h:93
std::shared_ptr< RapidXmlReader > RapidXmlReaderPtr
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.