source: finroc_plugins_runtime_construction/tFinstructable.cpp @ 173:7b8f89b97f73

17.03 tip
Last change on this file since 173:7b8f89b97f73 was 173:7b8f89b97f73, checked in by Max Reichardt <max.reichardt@…>, 23 months ago

Fixes issue loading parameter config entries for composite composite components in the uncommon constellation that a sub-component has the same name

File size: 53.4 KB
Line 
1//
2// You received this file as part of Finroc
3// A framework for intelligent robot control
4//
5// Copyright (C) Finroc GbR (finroc.org)
6//
7// This program is free software; you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation; either version 2 of the License, or
10// (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License along
18// with this program; if not, write to the Free Software Foundation, Inc.,
19// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20//
21//----------------------------------------------------------------------
22/*!\file    plugins/runtime_construction/tFinstructable.cpp
23 *
24 * \author  Max Reichardt
25 *
26 * \date    2012-12-02
27 *
28 */
29//----------------------------------------------------------------------
30#include "plugins/runtime_construction/tFinstructable.h"
31
32//----------------------------------------------------------------------
33// External includes (system with <>, local with "")
34//----------------------------------------------------------------------
35#include <set>
36#include "rrlib/util/string.h"
37#include "core/file_lookup.h"
38#include "core/tRuntimeEnvironment.h"
39#include "plugins/parameters/internal/tParameterInfo.h"
40#include "rrlib/rtti/tStaticTypeRegistration.h"
41
42//----------------------------------------------------------------------
43// Internal includes with ""
44//----------------------------------------------------------------------
45#include "plugins/runtime_construction/tEditableInterfaces.h"
46#include "plugins/runtime_construction/dynamic_loading.h"
47#include "plugins/runtime_construction/tInclude.h"
48
49//----------------------------------------------------------------------
50// Debugging
51//----------------------------------------------------------------------
52#include <cassert>
53
54//----------------------------------------------------------------------
55// Namespace usage
56//----------------------------------------------------------------------
57
58//----------------------------------------------------------------------
59// Namespace declaration
60//----------------------------------------------------------------------
61namespace finroc
62{
63namespace runtime_construction
64{
65
66//----------------------------------------------------------------------
67// Forward declarations / typedefs / enums
68//----------------------------------------------------------------------
69typedef core::tFrameworkElement tFrameworkElement;
70typedef tFrameworkElement::tFlag tFlag;
71typedef tInclude tIncludeElement;
72
73//----------------------------------------------------------------------
74// Const values
75//----------------------------------------------------------------------
76
77namespace
78{
79
80/*! We do not want to have this prefix in XML file names, as this will not be found when a system installation is used */
81const char* cUNWANTED_XML_FILE_PREFIX = "sources/cpp/";
82
83/*! Characters that are not escaped in path URIs */
84const char* cUNENCODED_RESERVED_CHARACTERS_PATH = "!$&'()*+,;= @";
85
86/*! Current version of file format (YYMM) */
87const uint cVERSION = 1703;
88
89//----------------------------------------------------------------------
90// Implementation
91//----------------------------------------------------------------------
92
93/*! Thread currently saving finstructable group */
94rrlib::thread::tThread* saving_thread = nullptr;
95
96/*! Temporary variable for saving: .so files that should be loaded prior to instantiating this group */
97std::set<tSharedLibrary> dependencies_tmp;
98
99/*! Loaded finroc libraries at startup */
100std::set<tSharedLibrary> startup_loaded_finroc_libs;
101
102
103rrlib::uri::tPath ReplaceInterfaceInPath(const rrlib::uri::tPath& path, const std::string& new_interface)
104{
105  if (path.Size() < 2)
106  {
107    return path;
108  }
109  std::vector<rrlib::uri::tStringRange> path_components;
110  for (uint i = 0; i < path.Size(); i++)
111  {
112    path_components.push_back(path[i]);
113  }
114  path_components[path_components.size() - 2] = rrlib::uri::tStringRange(new_interface);
115  return rrlib::uri::tPath(path.IsAbsolute(), path_components.begin(), path_components.end());
116}
117
118/*! Specialized tFinstructable class to realize include mechanism */
119class tIncludeFinstructable : public tFinstructable
120{
121public:
122  /*!
123   * Root framework element that this annotation belongs to
124   * Optional setting that overrides element that annotation is attached to
125   */
126  core::tFrameworkElement* root_framework_element = nullptr;
127  std::string xml_file;
128  core::tFrameworkElement* origin = nullptr;
129
130  tIncludeFinstructable() : tFinstructable(xml_file)
131  {}
132};
133
134// Returns tIncludeFinstructable if 'finstructable' is one - otherwise nullptr
135tIncludeFinstructable* IncludeFinstructable(tFinstructable* finstructable)
136{
137  return typeid(*finstructable) == typeid(tIncludeFinstructable) ? static_cast<tIncludeFinstructable*>(finstructable) : nullptr;
138}
139const tIncludeFinstructable* IncludeFinstructable(const tFinstructable* finstructable)
140{
141  return typeid(*finstructable) == typeid(tIncludeFinstructable) ? static_cast<const tIncludeFinstructable*>(finstructable) : nullptr;
142}
143
144bool HasNonDefaultFinstructInfo(const parameters::internal::tParameterInfo& info)
145{
146  for (auto & setting : info.Settings())
147  {
148    if (setting.high_precedence && setting.origin == nullptr)
149    {
150      return true;
151    }
152  }
153  return false;
154}
155
156class tOnFullInitialization : public core::tAnnotation
157{
158public:
159  tOnFullInitialization(tFinstructable& finstructable, bool& fully_initialized) :
160    finstructable(finstructable),
161    fully_initialized(fully_initialized)
162  {}
163
164private:
165  virtual void OnInitialization() override
166  {
167    FINROC_LOG_PRINT_STATIC(DEBUG_VERBOSE_1, "Fully initialized", *GetAnnotated<core::tFrameworkElement>());
168    finstructable.CheckPendingConnectors(true);
169    fully_initialized = true;
170  }
171
172  tFinstructable& finstructable;
173  bool& fully_initialized;
174};
175
176}
177
178
179tFinstructable::tFinstructable(const std::string& xml_file) :
180  main_name(),
181  xml_file(xml_file)
182{
183}
184
185void tFinstructable::AddDependency(const tSharedLibrary& dependency)
186{
187#ifndef RRLIB_SINGLE_THREADED
188  if (&rrlib::thread::tThread::CurrentThread() == saving_thread)
189#endif
190  {
191    if (startup_loaded_finroc_libs.find(dependency) == startup_loaded_finroc_libs.end())
192    {
193      dependencies_tmp.insert(dependency);
194    }
195  }
196}
197
198void tFinstructable::AddDependency(const rrlib::rtti::tType& dt)
199{
200  auto shared_library_string = rrlib::rtti::tStaticTypeRegistration::GetTypeRegistrationSharedLibrary(dt);
201  if (shared_library_string)
202  {
203    AddDependency(tSharedLibrary(shared_library_string));
204  }
205}
206
207void tFinstructable::CheckPendingConnectors(bool print_warning_messages)
208{
209  if (pending_connectors.size())
210  {
211    core::tPath path_to_this = GetFrameworkElement()->GetPath();
212    std::vector<tConnectorData> pending_connectors_copy;
213    std::swap(pending_connectors_copy, pending_connectors);
214    assert(pending_connectors.empty());
215    for (auto & connector_data : pending_connectors_copy)
216    {
217      connector_data.status = InstantiateConnector(connector_data, path_to_this);
218      if (connector_data.status.length())
219      {
220        pending_connectors.push_back(connector_data);
221        if (print_warning_messages)
222        {
223          FINROC_LOG_PRINT(WARNING, "Creating connector from ", connector_data.source, " to ", connector_data.destination, " failed. Reason: ", connector_data.status);
224        }
225      }
226    }
227  }
228}
229
230core::tAbstractPort* tFinstructable::GetChildPort(const core::tPath& path)
231{
232  tFrameworkElement* element = GetFrameworkElement()->GetDescendant(path);
233  if (element && element->IsPort())
234  {
235    return static_cast<core::tAbstractPort*>(element);
236  }
237  return nullptr;
238}
239
240core::tPath tFinstructable::GetConnectorPath(const core::tPath& target_path, const core::tPath& this_group_path)
241{
242  if (target_path.CountCommonElements(this_group_path) == this_group_path.Size())
243  {
244    return core::tPath(false, target_path.Begin() + this_group_path.Size(), target_path.End());
245  }
246  return target_path;
247}
248
249core::tPath tFinstructable::GetConnectorPath(core::tAbstractPort& port, const core::tPath& this_group_path)
250{
251  core::tPath port_path = port.GetPath();
252  tFrameworkElement* alt_root = port.GetParentWithFlags(tFlag::ALTERNATIVE_LOCAL_URI_ROOT);
253  if (alt_root && alt_root->IsChildOf(*GetFrameworkElement()))
254  {
255    return core::tPath(true, port_path.Begin() + alt_root->GetPath().Size(), port_path.End());
256  }
257  return core::tPath(true, port_path.Begin() + this_group_path.Size(), port_path.End());
258}
259
260core::tFrameworkElement* tFinstructable::GetFrameworkElement() const
261{
262  auto include_instance = IncludeFinstructable(this);
263  return include_instance ? include_instance->root_framework_element : this->GetAnnotated<core::tFrameworkElement>();
264}
265
266std::string tFinstructable::GetLogDescription() const
267{
268  if (GetFrameworkElement())
269  {
270    std::stringstream stream;
271    stream << *GetFrameworkElement();
272    return stream.str();
273  }
274  return "Unattached Finstructable";
275}
276
277std::string tFinstructable::GetXmlFileString()
278{
279  std::string s = xml_file;
280  if (s.find(cUNWANTED_XML_FILE_PREFIX) != std::string::npos)
281  {
282    FINROC_LOG_PRINT(WARNING, "XML file name '", s, "' is deprecated, because it contains '", cUNWANTED_XML_FILE_PREFIX, "'. File will not be found when installed.");
283  }
284  return s;
285}
286
287void tFinstructable::Include(core::tFrameworkElement& parent, const std::string& xml_file, core::tFrameworkElement* origin)
288{
289  tIncludeFinstructable temp_finstructable;
290  temp_finstructable.xml_file = xml_file;
291  temp_finstructable.root_framework_element = &parent;
292  temp_finstructable.origin = origin;
293  temp_finstructable.LoadXml();
294}
295
296void tFinstructable::Instantiate(const rrlib::xml::tNode& node, tFrameworkElement* parent)
297{
298  std::string name = "component name not read";
299  try
300  {
301    std::string group = node.GetStringAttribute("group");
302    std::string type = node.GetStringAttribute("type");
303    if (node.HasAttribute("name"))
304    {
305      name = node.GetStringAttribute("name");
306    }
307
308    // find action
309    tCreateFrameworkElementAction& action = LoadComponentType(group, type);
310
311    // read parameters
312    rrlib::xml::tNode::const_iterator child_node = node.ChildrenBegin();
313    const rrlib::xml::tNode* parameters = nullptr;
314    const rrlib::xml::tNode* constructor_params = nullptr;
315    std::string p_name = child_node == node.ChildrenEnd() ? std::string() : child_node->Name();
316    if (p_name == "constructor")
317    {
318      constructor_params = &(*child_node);
319      ++child_node;
320      p_name = child_node == node.ChildrenEnd() ? std::string() : child_node->Name();
321    }
322    if (p_name == "parameters")
323    {
324      parameters = &(*child_node);
325      ++child_node;
326    }
327
328    // create mode
329    tFrameworkElement* created = nullptr;
330    tConstructorParameters* spl = nullptr;
331    if (constructor_params != nullptr)
332    {
333      spl = action.GetParameterTypes()->Instantiate();
334      spl->Deserialize(*constructor_params, true);
335    }
336    created = action.CreateModule(parent, name, spl);
337    tOriginData data;
338    auto include_instance = IncludeFinstructable(this);
339    if (include_instance && include_instance->origin)
340    {
341      data.origin = include_instance->origin;
342      SetFinstructed(*created, action, spl, data);
343    }
344    else if (!include_instance)
345    {
346      SetFinstructed(*created, action, spl, data);
347    }
348    if (parameters)
349    {
350      parameters::internal::tStaticParameterList::GetOrCreate(*created).Deserialize(*parameters, true);
351    }
352    created->Init();
353
354    // continue with children
355    for (; child_node != node.ChildrenEnd(); ++child_node)
356    {
357      std::string name2 = child_node->Name();
358      if (name2 == "element")
359      {
360        Instantiate(*child_node, created);
361      }
362      else
363      {
364        FINROC_LOG_PRINT(WARNING, "Unknown XML tag: ", name2);
365      }
366    }
367  }
368  catch (const rrlib::xml::tException& e)
369  {
370    FINROC_LOG_PRINT(ERROR, "Failed to instantiate component '", name, "'. XML Exception: ", e.what(), ". Skipping.");
371  }
372  catch (const std::exception& e)
373  {
374    FINROC_LOG_PRINT(ERROR, "Failed to instantiate component '", name, "'. ", e.what(), ". Skipping.");
375  }
376}
377
378bool tFinstructable::IsResponsibleForConfigFileConnections(tFrameworkElement& ap) const
379{
380  return parameters::internal::tParameterInfo::IsFinstructableGroupResponsibleForConfigFileConnections(*GetFrameworkElement(), ap);
381}
382
383void tFinstructable::LoadParameter(const rrlib::xml::tNode& node, core::tFrameworkElement& parameter_element)
384{
385  parameters::internal::tParameterInfo* pi = parameter_element.GetAnnotation<parameters::internal::tParameterInfo>();
386  bool outermost_group = GetFrameworkElement()->GetParent() == &(core::tRuntimeEnvironment::GetInstance());
387  if (!pi)
388  {
389    FINROC_LOG_PRINT(WARNING, "Element is not parameter-related: '", parameter_element, "'. Parameter entry is not loaded.");
390  }
391  else
392  {
393    auto include_instance = IncludeFinstructable(this);
394    bool high_precendence = include_instance == nullptr;
395    auto origin = include_instance ? include_instance->origin : nullptr;
396    if (outermost_group && parameter_element.IsPort() && node.HasAttribute("cmdline") && (!IsResponsibleForConfigFileConnections(parameter_element)))
397    {
398      pi->SetCommandLineOption(node.GetStringAttribute("cmdline"), high_precendence, origin);
399    }
400    else
401    {
402      pi->Deserialize(node, high_precendence, outermost_group, origin);
403    }
404    try
405    {
406      pi->LoadValue();
407    }
408    catch (const std::exception& e)
409    {
410      FINROC_LOG_PRINT(WARNING, "Unable to load parameter config for '", parameter_element, "'. ", e);
411    }
412  }
413}
414
415void tFinstructable::LoadXml()
416{
417  {
418    auto include_instance = IncludeFinstructable(this);
419    tOriginData origin_data;
420    if (include_instance && include_instance->origin)
421    {
422      origin_data.origin = include_instance->origin;
423    }
424
425    rrlib::thread::tLock lock2(core::tRuntimeEnvironment::GetInstance().GetStructureMutex());
426    try
427    {
428      FINROC_LOG_PRINT(DEBUG, "Loading XML: ", GetXmlFileString());
429      rrlib::xml::tDocument doc(core::GetFinrocXMLDocument(GetXmlFileString(), false));
430      rrlib::xml::tNode& root = doc.RootNode();
431      core::tPath path_to_this = GetFrameworkElement()->GetPath();
432      if (main_name.length() == 0 && root.HasAttribute("defaultname"))
433      {
434        main_name = root.GetStringAttribute("defaultname");
435      }
436      uint version = 0;
437      if (root.HasAttribute("version"))
438      {
439        version = root.GetIntAttribute("version");
440      }
441
442      // load dependencies
443      if (root.HasAttribute("dependencies"))
444      {
445        std::vector<std::string> deps;
446        std::stringstream stream(root.GetStringAttribute("dependencies"));
447        std::string dependency;
448        while (std::getline(stream, dependency, ','))
449        {
450          rrlib::util::TrimWhitespace(dependency);
451          tSharedLibrary dep(dependency);
452          std::vector<tSharedLibrary> loadable = GetLoadableFinrocLibraries();
453          bool loaded = false;
454          for (size_t i = 0; i < loadable.size(); i++)
455          {
456            if (loadable[i] == dep)
457            {
458              try
459              {
460                DLOpen(dep);
461                loaded = true;
462                break;
463              }
464              catch (const std::exception& exception)
465              {
466                FINROC_LOG_PRINT(ERROR, exception);
467              }
468            }
469          }
470          if (!loaded)
471          {
472            std::set<tSharedLibrary> loaded_libs = GetLoadedFinrocLibraries();
473            if (loaded_libs.find(dep) == loaded_libs.end())
474            {
475              FINROC_LOG_PRINT(WARNING, "Dependency ", dep.ToString(true), " is not available.");
476            }
477          }
478        }
479      }
480
481      // Load components (before interface in order to reduce issues with missing/unregistered data types)
482      for (rrlib::xml::tNode::const_iterator node = root.ChildrenBegin(); node != root.ChildrenEnd(); ++node)
483      {
484        std::string name = node->Name();
485        if (name == "element")
486        {
487          Instantiate(*node, GetFrameworkElement());
488        }
489        if (name == "include")
490        {
491          std::string file;
492          try
493          {
494            file = node->GetStringAttribute("file");
495            auto parent = this->GetFrameworkElement();
496            if (node->HasAttribute("element"))
497            {
498              rrlib::uri::tURIElements uri_elements;
499              rrlib::uri::tURI uri(node->GetStringAttribute("element"));
500              uri.Parse(uri_elements);
501              parent = GetFrameworkElement()->GetDescendant(uri_elements.path);
502              if (!parent)
503              {
504                throw std::runtime_error("Element '" + node->GetStringAttribute("element") + "' not found");
505              }
506            }
507            auto created = new tIncludeElement(parent, file, file);
508            origin_data.order = std::chrono::steady_clock::now().time_since_epoch().count();
509            if (!include_instance)
510            {
511              created->SetFlag(tFlag::FINSTRUCTED);
512            }
513            created->EmplaceAnnotation<tOriginDataAnnotation>(origin_data);
514            created->Init();
515          }
516          catch (const std::exception& e)
517          {
518            FINROC_LOG_PRINT(ERROR, "Including ", file, " failed: ", e);
519          }
520        }
521      }
522
523      // Load all remaining XML elements
524      bool this_is_outermost_composite_component = GetFrameworkElement()->GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP) == nullptr;
525      for (rrlib::xml::tNode::const_iterator node = root.ChildrenBegin(); node != root.ChildrenEnd(); ++node)
526      {
527        std::string name = node->Name();
528        if (name == "interface")
529        {
530          tEditableInterfaces* editable_interfaces = GetFrameworkElement()->GetAnnotation<tEditableInterfaces>();
531          auto include_instance = IncludeFinstructable(this);
532          core::tFrameworkElement* extended_element = nullptr;
533          if (node->HasAttribute("element"))
534          {
535            if (include_instance)  // Without include instance, origins cannot be separated
536            {
537              rrlib::uri::tURIElements uri_elements;
538              rrlib::uri::tURI uri(node->GetStringAttribute("element"));
539              uri.Parse(uri_elements);
540              extended_element = GetFrameworkElement()->GetDescendant(uri_elements.path);
541              if (!extended_element)
542              {
543                FINROC_LOG_PRINT(WARNING, "Interface element to extend not found: ", uri_elements.path);
544              }
545              editable_interfaces = extended_element ? extended_element->GetAnnotation<tEditableInterfaces>() : nullptr;
546            }
547            else
548            {
549              FINROC_LOG_PRINT(WARNING, "Setting 'element' attribute of interface tag is only supported in include files");
550            }
551          }
552          if (editable_interfaces)
553          {
554            try
555            {
556              core::tPortGroup& loaded_interface = editable_interfaces->LoadInterfacePorts(*node, !include_instance, include_instance ? include_instance->origin : nullptr);
557
558              // Move RPC port to suitable interfaces when loading legacy files
559              if (version == 0 && (!loaded_interface.GetFlag(tFlag::INTERFACE_FOR_RPC_PORTS)))
560              {
561                for (auto it = loaded_interface.ChildPortsBegin(); it != loaded_interface.ChildPortsEnd(); ++it)
562                {
563                  if (it->GetDataType().GetTypeClassification() == rrlib::rtti::tTypeClassification::RPC_TYPE)
564                  {
565                    core::tFrameworkElement* services_interface = nullptr;
566                    for (auto child_interface = GetFrameworkElement()->ChildrenBegin(); child_interface != GetFrameworkElement()->ChildrenEnd(); ++child_interface)
567                    {
568                      if (child_interface->IsReady() && (!child_interface->IsPort()) && child_interface->GetFlag(tFlag::INTERFACE) && child_interface->GetFlag(tFlag::INTERFACE_FOR_RPC_PORTS))
569                      {
570                        if (services_interface)
571                        {
572                          services_interface = nullptr;
573                          break;
574                        }
575                        services_interface = &(*child_interface);
576                      }
577                    }
578
579                    if (services_interface)
580                    {
581                      FINROC_LOG_PRINT(WARNING, "Moving RPC port '", it->GetName(), "' to RPC interface '", *services_interface, "' (auto-update loading legacy files).");
582                      auto cKEEP_FLAGS = (tFlag::ACCEPTS_DATA | tFlag::EMITS_DATA | tFlag::OUTPUT_PORT | tFlag::FINSTRUCTED_PORT).Raw();
583                      tPortCreationList creation_list(static_cast<core::tPortGroup&>(*services_interface), core::tFrameworkElementFlags(it->GetAllFlags().Raw() & cKEEP_FLAGS), tPortCreateOptions());
584                      creation_list.Add(it->GetName(), it->GetDataType());
585                      it->ManagedDelete();
586                    }
587                  }
588                }
589              }
590
591              if (extended_element)
592              {
593                parameters::internal::tStaticParameterList::DoStaticParameterEvaluation(*extended_element);
594              }
595            }
596            catch (const std::exception& e)
597            {
598              FINROC_LOG_PRINT(WARNING, "Loading interface ports failed. Reason: ", e);
599            }
600          }
601          else
602          {
603            FINROC_LOG_PRINT(WARNING, "Cannot load interface, because finstructable group does not have any editable interfaces.");
604          }
605        }
606        else if (name == "element" || name == "include")
607        {
608          // already instantiated
609        }
610        else if (name == "edge")
611        {
612          tConnectorData connector_data;
613          connector_data.source = node->GetStringAttribute("src");
614          connector_data.destination = node->GetStringAttribute("dest");
615          connector_data.structure_file_version = version;
616          connector_data.top_level_composite_component = this_is_outermost_composite_component;
617
618          try
619          {
620            core::tUriConnectOptions& connect_options = connector_data.connect_options;
621            connector_data.connect_options.flags.Set(core::tConnectionFlag::FINSTRUCTED, !static_cast<bool>(include_instance));  // TODO attach origin data to connectors also
622            if (node->HasAttribute("flags"))
623            {
624              connect_options.flags |= rrlib::serialization::Deserialize<core::tConnectionFlags>(node->GetStringAttribute("flags"));
625            }
626
627            for (rrlib::xml::tNode::const_iterator conversion_node = node->ChildrenBegin(); conversion_node != node->ChildrenEnd(); ++conversion_node)
628            {
629              if (conversion_node->Name() == "conversion")
630              {
631                rrlib::rtti::tType intermediate_type;
632                if (conversion_node->HasAttribute("intermediate_type"))
633                {
634                  intermediate_type = rrlib::rtti::tType::FindType(conversion_node->GetStringAttribute("intermediate_type"));
635                }
636                if (conversion_node->HasAttribute("operation2"))
637                {
638                  // Two operations
639                  std::string operation1 = conversion_node->GetStringAttribute("operation1");
640                  std::string operation2 = conversion_node->GetStringAttribute("operation2");
641                  connect_options.conversion_operations = rrlib::rtti::conversion::tConversionOperationSequence(operation1, operation2, intermediate_type);
642                  if (conversion_node->HasAttribute("parameter1"))
643                  {
644                    connect_options.conversion_operations.SetParameterValue(0, conversion_node->GetStringAttribute("parameter1"));
645                  }
646                  if (conversion_node->HasAttribute("parameter2"))
647                  {
648                    connect_options.conversion_operations.SetParameterValue(1, conversion_node->GetStringAttribute("parameter2"));
649                  }
650                }
651                else if (conversion_node->HasAttribute("operation"))
652                {
653                  // One operation
654                  std::string operation1 = conversion_node->GetStringAttribute("operation");
655                  connect_options.conversion_operations = rrlib::rtti::conversion::tConversionOperationSequence(operation1, "", intermediate_type);
656                  if (conversion_node->HasAttribute("parameter"))
657                  {
658                    connect_options.conversion_operations.SetParameterValue(0, conversion_node->GetStringAttribute("parameter"));
659                  }
660                }
661              }
662            }
663
664            // read parameters
665            for (rrlib::xml::tNode::const_iterator parameter_node = node->ChildrenBegin(); parameter_node != node->ChildrenEnd(); ++parameter_node)
666            {
667              if (parameter_node->Name() == "parameter")
668              {
669                connect_options.parameters.emplace(parameter_node->GetStringAttribute("name"), parameter_node->GetTextContent());
670              }
671            }
672
673            connector_data.status = InstantiateConnector(connector_data, path_to_this);
674            if (connector_data.status.length())
675            {
676              this->pending_connectors.push_back(connector_data);
677              if (this->fully_initialized)
678              {
679                FINROC_LOG_PRINT(WARNING, "Creating connector from ", connector_data.source, " to ", connector_data.destination, " failed. Reason: ", connector_data.status);
680              }
681            }
682          }
683          catch (const std::exception& e)
684          {
685            FINROC_LOG_PRINT(WARNING, "Creating connector from ", connector_data.source, " to ", connector_data.destination, " failed. Reason: ", e.what());
686          }
687        }
688        else if (name == "parameter") // legacy parameter info support
689        {
690          core::tURI parameter_uri(node->GetStringAttribute("link"));
691          rrlib::uri::tURIElements parameter_uri_parsed;
692          parameter_uri.Parse(parameter_uri_parsed);
693          core::tAbstractPort* parameter = GetChildPort(parameter_uri_parsed.path);
694          if (!parameter)
695          {
696            FINROC_LOG_PRINT(WARNING, "Cannot set config entry, because parameter is not available: ", parameter_uri.ToString());
697          }
698          else
699          {
700            LoadParameter(*node, *parameter);
701          }
702        }
703        else if (name == "parameter_links")
704        {
705          ProcessParameterLinksNode(*node, *GetFrameworkElement());
706          if (node->HasAttribute("config"))
707          {
708            core::tFrameworkElement* parameters_interface = GetFrameworkElement()->GetChild("Parameters");
709            if (parameters_interface)
710            {
711              LoadParameter(*node, *parameters_interface);
712            }
713          }
714        }
715        else
716        {
717          FINROC_LOG_PRINT(WARNING, "Unknown XML tag: ", name);
718        }
719      }
720      FINROC_LOG_PRINT(DEBUG, "Loading XML successful");
721    }
722    catch (const std::exception& e)
723    {
724      FINROC_LOG_PRINT(WARNING, "Loading XML '", GetXmlFileString(), "' failed: ", e);
725    }
726  }
727}
728
729std::string tFinstructable::InstantiateConnector(const tConnectorData& connector_data, const core::tPath& path_to_this)
730{
731  auto version = connector_data.structure_file_version;
732  try
733  {
734    rrlib::uri::tURI source_uri(connector_data.source);
735    rrlib::uri::tURI destination_uri(connector_data.destination);
736
737    rrlib::uri::tURIElements source_uri_parsed;
738    rrlib::uri::tURIElements destination_uri_parsed;
739    if (version)
740    {
741      source_uri.Parse(source_uri_parsed);
742      destination_uri.Parse(destination_uri_parsed);
743    }
744    else
745    {
746      source_uri_parsed.path = rrlib::uri::tPath(connector_data.source);
747      destination_uri_parsed.path = rrlib::uri::tPath(connector_data.destination);
748    }
749
750    if (source_uri_parsed.scheme.length() == 0 && destination_uri_parsed.scheme.length() == 0)
751    {
752      core::tAbstractPort* source_port = GetChildPort(source_uri_parsed.path);
753      core::tAbstractPort* destination_port = GetChildPort(destination_uri_parsed.path);
754
755      // Backward-compatibility: Check whether this a connector between service interfaces now
756      if (version == 0 && source_port == nullptr && destination_port == nullptr)
757      {
758        core::tAbstractPort* service_source_port = GetChildPort(ReplaceInterfaceInPath(source_uri_parsed.path, "Services"));
759        core::tAbstractPort* service_destination_port = GetChildPort(ReplaceInterfaceInPath(destination_uri_parsed.path, "Services"));
760        if (service_source_port && service_destination_port && service_source_port->GetDataType().GetTypeClassification() == rrlib::rtti::tTypeClassification::RPC_TYPE)
761        {
762          FINROC_LOG_PRINT(WARNING, "Adjusted connector's interfaces to service interfaces (auto-update loading legacy files): now connects '", *service_source_port, "' and '", *service_destination_port, "'");
763          source_port = service_source_port;
764          destination_port = service_destination_port;
765        }
766      }
767      else if (version == 0 && source_port && source_port->GetDataType().GetTypeClassification() == rrlib::rtti::tTypeClassification::RPC_TYPE && destination_port == nullptr)
768      {
769        core::tAbstractPort* service_destination_port = GetChildPort(ReplaceInterfaceInPath(destination_uri_parsed.path, "Services"));
770        if (service_destination_port && service_destination_port->GetDataType() == source_port->GetDataType())
771        {
772          FINROC_LOG_PRINT(WARNING, "Adjusted connector's interfaces to service interfaces (auto-update loading legacy files): now connects '", *source_port, "' and '", *service_destination_port, "'");
773          destination_port = service_destination_port;
774        }
775      }
776      else if (version == 0 && destination_port && destination_port->GetDataType().GetTypeClassification() == rrlib::rtti::tTypeClassification::RPC_TYPE && source_port == nullptr)
777      {
778        core::tAbstractPort* service_source_port = GetChildPort(ReplaceInterfaceInPath(source_uri_parsed.path, "Services"));
779        if (service_source_port && service_source_port->GetDataType() == destination_port->GetDataType())
780        {
781          FINROC_LOG_PRINT(WARNING, "Adjusted connector's interfaces to service interfaces (auto-update loading legacy files): now connects '", *service_source_port, "' and '", *destination_port, "'");
782          source_port = service_source_port;
783        }
784      }
785
786      if (source_port == nullptr && destination_port == nullptr)
787      {
788        std::stringstream error_message;
789        error_message << "Cannot create connector because neither port is available: '" << source_uri_parsed.path << "' and '" << destination_uri_parsed.path << "'";
790        throw std::runtime_error(error_message.str());
791      }
792      else if (source_port == nullptr || source_port->GetFlag(tFlag::VOLATILE))
793      {
794        if (source_uri_parsed.path.IsAbsolute() && connector_data.top_level_composite_component && version == 0)
795        {
796          FINROC_LOG_PRINT(WARNING, "Interpreting absolute connector source path (", source_uri_parsed.path, ") as legacy TCP connection");
797          destination_port->ConnectTo(rrlib::uri::tURI("tcp:" + rrlib::uri::tURI(source_uri_parsed.path).ToString()), core::tConnectionFlag::FINSTRUCTED);
798        }
799        else
800        {
801          destination_port->ConnectTo(source_uri_parsed.path.IsAbsolute() ? source_uri_parsed.path : path_to_this.Append(source_uri_parsed.path), connector_data.connect_options);
802        }
803      }
804      else if (destination_port == nullptr || destination_port->GetFlag(tFlag::VOLATILE))
805      {
806        if (destination_uri_parsed.path.IsAbsolute() && connector_data.top_level_composite_component && version == 0)
807        {
808          FINROC_LOG_PRINT(WARNING, "Interpreting absolute connector destination path (", destination_uri_parsed.path, ") as legacy TCP connection");
809          source_port->ConnectTo(rrlib::uri::tURI("tcp:" + rrlib::uri::tURI(destination_uri_parsed.path).ToString()), core::tConnectionFlag::FINSTRUCTED);
810        }
811        else
812        {
813          source_port->ConnectTo(destination_uri_parsed.path.IsAbsolute() ? destination_uri_parsed.path : path_to_this.Append(destination_uri_parsed.path), connector_data.connect_options);
814        }
815      }
816      else
817      {
818        source_port->ConnectTo(*destination_port, connector_data.connect_options);
819      }
820    }
821    else
822    {
823      // Create URI connector
824      if (source_uri_parsed.scheme.length() && destination_uri_parsed.scheme.length())
825      {
826        throw std::runtime_error("Only one port may have an address with an URI scheme");
827      }
828      core::tAbstractPort* source_port = GetChildPort(source_uri_parsed.scheme.length() == 0 ? source_uri_parsed.path : destination_uri_parsed.path);
829      if (!source_port)
830      {
831        std::stringstream message;
832        message << "Cannot create connector because port is not available: " << (source_uri_parsed.scheme.length() == 0 ? source_uri_parsed.path : destination_uri_parsed.path);
833        throw std::runtime_error(message.str());
834      }
835      const core::tURI& scheme_uri = source_uri_parsed.scheme.length() != 0 ? source_uri : destination_uri;
836
837      core::tUriConnector::Create(*source_port, scheme_uri, connector_data.connect_options);
838    }
839  }
840  catch (const std::exception& e)
841  {
842    return e.what();
843  }
844  return "";
845}
846
847void tFinstructable::OnInitialization()
848{
849  if (!GetFrameworkElement()->GetFlag(tFlag::FINSTRUCTABLE_GROUP))
850  {
851    throw std::logic_error("Any class using tFinstructable must set tFlag::FINSTRUCTABLE_GROUP in constructor");
852  }
853
854  if (pending_connectors.empty())
855  {
856    fully_initialized = true;
857  }
858  else
859  {
860    GetAnnotated<core::tFrameworkElement>()->EmplaceAnnotation<tOnFullInitialization>(*this, fully_initialized);
861  }
862}
863
864void tFinstructable::ProcessParameterLinksNode(const rrlib::xml::tNode& node, core::tFrameworkElement& element)
865{
866  for (auto it = node.ChildrenBegin(); it != node.ChildrenEnd(); ++it)
867  {
868    if (it->Name() == "element")
869    {
870      std::string name = it->GetStringAttribute("name");
871      core::tFrameworkElement* corresponding_element = element.GetChild(name);
872      if (corresponding_element)
873      {
874        ProcessParameterLinksNode(*it, *corresponding_element);
875
876        if (it->HasAttribute("config"))
877        {
878          core::tFrameworkElement* parameters_interface = corresponding_element->GetChild("Parameters");
879          if (parameters_interface)
880          {
881            LoadParameter(*it, *parameters_interface);
882          }
883        }
884      }
885      else
886      {
887        FINROC_LOG_PRINT(WARNING, "Cannot find '", element, "/", name, "'. Parameter entries below are not loaded.");
888      }
889    }
890    else if (it->Name() == "parameter")
891    {
892      std::string name = it->GetStringAttribute("name");
893      core::tFrameworkElement* parameter_element = element.GetChild(name);
894      if ((!parameter_element) || (!parameter_element->IsPort()))
895      {
896        core::tFrameworkElement* parameters_interface = element.GetChild("Parameters");
897        parameter_element = parameters_interface ? parameters_interface->GetChild(name) : nullptr;
898      }
899      if (parameter_element && parameter_element->IsPort())
900      {
901        LoadParameter(*it, *parameter_element);
902      }
903      else
904      {
905        FINROC_LOG_PRINT(WARNING, "Cannot find parameter '", element, "/", name, "'. Parameter entry is not loaded.");
906      }
907    }
908  }
909}
910
911bool tFinstructable::SaveParameterConfigEntries(rrlib::xml::tNode& node, core::tFrameworkElement& element)
912{
913  // Get alphabetically sorted list of children
914  std::vector<core::tFrameworkElement*> child_elements;
915  std::vector<core::tAbstractPort*> parameter_ports;
916  core::tFrameworkElement* parameters_interface = nullptr;
917  for (auto it = element.ChildrenBegin(); it != element.ChildrenEnd(); ++it)
918  {
919    if (it->IsReady())
920    {
921      if ((!it->IsPort()) && it->GetFlag(tFlag::INTERFACE) && it->GetFlag(tFlag::PARAMETER_INTERFACE))
922      {
923        for (auto parameter_it = it->ChildrenBegin(); parameter_it != it->ChildrenEnd(); ++parameter_it)
924        {
925          if (parameter_it->IsReady() && parameter_it->IsPort() && parameter_it->GetAnnotation<parameters::internal::tParameterInfo>())
926          {
927            parameter_ports.push_back(&static_cast<core::tAbstractPort&>(*parameter_it));
928          }
929        }
930        parameters_interface = &*it;
931      }
932      else if (it->IsPort() && it->GetAnnotation<parameters::internal::tParameterInfo>())
933      {
934        parameter_ports.push_back(&static_cast<core::tAbstractPort&>(*it));
935      }
936      else
937      {
938        child_elements.push_back(&(*it));
939      }
940    }
941  }
942
943  struct
944  {
945    bool operator()(core::tFrameworkElement* a, core::tFrameworkElement* b)
946    {
947      return a->GetName() < b->GetName();
948    }
949  } comparator;
950  std::sort(child_elements.begin(), child_elements.end(), comparator);
951  std::sort(parameter_ports.begin(), parameter_ports.end(), comparator);
952
953  // Save parameters first
954  bool result = false;
955  for (core::tAbstractPort * port : parameter_ports)
956  {
957    bool outermost_group = GetFrameworkElement()->GetParent() == &(core::tRuntimeEnvironment::GetInstance());
958    parameters::internal::tParameterInfo* info = port->GetAnnotation<parameters::internal::tParameterInfo>();
959    bool is_responsible_for_parameter_links = IsResponsibleForConfigFileConnections(*port);
960
961    if (info && HasNonDefaultFinstructInfo(*info) && (is_responsible_for_parameter_links || (outermost_group && info->GetCommandLineOption().length())))
962    {
963      // Save Parameter
964      rrlib::xml::tNode& parameter_node = node.AddChildNode("parameter");
965      parameter_node.SetAttribute("name", port->GetName());
966
967      if (!is_responsible_for_parameter_links)
968      {
969        parameter_node.SetAttribute("cmdline", info->GetCommandLineOption());
970      }
971      else
972      {
973        info->Serialize(parameter_node, true, outermost_group, nullptr);
974      }
975      result = true;
976    }
977  }
978
979  // Save possible config entry for parameters interface
980  if (parameters_interface)
981  {
982    parameters::internal::tParameterInfo* info = parameters_interface->GetAnnotation<parameters::internal::tParameterInfo>();
983    bool is_responsible_for_parameter_links = IsResponsibleForConfigFileConnections(*parameters_interface);
984    if (info && HasNonDefaultFinstructInfo(*info) && is_responsible_for_parameter_links)
985    {
986      info->Serialize(node, true, false, nullptr);
987      result = true;
988    }
989  }
990
991  // Process child elements
992  for (core::tFrameworkElement * child : child_elements)
993  {
994    rrlib::xml::tNode& child_node = node.AddChildNode("element");
995    child_node.SetAttribute("name", child->GetName());
996    bool sub_result = SaveParameterConfigEntries(child_node, *child);
997    result |= sub_result;
998    if (!sub_result)
999    {
1000      node.RemoveChildNode(child_node);
1001    }
1002  }
1003  return result;
1004}
1005
1006void tFinstructable::SaveXml()
1007{
1008  {
1009    rrlib::thread::tLock lock2(core::tRuntimeEnvironment::GetInstance().GetStructureMutex());
1010#ifndef RRLIB_SINGLE_THREADED
1011    saving_thread = &rrlib::thread::tThread::CurrentThread();
1012#endif
1013    dependencies_tmp.clear();
1014    std::string save_to = core::GetFinrocFileToSaveTo(GetXmlFileString());
1015    if (save_to.length() == 0)
1016    {
1017      std::string save_to_alt = GetXmlFileString();
1018      std::replace(save_to_alt.begin(), save_to_alt.end(), '/', '_');
1019      FINROC_LOG_PRINT(USER, "There does not seem to be any suitable location for: '", GetXmlFileString(), "' . For now, using '", save_to_alt, "'.");
1020      save_to = save_to_alt;
1021    }
1022    FINROC_LOG_PRINT(USER, "Saving XML: ", save_to);
1023    rrlib::xml::tDocument doc;
1024    try
1025    {
1026      rrlib::xml::tNode& root = doc.AddRootNode("Finstructable"); // TODO: find good name ("finroc_structure")
1027
1028      // serialize default main name
1029      if (main_name.length() > 0)
1030      {
1031        root.SetAttribute("defaultname", main_name);
1032      }
1033      root.SetAttribute("version", cVERSION);
1034
1035      // serialize any editable interfaces
1036      tEditableInterfaces* editable_interfaces = GetFrameworkElement()->GetAnnotation<tEditableInterfaces>();
1037      if (editable_interfaces)
1038      {
1039        editable_interfaces->SaveAllNonEmptyInterfaces(root);
1040      }
1041
1042      // serialize framework elements
1043      SerializeChildren(root, *GetFrameworkElement());
1044
1045      // serialize connectors (sorted by port URIs (we do not want changes in finstruct files depending on whether or not an optional/volatile connector exists))
1046      rrlib::uri::tPath this_path = GetFrameworkElement()->GetPath();
1047      std::map<std::pair<rrlib::uri::tURI, rrlib::uri::tURI>, std::pair<core::tConnector*, core::tUriConnector*>> connector_map;
1048      bool this_is_outermost_composite_component = GetFrameworkElement()->GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP) == nullptr;
1049
1050      // Get (primary) connectors
1051      for (auto it = GetFrameworkElement()->SubElementsBegin(); it != GetFrameworkElement()->SubElementsEnd(); ++it)
1052      {
1053        if ((!it->IsPort()) || (!it->IsReady()))
1054        {
1055          continue;
1056        }
1057        core::tAbstractPort& port = static_cast<core::tAbstractPort&>(*it);
1058        tFrameworkElement* port_parent_group = port.GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP);
1059
1060        // Plain connectors
1061        for (auto it = port.OutgoingConnectionsBegin(); it != port.OutgoingConnectionsEnd(); ++it) // only outgoing edges => we don't get any edges twice
1062        {
1063          // Checks whether to save connector
1064          // only save primary finstructed edges
1065          if ((!it->Flags().Get(core::tConnectionFlag::FINSTRUCTED)) || it->Flags().Get(core::tConnectionFlag::NON_PRIMARY_CONNECTOR))
1066          {
1067            continue;
1068          }
1069
1070          // connectors should be saved in innermost composite component that contains both ports (common parent); if there is no such port, then save in outermost composite component
1071          tFrameworkElement* common_parent = port_parent_group;
1072          while (common_parent && (!it->Destination().IsChildOf(*common_parent)))
1073          {
1074            common_parent = common_parent->GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP);
1075          }
1076          if (common_parent != GetFrameworkElement() && (!(this_is_outermost_composite_component && common_parent == nullptr)))
1077          {
1078            // save connector in another group
1079            continue;
1080          }
1081
1082          std::pair<rrlib::uri::tURI, rrlib::uri::tURI> key(rrlib::uri::tURI(GetConnectorPath(port.GetPath(), this_path), cUNENCODED_RESERVED_CHARACTERS_PATH), rrlib::uri::tURI(GetConnectorPath(it->Destination().GetPath(), this_path), cUNENCODED_RESERVED_CHARACTERS_PATH));
1083          std::pair<core::tConnector*, core::tUriConnector*> value(&(*it), nullptr);
1084          connector_map.emplace(key, value);
1085        }
1086
1087        // Get URI connectors
1088        for (auto & connector : port.UriConnectors())
1089        {
1090          // Checks whether to save connector
1091          // only save primary finstructed edges
1092          if ((!connector) || (!connector->Flags().Get(core::tConnectionFlag::FINSTRUCTED)) || connector->Flags().Get(core::tConnectionFlag::NON_PRIMARY_CONNECTOR))
1093          {
1094            continue;
1095          }
1096
1097          std::pair<rrlib::uri::tURI, rrlib::uri::tURI> key(rrlib::uri::tURI(GetConnectorPath(port.GetPath(), this_path), cUNENCODED_RESERVED_CHARACTERS_PATH), connector->Uri());
1098
1099          // local URI connectors should be saved in innermost composite component that contains both ports (common parent); if there is no such port, then save in outermost composite component
1100          core::tUriConnector& connector_reference = *connector;
1101          if (typeid(connector_reference) == typeid(core::internal::tLocalUriConnector))
1102          {
1103            auto& local_connector = static_cast<core::internal::tLocalUriConnector&>(*connector);
1104            bool source_uri = local_connector.GetPortReferences()[0].path.Size();
1105            rrlib::uri::tPath path = local_connector.GetPortReferences()[source_uri ? 0 : 1].path;
1106            tFrameworkElement* common_parent = port_parent_group;
1107            rrlib::uri::tPath parent_group_path = common_parent->GetPath();
1108            while (common_parent && path.CountCommonElements(parent_group_path) != parent_group_path.Size())
1109            {
1110              common_parent = common_parent->GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP);
1111              parent_group_path = common_parent ? common_parent->GetPath() : parent_group_path;
1112            }
1113            if (common_parent != GetFrameworkElement() && (!(this_is_outermost_composite_component && common_parent == nullptr)))
1114            {
1115              // save connector in another group
1116              continue;
1117            }
1118
1119            rrlib::uri::tURI port_uri = key.first;
1120            key.first = source_uri ? rrlib::uri::tURI(GetConnectorPath(path, parent_group_path), cUNENCODED_RESERVED_CHARACTERS_PATH) : port_uri;
1121            key.second = source_uri ? port_uri : rrlib::uri::tURI(GetConnectorPath(path, parent_group_path), cUNENCODED_RESERVED_CHARACTERS_PATH);
1122          }
1123
1124          std::pair<core::tConnector*, core::tUriConnector*> value(nullptr, connector.get());
1125          connector_map.emplace(key, value);
1126        }
1127      }
1128
1129      for (auto & entry : connector_map)
1130      {
1131        rrlib::xml::tNode& edge = root.AddChildNode("edge"); // TODO: "connector"?
1132        edge.SetAttribute("src", entry.first.first.ToString());
1133        edge.SetAttribute("dest", entry.first.second.ToString());
1134
1135        // Save flags
1136        core::tConnectionFlags cFLAGS_TO_SAVE = core::tConnectionFlag::DIRECTION_TO_DESTINATION | core::tConnectionFlag::DIRECTION_TO_SOURCE | core::tConnectionFlag::OPTIONAL | core::tConnectionFlag::RECONNECT | core::tConnectionFlag::SCHEDULING_NEUTRAL;
1137        core::tConnectionFlags flags_to_save((entry.second.first ? entry.second.first->Flags() : entry.second.second->Flags()).Raw() & cFLAGS_TO_SAVE.Raw());
1138        if (flags_to_save.Raw())
1139        {
1140          edge.SetAttribute("flags", rrlib::serialization::Serialize(flags_to_save));
1141        }
1142
1143        // Save conversion operation
1144        const rrlib::rtti::conversion::tConversionOperationSequence& conversion_operations = entry.second.first ? entry.second.first->ConversionOperations() : entry.second.second->ConversionOperations();
1145        if (conversion_operations.Size())
1146        {
1147          rrlib::xml::tNode& conversion = edge.AddChildNode("conversion");
1148          conversion.SetAttribute(conversion_operations.Size() == 2 ? "operation1" : "operation", conversion_operations[0].first);
1149          if (conversion_operations.GetParameterValue(0))
1150          {
1151            conversion.SetAttribute(conversion_operations.Size() == 2 ? "parameter1" : "parameter", conversion_operations.GetParameterValue(0).ToString());
1152          }
1153          if (conversion_operations.IntermediateType())
1154          {
1155            conversion.SetAttribute("intermediate_type", conversion_operations.IntermediateType().GetName());
1156          }
1157          if (conversion_operations.Size() == 2)
1158          {
1159            conversion.SetAttribute("operation2", conversion_operations[1].first);
1160            if (conversion_operations.GetParameterValue(1))
1161            {
1162              conversion.SetAttribute("parameter2", conversion_operations.GetParameterValue(1).ToString());
1163            }
1164          }
1165        }
1166
1167        // Save parameters of URI connector
1168        if (entry.second.second)
1169        {
1170          core::tUriConnector::tConstParameterDefinitionRange definition_range = entry.second.second->GetParameterDefinitions();
1171          core::tUriConnector::tParameterValueRange value_range = entry.second.second->GetParameterValues();
1172          assert(definition_range.End() - definition_range.Begin() == value_range.End() - value_range.Begin());
1173
1174          auto definition = definition_range.Begin();
1175          auto value = value_range.Begin();
1176          for (; definition < definition_range.End(); ++definition, ++value)
1177          {
1178            if (!value->Equals(definition->GetDefaultValue()))
1179            {
1180              rrlib::xml::tNode& parameter_node = edge.AddChildNode("parameter");
1181              parameter_node.SetAttribute("name", definition->GetName());
1182              value->Serialize(parameter_node);
1183            }
1184          }
1185        }
1186      }
1187
1188      // Save parameter config entries
1189      auto& parameter_node = root.AddChildNode("parameter_links");
1190      if (!SaveParameterConfigEntries(parameter_node, *GetFrameworkElement()))
1191      {
1192        root.RemoveChildNode(parameter_node);
1193      }
1194
1195      // add dependencies
1196      if (dependencies_tmp.size() > 0)
1197      {
1198        std::stringstream s;
1199        for (auto it = dependencies_tmp.begin(); it != dependencies_tmp.end(); ++it)
1200        {
1201          if (it != dependencies_tmp.begin())
1202          {
1203            s << ", ";
1204          }
1205          s << it->ToString();
1206        }
1207
1208        root.SetAttribute("dependencies", s.str());
1209        dependencies_tmp.clear();
1210      }
1211
1212      doc.WriteToFile(save_to);
1213      FINROC_LOG_PRINT(USER, "Saving successful.");
1214
1215    }
1216    catch (const rrlib::xml::tException& e)
1217    {
1218      const char* msg = e.what();
1219      FINROC_LOG_PRINT(USER, "Saving failed: ", msg);
1220      throw std::runtime_error(msg);
1221    }
1222  }
1223  saving_thread = nullptr;
1224}
1225
1226std::vector<std::string> tFinstructable::ScanForCommandLineArgs(const std::string& finroc_file)
1227{
1228  std::vector<std::string> result;
1229  try
1230  {
1231    rrlib::xml::tDocument doc(core::GetFinrocXMLDocument(finroc_file, false));
1232    try
1233    {
1234      FINROC_LOG_PRINT_STATIC(DEBUG, "Scanning for command line options in ", finroc_file);
1235      rrlib::xml::tNode& root = doc.RootNode();
1236      ScanForCommandLineArgsHelper(result, root);
1237      FINROC_LOG_PRINTF_STATIC(DEBUG, "Scanning successful. Found %zu additional options.", result.size());
1238    }
1239    catch (std::exception& e)
1240    {
1241      FINROC_LOG_PRINT_STATIC(WARNING, "FinstructableGroup", "Scanning failed: ", finroc_file, e);
1242    }
1243  }
1244  catch (std::exception& e)
1245  {}
1246  return result;
1247}
1248
1249void tFinstructable::ScanForCommandLineArgsHelper(std::vector<std::string>& result, const rrlib::xml::tNode& parent)
1250{
1251  for (rrlib::xml::tNode::const_iterator node = parent.ChildrenBegin(); node != parent.ChildrenEnd(); ++node)
1252  {
1253    std::string name(node->Name());
1254    if (node->HasAttribute("cmdline") && (name == "staticparameter" || name == "parameter"))
1255    {
1256      result.push_back(node->GetStringAttribute("cmdline"));
1257    }
1258    ScanForCommandLineArgsHelper(result, *node);
1259  }
1260}
1261
1262void tFinstructable::SerializeChildren(rrlib::xml::tNode& node, tFrameworkElement& current)
1263{
1264  std::vector<std::pair<tOriginData, core::tFrameworkElement*>> children_to_serialize;
1265  std::vector<core::tFrameworkElement*> elements_to_scan = { &current };
1266  auto include_parent = tIncludeElement::GetIncludesParent(&current, false);
1267  if (include_parent)
1268  {
1269    elements_to_scan.push_back(include_parent);
1270  }
1271  for (auto element_to_scan : elements_to_scan)
1272  {
1273    for (auto child = element_to_scan->ChildrenBegin(); child != element_to_scan->ChildrenEnd(); ++child)
1274    {
1275      auto origin_data = child->GetAnnotation<tOriginDataAnnotation>();
1276      if (child->IsReady() && child->GetFlag(tFlag::FINSTRUCTED) && origin_data && (child->GetAnnotation<parameters::internal::tStaticParameterList>() || typeid(*child) == typeid(tIncludeElement)))
1277      {
1278        children_to_serialize.emplace_back(origin_data->Data(), &*child);
1279      }
1280    }
1281  }
1282
1283  std::sort(children_to_serialize.begin(), children_to_serialize.end());
1284
1285  for (auto & child_entry : children_to_serialize)
1286  {
1287    auto child = child_entry.second;
1288
1289    if (typeid(*child) == typeid(tIncludeElement))
1290    {
1291      rrlib::xml::tNode& n = node.AddChildNode("include");
1292      n.SetAttribute("file", static_cast<tIncludeElement*>(child)->IncludeFile());
1293      continue;
1294    }
1295
1296    parameters::internal::tStaticParameterList* spl = child->GetAnnotation<parameters::internal::tStaticParameterList>();
1297    tConstructorParameters* cps = child->GetAnnotation<tConstructorParameters>();
1298
1299    // serialize framework element
1300    rrlib::xml::tNode& n = node.AddChildNode("element");
1301    if (child->GetParent() != include_parent)
1302    {
1303      n.SetAttribute("name", child->GetName());
1304    }
1305    assert(spl);
1306    tCreateFrameworkElementAction* cma = tCreateFrameworkElementAction::GetConstructibleElements()[spl->GetCreateAction()];
1307    n.SetAttribute("group", cma->GetModuleGroup().ToString());
1308    //if (boost::ends_with(cma->GetModuleGroup(), ".so"))
1309    //{
1310    AddDependency(cma->GetModuleGroup());
1311    //}
1312    n.SetAttribute("type", cma->GetName());
1313    if (cps)
1314    {
1315      rrlib::xml::tNode& pn = n.AddChildNode("constructor");
1316      cps->Serialize(pn, true);
1317    }
1318    if (spl)
1319    {
1320      rrlib::xml::tNode& pn = n.AddChildNode("parameters");
1321      spl->Serialize(pn, true);
1322    }
1323
1324    // serialize its children
1325    if (!child->GetFlag(tFlag::FINSTRUCTABLE_GROUP))
1326    {
1327      SerializeChildren(n, *child);
1328    }
1329  }
1330}
1331
1332void tFinstructable::SetFinstructed(tFrameworkElement& fe, tCreateFrameworkElementAction& create_action, tConstructorParameters* params, tOriginData origin_data)
1333{
1334  assert(!fe.GetFlag(tFlag::FINSTRUCTED) && (!fe.IsReady()));
1335  parameters::internal::tStaticParameterList& list = parameters::internal::tStaticParameterList::GetOrCreate(fe);
1336  auto& element_list = tCreateFrameworkElementAction::GetConstructibleElements();
1337  for (size_t i = 0, n = element_list.Size(); i < n; ++i)
1338  {
1339    if (element_list[i] == &create_action)
1340    {
1341      list.SetCreateAction(i);
1342      break;
1343    }
1344  }
1345  bool include_finstructed = origin_data.origin;
1346  fe.SetFlag(tFlag::FINSTRUCTED, (!include_finstructed));
1347  if (params)
1348  {
1349    fe.AddAnnotation<tConstructorParameters>(*params);
1350  }
1351
1352  origin_data.order = std::chrono::steady_clock::now().time_since_epoch().count();
1353  fe.EmplaceAnnotation<tOriginDataAnnotation>(origin_data);
1354}
1355
1356void tFinstructable::StaticInit()
1357{
1358  startup_loaded_finroc_libs = GetLoadedFinrocLibraries();
1359}
1360
1361//----------------------------------------------------------------------
1362// End of namespace declaration
1363//----------------------------------------------------------------------
1364}
1365}
Note: See TracBrowser for help on using the repository browser.