source: finroc_plugins_runtime_construction/tFinstructable.cpp

tip
Last change on this file was 163:19d7bbecc1fb, checked in by Max Reichardt <max.reichardt@…>, 2 months ago

Improves handling of connectors loaded from XML files with no referenced port present at loading time.

File size: 52.9 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 (include_instance && node->HasAttribute("element"))
534          {
535            rrlib::uri::tURIElements uri_elements;
536            rrlib::uri::tURI uri(node->GetStringAttribute("element"));
537            uri.Parse(uri_elements);
538            extended_element = GetFrameworkElement()->GetDescendant(uri_elements.path);
539            editable_interfaces = extended_element ? extended_element->GetAnnotation<tEditableInterfaces>() : nullptr;
540          }
541          if (editable_interfaces)
542          {
543            try
544            {
545              core::tPortGroup& loaded_interface = editable_interfaces->LoadInterfacePorts(*node, !include_instance, include_instance ? include_instance->origin : nullptr);
546
547              // Move RPC port to suitable interfaces when loading legacy files
548              if (version == 0 && (!loaded_interface.GetFlag(tFlag::INTERFACE_FOR_RPC_PORTS)))
549              {
550                for (auto it = loaded_interface.ChildPortsBegin(); it != loaded_interface.ChildPortsEnd(); ++it)
551                {
552                  if (it->GetDataType().GetTypeClassification() == rrlib::rtti::tTypeClassification::RPC_TYPE)
553                  {
554                    core::tFrameworkElement* services_interface = nullptr;
555                    for (auto child_interface = GetFrameworkElement()->ChildrenBegin(); child_interface != GetFrameworkElement()->ChildrenEnd(); ++child_interface)
556                    {
557                      if (child_interface->IsReady() && (!child_interface->IsPort()) && child_interface->GetFlag(tFlag::INTERFACE) && child_interface->GetFlag(tFlag::INTERFACE_FOR_RPC_PORTS))
558                      {
559                        if (services_interface)
560                        {
561                          services_interface = nullptr;
562                          break;
563                        }
564                        services_interface = &(*child_interface);
565                      }
566                    }
567
568                    if (services_interface)
569                    {
570                      FINROC_LOG_PRINT(WARNING, "Moving RPC port '", it->GetName(), "' to RPC interface '", *services_interface, "' (auto-update loading legacy files).");
571                      auto cKEEP_FLAGS = (tFlag::ACCEPTS_DATA | tFlag::EMITS_DATA | tFlag::OUTPUT_PORT | tFlag::FINSTRUCTED_PORT).Raw();
572                      tPortCreationList creation_list(static_cast<core::tPortGroup&>(*services_interface), core::tFrameworkElementFlags(it->GetAllFlags().Raw() & cKEEP_FLAGS), tPortCreateOptions());
573                      creation_list.Add(it->GetName(), it->GetDataType());
574                      it->ManagedDelete();
575                    }
576                  }
577                }
578              }
579
580              if (extended_element)
581              {
582                parameters::internal::tStaticParameterList::DoStaticParameterEvaluation(*extended_element);
583              }
584            }
585            catch (const std::exception& e)
586            {
587              FINROC_LOG_PRINT(WARNING, "Loading interface ports failed. Reason: ", e);
588            }
589          }
590          else
591          {
592            FINROC_LOG_PRINT(WARNING, "Cannot load interface, because finstructable group does not have any editable interfaces.");
593          }
594        }
595        else if (name == "element" || name == "include")
596        {
597          // already instantiated
598        }
599        else if (name == "edge")
600        {
601          tConnectorData connector_data;
602          connector_data.source = node->GetStringAttribute("src");
603          connector_data.destination = node->GetStringAttribute("dest");
604          connector_data.structure_file_version = version;
605          connector_data.top_level_composite_component = this_is_outermost_composite_component;
606
607          try
608          {
609            core::tUriConnectOptions& connect_options = connector_data.connect_options;
610            connector_data.connect_options.flags.Set(core::tConnectionFlag::FINSTRUCTED, !static_cast<bool>(include_instance));  // TODO attach origin data to connectors also
611            if (node->HasAttribute("flags"))
612            {
613              connect_options.flags |= rrlib::serialization::Deserialize<core::tConnectionFlags>(node->GetStringAttribute("flags"));
614            }
615
616            for (rrlib::xml::tNode::const_iterator conversion_node = node->ChildrenBegin(); conversion_node != node->ChildrenEnd(); ++conversion_node)
617            {
618              if (conversion_node->Name() == "conversion")
619              {
620                rrlib::rtti::tType intermediate_type;
621                if (conversion_node->HasAttribute("intermediate_type"))
622                {
623                  intermediate_type = rrlib::rtti::tType::FindType(conversion_node->GetStringAttribute("intermediate_type"));
624                }
625                if (conversion_node->HasAttribute("operation2"))
626                {
627                  // Two operations
628                  std::string operation1 = conversion_node->GetStringAttribute("operation1");
629                  std::string operation2 = conversion_node->GetStringAttribute("operation2");
630                  connect_options.conversion_operations = rrlib::rtti::conversion::tConversionOperationSequence(operation1, operation2, intermediate_type);
631                  if (conversion_node->HasAttribute("parameter1"))
632                  {
633                    connect_options.conversion_operations.SetParameterValue(0, conversion_node->GetStringAttribute("parameter1"));
634                  }
635                  if (conversion_node->HasAttribute("parameter2"))
636                  {
637                    connect_options.conversion_operations.SetParameterValue(1, conversion_node->GetStringAttribute("parameter2"));
638                  }
639                }
640                else if (conversion_node->HasAttribute("operation"))
641                {
642                  // One operation
643                  std::string operation1 = conversion_node->GetStringAttribute("operation");
644                  connect_options.conversion_operations = rrlib::rtti::conversion::tConversionOperationSequence(operation1, "", intermediate_type);
645                  if (conversion_node->HasAttribute("parameter"))
646                  {
647                    connect_options.conversion_operations.SetParameterValue(0, conversion_node->GetStringAttribute("parameter"));
648                  }
649                }
650              }
651            }
652
653            // read parameters
654            for (rrlib::xml::tNode::const_iterator parameter_node = node->ChildrenBegin(); parameter_node != node->ChildrenEnd(); ++parameter_node)
655            {
656              if (parameter_node->Name() == "parameter")
657              {
658                connect_options.parameters.emplace(parameter_node->GetStringAttribute("name"), parameter_node->GetTextContent());
659              }
660            }
661
662            connector_data.status = InstantiateConnector(connector_data, path_to_this);
663            if (connector_data.status.length())
664            {
665              this->pending_connectors.push_back(connector_data);
666              if (this->fully_initialized)
667              {
668                FINROC_LOG_PRINT(WARNING, "Creating connector from ", connector_data.source, " to ", connector_data.destination, " failed. Reason: ", connector_data.status);
669              }
670            }
671          }
672          catch (const std::exception& e)
673          {
674            FINROC_LOG_PRINT(WARNING, "Creating connector from ", connector_data.source, " to ", connector_data.destination, " failed. Reason: ", e.what());
675          }
676        }
677        else if (name == "parameter") // legacy parameter info support
678        {
679          core::tURI parameter_uri(node->GetStringAttribute("link"));
680          rrlib::uri::tURIElements parameter_uri_parsed;
681          parameter_uri.Parse(parameter_uri_parsed);
682          core::tAbstractPort* parameter = GetChildPort(parameter_uri_parsed.path);
683          if (!parameter)
684          {
685            FINROC_LOG_PRINT(WARNING, "Cannot set config entry, because parameter is not available: ", parameter_uri.ToString());
686          }
687          else
688          {
689            LoadParameter(*node, *parameter);
690          }
691        }
692        else if (name == "parameter_links")
693        {
694          ProcessParameterLinksNode(*node, *GetFrameworkElement());
695          if (node->HasAttribute("config"))
696          {
697            core::tFrameworkElement* parameters_interface = GetFrameworkElement()->GetChild("Parameters");
698            if (parameters_interface)
699            {
700              LoadParameter(*node, *parameters_interface);
701            }
702          }
703        }
704        else
705        {
706          FINROC_LOG_PRINT(WARNING, "Unknown XML tag: ", name);
707        }
708      }
709      FINROC_LOG_PRINT(DEBUG, "Loading XML successful");
710    }
711    catch (const std::exception& e)
712    {
713      FINROC_LOG_PRINT(WARNING, "Loading XML '", GetXmlFileString(), "' failed: ", e);
714    }
715  }
716}
717
718std::string tFinstructable::InstantiateConnector(const tConnectorData& connector_data, const core::tPath& path_to_this)
719{
720  auto version = connector_data.structure_file_version;
721  try
722  {
723    rrlib::uri::tURI source_uri(connector_data.source);
724    rrlib::uri::tURI destination_uri(connector_data.destination);
725
726    rrlib::uri::tURIElements source_uri_parsed;
727    rrlib::uri::tURIElements destination_uri_parsed;
728    if (version)
729    {
730      source_uri.Parse(source_uri_parsed);
731      destination_uri.Parse(destination_uri_parsed);
732    }
733    else
734    {
735      source_uri_parsed.path = rrlib::uri::tPath(connector_data.source);
736      destination_uri_parsed.path = rrlib::uri::tPath(connector_data.destination);
737    }
738
739    if (source_uri_parsed.scheme.length() == 0 && destination_uri_parsed.scheme.length() == 0)
740    {
741      core::tAbstractPort* source_port = GetChildPort(source_uri_parsed.path);
742      core::tAbstractPort* destination_port = GetChildPort(destination_uri_parsed.path);
743
744      // Backward-compatibility: Check whether this a connector between service interfaces now
745      if (version == 0 && source_port == nullptr && destination_port == nullptr)
746      {
747        core::tAbstractPort* service_source_port = GetChildPort(ReplaceInterfaceInPath(source_uri_parsed.path, "Services"));
748        core::tAbstractPort* service_destination_port = GetChildPort(ReplaceInterfaceInPath(destination_uri_parsed.path, "Services"));
749        if (service_source_port && service_destination_port && service_source_port->GetDataType().GetTypeClassification() == rrlib::rtti::tTypeClassification::RPC_TYPE)
750        {
751          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, "'");
752          source_port = service_source_port;
753          destination_port = service_destination_port;
754        }
755      }
756      else if (version == 0 && source_port && source_port->GetDataType().GetTypeClassification() == rrlib::rtti::tTypeClassification::RPC_TYPE && destination_port == nullptr)
757      {
758        core::tAbstractPort* service_destination_port = GetChildPort(ReplaceInterfaceInPath(destination_uri_parsed.path, "Services"));
759        if (service_destination_port && service_destination_port->GetDataType() == source_port->GetDataType())
760        {
761          FINROC_LOG_PRINT(WARNING, "Adjusted connector's interfaces to service interfaces (auto-update loading legacy files): now connects '", *source_port, "' and '", *service_destination_port, "'");
762          destination_port = service_destination_port;
763        }
764      }
765      else if (version == 0 && destination_port && destination_port->GetDataType().GetTypeClassification() == rrlib::rtti::tTypeClassification::RPC_TYPE && source_port == nullptr)
766      {
767        core::tAbstractPort* service_source_port = GetChildPort(ReplaceInterfaceInPath(source_uri_parsed.path, "Services"));
768        if (service_source_port && service_source_port->GetDataType() == destination_port->GetDataType())
769        {
770          FINROC_LOG_PRINT(WARNING, "Adjusted connector's interfaces to service interfaces (auto-update loading legacy files): now connects '", *service_source_port, "' and '", *destination_port, "'");
771          source_port = service_source_port;
772        }
773      }
774
775      if (source_port == nullptr && destination_port == nullptr)
776      {
777        std::stringstream error_message;
778        error_message << "Cannot create connector because neither port is available: '" << source_uri_parsed.path << "' and '" << destination_uri_parsed.path << "'";
779        throw std::runtime_error(error_message.str());
780      }
781      else if (source_port == nullptr || source_port->GetFlag(tFlag::VOLATILE))
782      {
783        if (source_uri_parsed.path.IsAbsolute() && connector_data.top_level_composite_component && version == 0)
784        {
785          FINROC_LOG_PRINT(WARNING, "Interpreting absolute connector source path (", source_uri_parsed.path, ") as legacy TCP connection");
786          destination_port->ConnectTo(rrlib::uri::tURI("tcp:" + rrlib::uri::tURI(source_uri_parsed.path).ToString()), core::tConnectionFlag::FINSTRUCTED);
787        }
788        else
789        {
790          destination_port->ConnectTo(source_uri_parsed.path.IsAbsolute() ? source_uri_parsed.path : path_to_this.Append(source_uri_parsed.path), connector_data.connect_options);
791        }
792      }
793      else if (destination_port == nullptr || destination_port->GetFlag(tFlag::VOLATILE))
794      {
795        if (destination_uri_parsed.path.IsAbsolute() && connector_data.top_level_composite_component && version == 0)
796        {
797          FINROC_LOG_PRINT(WARNING, "Interpreting absolute connector destination path (", destination_uri_parsed.path, ") as legacy TCP connection");
798          source_port->ConnectTo(rrlib::uri::tURI("tcp:" + rrlib::uri::tURI(destination_uri_parsed.path).ToString()), core::tConnectionFlag::FINSTRUCTED);
799        }
800        else
801        {
802          source_port->ConnectTo(destination_uri_parsed.path.IsAbsolute() ? destination_uri_parsed.path : path_to_this.Append(destination_uri_parsed.path), connector_data.connect_options);
803        }
804      }
805      else
806      {
807        source_port->ConnectTo(*destination_port, connector_data.connect_options);
808      }
809    }
810    else
811    {
812      // Create URI connector
813      if (source_uri_parsed.scheme.length() && destination_uri_parsed.scheme.length())
814      {
815        throw std::runtime_error("Only one port may have an address with an URI scheme");
816      }
817      core::tAbstractPort* source_port = GetChildPort(source_uri_parsed.scheme.length() == 0 ? source_uri_parsed.path : destination_uri_parsed.path);
818      if (!source_port)
819      {
820        std::stringstream message;
821        message << "Cannot create connector because port is not available: " << (source_uri_parsed.scheme.length() == 0 ? source_uri_parsed.path : destination_uri_parsed.path);
822        throw std::runtime_error(message.str());
823      }
824      const core::tURI& scheme_uri = source_uri_parsed.scheme.length() != 0 ? source_uri : destination_uri;
825
826      core::tUriConnector::Create(*source_port, scheme_uri, connector_data.connect_options);
827    }
828  }
829  catch (const std::exception& e)
830  {
831    return e.what();
832  }
833  return "";
834}
835
836void tFinstructable::OnInitialization()
837{
838  if (!GetFrameworkElement()->GetFlag(tFlag::FINSTRUCTABLE_GROUP))
839  {
840    throw std::logic_error("Any class using tFinstructable must set tFlag::FINSTRUCTABLE_GROUP in constructor");
841  }
842
843  if (pending_connectors.empty())
844  {
845    fully_initialized = true;
846  }
847  else
848  {
849    GetAnnotated<core::tFrameworkElement>()->EmplaceAnnotation<tOnFullInitialization>(*this, fully_initialized);
850  }
851}
852
853void tFinstructable::ProcessParameterLinksNode(const rrlib::xml::tNode& node, core::tFrameworkElement& element)
854{
855  for (auto it = node.ChildrenBegin(); it != node.ChildrenEnd(); ++it)
856  {
857    if (it->Name() == "element")
858    {
859      std::string name = it->GetStringAttribute("name");
860      core::tFrameworkElement* corresponding_element = element.GetChild(name);
861      if (corresponding_element)
862      {
863        ProcessParameterLinksNode(*it, *corresponding_element);
864
865        if (it->HasAttribute("config"))
866        {
867          core::tFrameworkElement* parameters_interface = corresponding_element->GetChild("Parameters");
868          if (parameters_interface)
869          {
870            LoadParameter(*it, *parameters_interface);
871          }
872        }
873      }
874      else
875      {
876        FINROC_LOG_PRINT(WARNING, "Cannot find '", element, "/", name, "'. Parameter entries below are not loaded.");
877      }
878    }
879    else if (it->Name() == "parameter")
880    {
881      std::string name = it->GetStringAttribute("name");
882      core::tFrameworkElement* parameter_element = element.GetChild(name);
883      if (!parameter_element)
884      {
885        core::tFrameworkElement* parameters_interface = element.GetChild("Parameters");
886        parameter_element = parameters_interface ? parameters_interface->GetChild(name) : nullptr;
887      }
888      if (parameter_element && parameter_element->IsPort())
889      {
890        LoadParameter(*it, *parameter_element);
891      }
892      else
893      {
894        FINROC_LOG_PRINT(WARNING, "Cannot find parameter '", element, "/", name, "'. Parameter entry is not loaded.");
895      }
896    }
897  }
898}
899
900bool tFinstructable::SaveParameterConfigEntries(rrlib::xml::tNode& node, core::tFrameworkElement& element)
901{
902  // Get alphabetically sorted list of children
903  std::vector<core::tFrameworkElement*> child_elements;
904  std::vector<core::tAbstractPort*> parameter_ports;
905  core::tFrameworkElement* parameters_interface = nullptr;
906  for (auto it = element.ChildrenBegin(); it != element.ChildrenEnd(); ++it)
907  {
908    if (it->IsReady())
909    {
910      if ((!it->IsPort()) && it->GetFlag(tFlag::INTERFACE) && it->GetFlag(tFlag::PARAMETER_INTERFACE))
911      {
912        for (auto parameter_it = it->ChildrenBegin(); parameter_it != it->ChildrenEnd(); ++parameter_it)
913        {
914          if (parameter_it->IsReady() && parameter_it->IsPort() && parameter_it->GetAnnotation<parameters::internal::tParameterInfo>())
915          {
916            parameter_ports.push_back(&static_cast<core::tAbstractPort&>(*parameter_it));
917          }
918        }
919        parameters_interface = &*it;
920      }
921      else if (it->IsPort() && it->GetAnnotation<parameters::internal::tParameterInfo>())
922      {
923        parameter_ports.push_back(&static_cast<core::tAbstractPort&>(*it));
924      }
925      else
926      {
927        child_elements.push_back(&(*it));
928      }
929    }
930  }
931
932  struct
933  {
934    bool operator()(core::tFrameworkElement* a, core::tFrameworkElement* b)
935    {
936      return a->GetName() < b->GetName();
937    }
938  } comparator;
939  std::sort(child_elements.begin(), child_elements.end(), comparator);
940  std::sort(parameter_ports.begin(), parameter_ports.end(), comparator);
941
942  // Save parameters first
943  bool result = false;
944  for (core::tAbstractPort * port : parameter_ports)
945  {
946    bool outermost_group = GetFrameworkElement()->GetParent() == &(core::tRuntimeEnvironment::GetInstance());
947    parameters::internal::tParameterInfo* info = port->GetAnnotation<parameters::internal::tParameterInfo>();
948    bool is_responsible_for_parameter_links = IsResponsibleForConfigFileConnections(*port);
949
950    if (info && HasNonDefaultFinstructInfo(*info) && (is_responsible_for_parameter_links || (outermost_group && info->GetCommandLineOption().length())))
951    {
952      // Save Parameter
953      rrlib::xml::tNode& parameter_node = node.AddChildNode("parameter");
954      parameter_node.SetAttribute("name", port->GetName());
955
956      if (!is_responsible_for_parameter_links)
957      {
958        parameter_node.SetAttribute("cmdline", info->GetCommandLineOption());
959      }
960      else
961      {
962        info->Serialize(parameter_node, true, outermost_group, nullptr);
963      }
964      result = true;
965    }
966  }
967
968  // Save possible config entry for parameters interface
969  if (parameters_interface)
970  {
971    parameters::internal::tParameterInfo* info = parameters_interface->GetAnnotation<parameters::internal::tParameterInfo>();
972    bool is_responsible_for_parameter_links = IsResponsibleForConfigFileConnections(*parameters_interface);
973    if (info && HasNonDefaultFinstructInfo(*info) && is_responsible_for_parameter_links)
974    {
975      info->Serialize(node, true, false, nullptr);
976      result = true;
977    }
978  }
979
980  // Process child elements
981  for (core::tFrameworkElement * child : child_elements)
982  {
983    rrlib::xml::tNode& child_node = node.AddChildNode("element");
984    child_node.SetAttribute("name", child->GetName());
985    bool sub_result = SaveParameterConfigEntries(child_node, *child);
986    result |= sub_result;
987    if (!sub_result)
988    {
989      node.RemoveChildNode(child_node);
990    }
991  }
992  return result;
993}
994
995void tFinstructable::SaveXml()
996{
997  {
998    rrlib::thread::tLock lock2(core::tRuntimeEnvironment::GetInstance().GetStructureMutex());
999#ifndef RRLIB_SINGLE_THREADED
1000    saving_thread = &rrlib::thread::tThread::CurrentThread();
1001#endif
1002    dependencies_tmp.clear();
1003    std::string save_to = core::GetFinrocFileToSaveTo(GetXmlFileString());
1004    if (save_to.length() == 0)
1005    {
1006      std::string save_to_alt = GetXmlFileString();
1007      std::replace(save_to_alt.begin(), save_to_alt.end(), '/', '_');
1008      FINROC_LOG_PRINT(USER, "There does not seem to be any suitable location for: '", GetXmlFileString(), "' . For now, using '", save_to_alt, "'.");
1009      save_to = save_to_alt;
1010    }
1011    FINROC_LOG_PRINT(USER, "Saving XML: ", save_to);
1012    rrlib::xml::tDocument doc;
1013    try
1014    {
1015      rrlib::xml::tNode& root = doc.AddRootNode("Finstructable"); // TODO: find good name ("finroc_structure")
1016
1017      // serialize default main name
1018      if (main_name.length() > 0)
1019      {
1020        root.SetAttribute("defaultname", main_name);
1021      }
1022      root.SetAttribute("version", cVERSION);
1023
1024      // serialize any editable interfaces
1025      tEditableInterfaces* editable_interfaces = GetFrameworkElement()->GetAnnotation<tEditableInterfaces>();
1026      if (editable_interfaces)
1027      {
1028        editable_interfaces->SaveAllNonEmptyInterfaces(root);
1029      }
1030
1031      // serialize framework elements
1032      SerializeChildren(root, *GetFrameworkElement());
1033
1034      // serialize connectors (sorted by port URIs (we do not want changes in finstruct files depending on whether or not an optional/volatile connector exists))
1035      rrlib::uri::tPath this_path = GetFrameworkElement()->GetPath();
1036      std::map<std::pair<rrlib::uri::tURI, rrlib::uri::tURI>, std::pair<core::tConnector*, core::tUriConnector*>> connector_map;
1037      bool this_is_outermost_composite_component = GetFrameworkElement()->GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP) == nullptr;
1038
1039      // Get (primary) connectors
1040      for (auto it = GetFrameworkElement()->SubElementsBegin(); it != GetFrameworkElement()->SubElementsEnd(); ++it)
1041      {
1042        if ((!it->IsPort()) || (!it->IsReady()))
1043        {
1044          continue;
1045        }
1046        core::tAbstractPort& port = static_cast<core::tAbstractPort&>(*it);
1047        tFrameworkElement* port_parent_group = port.GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP);
1048
1049        // Plain connectors
1050        for (auto it = port.OutgoingConnectionsBegin(); it != port.OutgoingConnectionsEnd(); ++it) // only outgoing edges => we don't get any edges twice
1051        {
1052          // Checks whether to save connector
1053          // only save primary finstructed edges
1054          if ((!it->Flags().Get(core::tConnectionFlag::FINSTRUCTED)) || it->Flags().Get(core::tConnectionFlag::NON_PRIMARY_CONNECTOR))
1055          {
1056            continue;
1057          }
1058
1059          // 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
1060          tFrameworkElement* common_parent = port_parent_group;
1061          while (common_parent && (!it->Destination().IsChildOf(*common_parent)))
1062          {
1063            common_parent = common_parent->GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP);
1064          }
1065          if (common_parent != GetFrameworkElement() && (!(this_is_outermost_composite_component && common_parent == nullptr)))
1066          {
1067            // save connector in another group
1068            continue;
1069          }
1070
1071          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));
1072          std::pair<core::tConnector*, core::tUriConnector*> value(&(*it), nullptr);
1073          connector_map.emplace(key, value);
1074        }
1075
1076        // Get URI connectors
1077        for (auto & connector : port.UriConnectors())
1078        {
1079          // Checks whether to save connector
1080          // only save primary finstructed edges
1081          if ((!connector) || (!connector->Flags().Get(core::tConnectionFlag::FINSTRUCTED)) || connector->Flags().Get(core::tConnectionFlag::NON_PRIMARY_CONNECTOR))
1082          {
1083            continue;
1084          }
1085
1086          std::pair<rrlib::uri::tURI, rrlib::uri::tURI> key(rrlib::uri::tURI(GetConnectorPath(port.GetPath(), this_path), cUNENCODED_RESERVED_CHARACTERS_PATH), connector->Uri());
1087
1088          // 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
1089          core::tUriConnector& connector_reference = *connector;
1090          if (typeid(connector_reference) == typeid(core::internal::tLocalUriConnector))
1091          {
1092            auto& local_connector = static_cast<core::internal::tLocalUriConnector&>(*connector);
1093            bool source_uri = local_connector.GetPortReferences()[0].path.Size();
1094            rrlib::uri::tPath path = local_connector.GetPortReferences()[source_uri ? 0 : 1].path;
1095            tFrameworkElement* common_parent = port_parent_group;
1096            rrlib::uri::tPath parent_group_path = common_parent->GetPath();
1097            while (common_parent && path.CountCommonElements(parent_group_path) != parent_group_path.Size())
1098            {
1099              common_parent = common_parent->GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP);
1100              parent_group_path = common_parent ? common_parent->GetPath() : parent_group_path;
1101            }
1102            if (common_parent != GetFrameworkElement() && (!(this_is_outermost_composite_component && common_parent == nullptr)))
1103            {
1104              // save connector in another group
1105              continue;
1106            }
1107
1108            rrlib::uri::tURI port_uri = key.first;
1109            key.first = source_uri ? rrlib::uri::tURI(GetConnectorPath(path, parent_group_path), cUNENCODED_RESERVED_CHARACTERS_PATH) : port_uri;
1110            key.second = source_uri ? port_uri : rrlib::uri::tURI(GetConnectorPath(path, parent_group_path), cUNENCODED_RESERVED_CHARACTERS_PATH);
1111          }
1112
1113          std::pair<core::tConnector*, core::tUriConnector*> value(nullptr, connector.get());
1114          connector_map.emplace(key, value);
1115        }
1116      }
1117
1118      for (auto & entry : connector_map)
1119      {
1120        rrlib::xml::tNode& edge = root.AddChildNode("edge"); // TODO: "connector"?
1121        edge.SetAttribute("src", entry.first.first.ToString());
1122        edge.SetAttribute("dest", entry.first.second.ToString());
1123
1124        // Save flags
1125        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;
1126        core::tConnectionFlags flags_to_save((entry.second.first ? entry.second.first->Flags() : entry.second.second->Flags()).Raw() & cFLAGS_TO_SAVE.Raw());
1127        if (flags_to_save.Raw())
1128        {
1129          edge.SetAttribute("flags", rrlib::serialization::Serialize(flags_to_save));
1130        }
1131
1132        // Save conversion operation
1133        const rrlib::rtti::conversion::tConversionOperationSequence& conversion_operations = entry.second.first ? entry.second.first->ConversionOperations() : entry.second.second->ConversionOperations();
1134        if (conversion_operations.Size())
1135        {
1136          rrlib::xml::tNode& conversion = edge.AddChildNode("conversion");
1137          conversion.SetAttribute(conversion_operations.Size() == 2 ? "operation1" : "operation", conversion_operations[0].first);
1138          if (conversion_operations.GetParameterValue(0))
1139          {
1140            conversion.SetAttribute(conversion_operations.Size() == 2 ? "parameter1" : "parameter", conversion_operations.GetParameterValue(0).ToString());
1141          }
1142          if (conversion_operations.IntermediateType())
1143          {
1144            conversion.SetAttribute("intermediate_type", conversion_operations.IntermediateType().GetName());
1145          }
1146          if (conversion_operations.Size() == 2)
1147          {
1148            conversion.SetAttribute("operation2", conversion_operations[1].first);
1149            if (conversion_operations.GetParameterValue(1))
1150            {
1151              conversion.SetAttribute("parameter2", conversion_operations.GetParameterValue(1).ToString());
1152            }
1153          }
1154        }
1155
1156        // Save parameters of URI connector
1157        if (entry.second.second)
1158        {
1159          core::tUriConnector::tConstParameterDefinitionRange definition_range = entry.second.second->GetParameterDefinitions();
1160          core::tUriConnector::tParameterValueRange value_range = entry.second.second->GetParameterValues();
1161          assert(definition_range.End() - definition_range.Begin() == value_range.End() - value_range.Begin());
1162
1163          auto definition = definition_range.Begin();
1164          auto value = value_range.Begin();
1165          for (; definition < definition_range.End(); ++definition, ++value)
1166          {
1167            if (!value->Equals(definition->GetDefaultValue()))
1168            {
1169              rrlib::xml::tNode& parameter_node = edge.AddChildNode("parameter");
1170              parameter_node.SetAttribute("name", definition->GetName());
1171              value->Serialize(parameter_node);
1172            }
1173          }
1174        }
1175      }
1176
1177      // Save parameter config entries
1178      auto& parameter_node = root.AddChildNode("parameter_links");
1179      if (!SaveParameterConfigEntries(parameter_node, *GetFrameworkElement()))
1180      {
1181        root.RemoveChildNode(parameter_node);
1182      }
1183
1184      // add dependencies
1185      if (dependencies_tmp.size() > 0)
1186      {
1187        std::stringstream s;
1188        for (auto it = dependencies_tmp.begin(); it != dependencies_tmp.end(); ++it)
1189        {
1190          if (it != dependencies_tmp.begin())
1191          {
1192            s << ", ";
1193          }
1194          s << it->ToString();
1195        }
1196
1197        root.SetAttribute("dependencies", s.str());
1198        dependencies_tmp.clear();
1199      }
1200
1201      doc.WriteToFile(save_to);
1202      FINROC_LOG_PRINT(USER, "Saving successful.");
1203
1204    }
1205    catch (const rrlib::xml::tException& e)
1206    {
1207      const char* msg = e.what();
1208      FINROC_LOG_PRINT(USER, "Saving failed: ", msg);
1209      throw std::runtime_error(msg);
1210    }
1211  }
1212  saving_thread = nullptr;
1213}
1214
1215std::vector<std::string> tFinstructable::ScanForCommandLineArgs(const std::string& finroc_file)
1216{
1217  std::vector<std::string> result;
1218  try
1219  {
1220    rrlib::xml::tDocument doc(core::GetFinrocXMLDocument(finroc_file, false));
1221    try
1222    {
1223      FINROC_LOG_PRINT_STATIC(DEBUG, "Scanning for command line options in ", finroc_file);
1224      rrlib::xml::tNode& root = doc.RootNode();
1225      ScanForCommandLineArgsHelper(result, root);
1226      FINROC_LOG_PRINTF_STATIC(DEBUG, "Scanning successful. Found %zu additional options.", result.size());
1227    }
1228    catch (std::exception& e)
1229    {
1230      FINROC_LOG_PRINT_STATIC(WARNING, "FinstructableGroup", "Scanning failed: ", finroc_file, e);
1231    }
1232  }
1233  catch (std::exception& e)
1234  {}
1235  return result;
1236}
1237
1238void tFinstructable::ScanForCommandLineArgsHelper(std::vector<std::string>& result, const rrlib::xml::tNode& parent)
1239{
1240  for (rrlib::xml::tNode::const_iterator node = parent.ChildrenBegin(); node != parent.ChildrenEnd(); ++node)
1241  {
1242    std::string name(node->Name());
1243    if (node->HasAttribute("cmdline") && (name == "staticparameter" || name == "parameter"))
1244    {
1245      result.push_back(node->GetStringAttribute("cmdline"));
1246    }
1247    ScanForCommandLineArgsHelper(result, *node);
1248  }
1249}
1250
1251void tFinstructable::SerializeChildren(rrlib::xml::tNode& node, tFrameworkElement& current)
1252{
1253  std::vector<std::pair<tOriginData, core::tFrameworkElement*>> children_to_serialize;
1254  std::vector<core::tFrameworkElement*> elements_to_scan = { &current };
1255  auto include_parent = tIncludeElement::GetIncludesParent(&current, false);
1256  if (include_parent)
1257  {
1258    elements_to_scan.push_back(include_parent);
1259  }
1260  for (auto element_to_scan : elements_to_scan)
1261  {
1262    for (auto child = element_to_scan->ChildrenBegin(); child != element_to_scan->ChildrenEnd(); ++child)
1263    {
1264      auto origin_data = child->GetAnnotation<tOriginDataAnnotation>();
1265      if (child->IsReady() && child->GetFlag(tFlag::FINSTRUCTED) && origin_data && (child->GetAnnotation<parameters::internal::tStaticParameterList>() || typeid(*child) == typeid(tIncludeElement)))
1266      {
1267        children_to_serialize.emplace_back(origin_data->Data(), &*child);
1268      }
1269    }
1270  }
1271
1272  std::sort(children_to_serialize.begin(), children_to_serialize.end());
1273
1274  for (auto & child_entry : children_to_serialize)
1275  {
1276    auto child = child_entry.second;
1277
1278    if (typeid(*child) == typeid(tIncludeElement))
1279    {
1280      rrlib::xml::tNode& n = node.AddChildNode("include");
1281      n.SetAttribute("file", static_cast<tIncludeElement*>(child)->IncludeFile());
1282      continue;
1283    }
1284
1285    parameters::internal::tStaticParameterList* spl = child->GetAnnotation<parameters::internal::tStaticParameterList>();
1286    tConstructorParameters* cps = child->GetAnnotation<tConstructorParameters>();
1287
1288    // serialize framework element
1289    rrlib::xml::tNode& n = node.AddChildNode("element");
1290    if (child->GetParent() != include_parent)
1291    {
1292      n.SetAttribute("name", child->GetName());
1293    }
1294    assert(spl);
1295    tCreateFrameworkElementAction* cma = tCreateFrameworkElementAction::GetConstructibleElements()[spl->GetCreateAction()];
1296    n.SetAttribute("group", cma->GetModuleGroup().ToString());
1297    //if (boost::ends_with(cma->GetModuleGroup(), ".so"))
1298    //{
1299    AddDependency(cma->GetModuleGroup());
1300    //}
1301    n.SetAttribute("type", cma->GetName());
1302    if (cps)
1303    {
1304      rrlib::xml::tNode& pn = n.AddChildNode("constructor");
1305      cps->Serialize(pn, true);
1306    }
1307    if (spl)
1308    {
1309      rrlib::xml::tNode& pn = n.AddChildNode("parameters");
1310      spl->Serialize(pn, true);
1311    }
1312
1313    // serialize its children
1314    if (!child->GetFlag(tFlag::FINSTRUCTABLE_GROUP))
1315    {
1316      SerializeChildren(n, *child);
1317    }
1318  }
1319}
1320
1321void tFinstructable::SetFinstructed(tFrameworkElement& fe, tCreateFrameworkElementAction& create_action, tConstructorParameters* params, tOriginData origin_data)
1322{
1323  assert(!fe.GetFlag(tFlag::FINSTRUCTED) && (!fe.IsReady()));
1324  parameters::internal::tStaticParameterList& list = parameters::internal::tStaticParameterList::GetOrCreate(fe);
1325  auto& element_list = tCreateFrameworkElementAction::GetConstructibleElements();
1326  for (size_t i = 0, n = element_list.Size(); i < n; ++i)
1327  {
1328    if (element_list[i] == &create_action)
1329    {
1330      list.SetCreateAction(i);
1331      break;
1332    }
1333  }
1334  bool include_finstructed = origin_data.origin;
1335  fe.SetFlag(tFlag::FINSTRUCTED, (!include_finstructed));
1336  if (params)
1337  {
1338    fe.AddAnnotation<tConstructorParameters>(*params);
1339  }
1340
1341  origin_data.order = std::chrono::steady_clock::now().time_since_epoch().count();
1342  fe.EmplaceAnnotation<tOriginDataAnnotation>(origin_data);
1343}
1344
1345void tFinstructable::StaticInit()
1346{
1347  startup_loaded_finroc_libs = GetLoadedFinrocLibraries();
1348}
1349
1350//----------------------------------------------------------------------
1351// End of namespace declaration
1352//----------------------------------------------------------------------
1353}
1354}
Note: See TracBrowser for help on using the repository browser.