source: finroc_plugins_runtime_construction/tFinstructable.cpp @ 92:ee860f3b6448

17.03
Last change on this file since 92:ee860f3b6448 was 92:ee860f3b6448, checked in by Max Reichardt <mreichardt@…>, 4 years ago

Removes obsolete (and in some cases erroneous) setting of RECONNECT flag for connectors when loading finstructable files

File size: 38.2 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
41//----------------------------------------------------------------------
42// Internal includes with ""
43//----------------------------------------------------------------------
44#include "plugins/runtime_construction/tEditableInterfaces.h"
45#include "plugins/runtime_construction/dynamic_loading.h"
46
47//----------------------------------------------------------------------
48// Debugging
49//----------------------------------------------------------------------
50#include <cassert>
51
52//----------------------------------------------------------------------
53// Namespace usage
54//----------------------------------------------------------------------
55
56//----------------------------------------------------------------------
57// Namespace declaration
58//----------------------------------------------------------------------
59namespace finroc
60{
61namespace runtime_construction
62{
63
64//----------------------------------------------------------------------
65// Forward declarations / typedefs / enums
66//----------------------------------------------------------------------
67typedef core::tFrameworkElement tFrameworkElement;
68typedef tFrameworkElement::tFlag tFlag;
69
70//----------------------------------------------------------------------
71// Const values
72//----------------------------------------------------------------------
73
74//----------------------------------------------------------------------
75// Implementation
76//----------------------------------------------------------------------
77
78/*! Thread currently saving finstructable group */
79static rrlib::thread::tThread* saving_thread = nullptr;
80
81/*! Temporary variable for saving: .so files that should be loaded prior to instantiating this group */
82static std::set<tSharedLibrary> dependencies_tmp;
83
84/*! Loaded finroc libraries at startup */
85static std::set<tSharedLibrary> startup_loaded_finroc_libs;
86
87/*! We do not want to have this prefix in XML file names, as this will not be found when a system installation is used */
88static const char* cUNWANTED_XML_FILE_PREFIX = "sources/cpp/";
89
90tFinstructable::tFinstructable(const std::string& xml_file) :
91  main_name(),
92  xml_file(xml_file)
93{
94}
95
96void tFinstructable::AddDependency(const tSharedLibrary& dependency)
97{
98  if (&rrlib::thread::tThread::CurrentThread() == saving_thread && startup_loaded_finroc_libs.find(dependency) == startup_loaded_finroc_libs.end())
99  {
100    dependencies_tmp.insert(dependency);
101  }
102}
103
104void tFinstructable::AddDependency(const rrlib::rtti::tType& dt)
105{
106  tSharedLibrary shared_library = GetDataTypeDependency(dt);
107  if (shared_library.IsValid())
108  {
109    AddDependency(shared_library);
110  }
111}
112
113core::tAbstractPort* tFinstructable::GetChildPort(const core::tPath& path)
114{
115  tFrameworkElement* element = GetFrameworkElement()->GetChild(path);
116  if (element && element->IsPort())
117  {
118    return static_cast<core::tAbstractPort*>(element);
119  }
120  return nullptr;
121}
122
123core::tPath tFinstructable::GetConnectorPath(const core::tPath& target_path, const core::tPath& this_group_path)
124{
125  if (target_path.CountCommonElements(this_group_path) == this_group_path.Size())
126  {
127    return core::tPath(false, target_path.Begin() + this_group_path.Size(), target_path.End());
128  }
129  return target_path;
130}
131
132core::tPath tFinstructable::GetConnectorPath(core::tAbstractPort& port, const core::tPath& this_group_path)
133{
134  core::tPath port_path = port.GetPath();
135  tFrameworkElement* alt_root = port.GetParentWithFlags(tFlag::ALTERNATIVE_LOCAL_URI_ROOT);
136  if (alt_root && alt_root->IsChildOf(*GetFrameworkElement()))
137  {
138    return core::tPath(true, port_path.Begin() + alt_root->GetPath().Size(), port_path.End());
139  }
140  return core::tPath(true, port_path.Begin() + this_group_path.Size(), port_path.End());
141}
142
143std::string tFinstructable::GetLogDescription() const
144{
145  if (GetFrameworkElement())
146  {
147    std::stringstream stream;
148    stream << *GetFrameworkElement();
149    return stream.str();
150  }
151  return "Unattached Finstructable";
152}
153
154std::string tFinstructable::GetXmlFileString()
155{
156  std::string s = xml_file;
157  if (s.find(cUNWANTED_XML_FILE_PREFIX) != std::string::npos)
158  {
159    FINROC_LOG_PRINT(WARNING, "XML file name '", s, "' is deprecated, because it contains '", cUNWANTED_XML_FILE_PREFIX, "'. File will not be found when installed.");
160  }
161  return s;
162}
163
164void tFinstructable::Instantiate(const rrlib::xml::tNode& node, tFrameworkElement* parent)
165{
166  std::string name = "component name not read";
167  try
168  {
169    name = node.GetStringAttribute("name");
170    std::string group = node.GetStringAttribute("group");
171    std::string type = node.GetStringAttribute("type");
172
173    // find action
174    tCreateFrameworkElementAction& action = LoadComponentType(group, type);
175
176    // read parameters
177    rrlib::xml::tNode::const_iterator child_node = node.ChildrenBegin();
178    const rrlib::xml::tNode* parameters = nullptr;
179    const rrlib::xml::tNode* constructor_params = nullptr;
180    std::string p_name = child_node->Name();
181    if (p_name == "constructor")
182    {
183      constructor_params = &(*child_node);
184      ++child_node;
185      p_name = child_node->Name();
186    }
187    if (p_name == "parameters")
188    {
189      parameters = &(*child_node);
190      ++child_node;
191    }
192
193    // create mode
194    tFrameworkElement* created = nullptr;
195    tConstructorParameters* spl = nullptr;
196    if (constructor_params != nullptr)
197    {
198      spl = action.GetParameterTypes()->Instantiate();
199      spl->Deserialize(*constructor_params, true);
200    }
201    created = action.CreateModule(parent, name, spl);
202    SetFinstructed(*created, action, spl);
203    if (parameters)
204    {
205      created->GetAnnotation<parameters::internal::tStaticParameterList>()->Deserialize(*parameters, true);
206    }
207    created->Init();
208
209    // continue with children
210    for (; child_node != node.ChildrenEnd(); ++child_node)
211    {
212      std::string name2 = child_node->Name();
213      if (name2 == "element")
214      {
215        Instantiate(*child_node, created);
216      }
217      else
218      {
219        FINROC_LOG_PRINT(WARNING, "Unknown XML tag: ", name2);
220      }
221    }
222  }
223  catch (const rrlib::xml::tException& e)
224  {
225    FINROC_LOG_PRINT(ERROR, "Failed to instantiate component '", name, "'. XML Exception: ", e.what(), ". Skipping.");
226  }
227  catch (const std::exception& e)
228  {
229    FINROC_LOG_PRINT(ERROR, "Failed to instantiate component '", name, "'. ", e.what(), ". Skipping.");
230  }
231}
232
233bool tFinstructable::IsResponsibleForConfigFileConnections(tFrameworkElement& ap) const
234{
235  return parameters::internal::tParameterInfo::IsFinstructableGroupResponsibleForConfigFileConnections(*GetFrameworkElement(), ap);
236}
237
238void tFinstructable::LoadParameter(const rrlib::xml::tNode& node, core::tAbstractPort& parameter_port)
239{
240  parameters::internal::tParameterInfo* pi = parameter_port.GetAnnotation<parameters::internal::tParameterInfo>();
241  bool outermost_group = GetFrameworkElement()->GetParent() == &(core::tRuntimeEnvironment::GetInstance());
242  if (!pi)
243  {
244    FINROC_LOG_PRINT(WARNING, "Port is not a parameter: '", parameter_port, "'. Parameter entry is not loaded.");
245  }
246  else
247  {
248    if (outermost_group && node.HasAttribute("cmdline") && (!IsResponsibleForConfigFileConnections(parameter_port)))
249    {
250      pi->SetCommandLineOption(node.GetStringAttribute("cmdline"));
251    }
252    else
253    {
254      pi->Deserialize(node, true, outermost_group);
255    }
256    try
257    {
258      pi->LoadValue();
259    }
260    catch (const std::exception& e)
261    {
262      FINROC_LOG_PRINT(WARNING, "Unable to load parameter value for '", parameter_port, "'. ", e);
263    }
264  }
265}
266
267void tFinstructable::LoadXml()
268{
269  {
270    rrlib::thread::tLock lock2(core::tRuntimeEnvironment::GetInstance().GetStructureMutex());
271    try
272    {
273      FINROC_LOG_PRINT(DEBUG, "Loading XML: ", GetXmlFileString());
274      rrlib::xml::tDocument doc(core::GetFinrocXMLDocument(GetXmlFileString(), false));
275      rrlib::xml::tNode& root = doc.RootNode();
276      core::tPath path_to_this = GetFrameworkElement()->GetPath();
277      if (main_name.length() == 0 && root.HasAttribute("defaultname"))
278      {
279        main_name = root.GetStringAttribute("defaultname");
280      }
281
282      // load dependencies
283      if (root.HasAttribute("dependencies"))
284      {
285        std::vector<std::string> deps;
286        std::stringstream stream(root.GetStringAttribute("dependencies"));
287        std::string dependency;
288        while (std::getline(stream, dependency, ','))
289        {
290          rrlib::util::TrimWhitespace(dependency);
291          tSharedLibrary dep(dependency);
292          std::vector<tSharedLibrary> loadable = GetLoadableFinrocLibraries();
293          bool loaded = false;
294          for (size_t i = 0; i < loadable.size(); i++)
295          {
296            if (loadable[i] == dep)
297            {
298              try
299              {
300                DLOpen(dep);
301                loaded = true;
302                break;
303              }
304              catch (const std::exception& exception)
305              {
306                FINROC_LOG_PRINT(ERROR, exception);
307              }
308            }
309          }
310          if (!loaded)
311          {
312            std::set<tSharedLibrary> loaded_libs = GetLoadedFinrocLibraries();
313            if (loaded_libs.find(dep) == loaded_libs.end())
314            {
315              FINROC_LOG_PRINT(WARNING, "Dependency ", dep.ToString(true), " is not available.");
316            }
317          }
318        }
319      }
320
321      // Load components (before interface in order to reduce issues with missing/unregistered data types)
322      for (rrlib::xml::tNode::const_iterator node = root.ChildrenBegin(); node != root.ChildrenEnd(); ++node)
323      {
324        std::string name = node->Name();
325        if (name == "element")
326        {
327          Instantiate(*node, GetFrameworkElement());
328        }
329      }
330
331      // Load all remaining XML elements
332      for (rrlib::xml::tNode::const_iterator node = root.ChildrenBegin(); node != root.ChildrenEnd(); ++node)
333      {
334        std::string name = node->Name();
335        if (name == "interface")
336        {
337          tEditableInterfaces* editable_interfaces = GetFrameworkElement()->GetAnnotation<tEditableInterfaces>();
338          if (editable_interfaces)
339          {
340            try
341            {
342              editable_interfaces->LoadInterfacePorts(*node);
343            }
344            catch (const std::exception& e)
345            {
346              FINROC_LOG_PRINT(WARNING, "Loading interface ports failed. Reason: ", e);
347            }
348          }
349          else
350          {
351            FINROC_LOG_PRINT(WARNING, "Cannot load interface, because finstructable group does not have any editable interfaces.");
352          }
353        }
354        else if (name == "element")
355        {
356          // already instantiated
357        }
358        else if (name == "edge")
359        {
360          core::tURI source_uri(node->GetStringAttribute("src"));
361          core::tURI destination_uri(node->GetStringAttribute("dest"));
362          try
363          {
364            rrlib::uri::tURIElements source_uri_parsed;
365            rrlib::uri::tURIElements destination_uri_parsed;
366            source_uri.Parse(source_uri_parsed);
367            destination_uri.Parse(destination_uri_parsed);
368            core::tUriConnectOptions connect_options(core::tConnectionFlag::FINSTRUCTED);
369            if (node->HasAttribute("flags"))
370            {
371              connect_options.flags |= rrlib::serialization::Deserialize<core::tConnectionFlags>(node->GetStringAttribute("flags"));
372            }
373
374            for (rrlib::xml::tNode::const_iterator conversion_node = node->ChildrenBegin(); conversion_node != node->ChildrenEnd(); ++conversion_node)
375            {
376              if (conversion_node->Name() == "conversion")
377              {
378                rrlib::rtti::tType intermediate_type;
379                if (conversion_node->HasAttribute("intermediate_type"))
380                {
381                  intermediate_type = rrlib::rtti::tType::FindType(conversion_node->GetStringAttribute("intermediate_type"));
382                }
383                if (conversion_node->HasAttribute("operation2"))
384                {
385                  // Two operations
386                  std::string operation1 = conversion_node->GetStringAttribute("operation1");
387                  std::string operation2 = conversion_node->GetStringAttribute("operation2");
388                  connect_options.conversion_operations = rrlib::rtti::conversion::tConversionOperationSequence(operation1, operation2, intermediate_type);
389                  if (conversion_node->HasAttribute("parameter1"))
390                  {
391                    connect_options.conversion_operations.SetParameterValue(0, conversion_node->GetStringAttribute("parameter1"));
392                  }
393                  if (conversion_node->HasAttribute("parameter2"))
394                  {
395                    connect_options.conversion_operations.SetParameterValue(1, conversion_node->GetStringAttribute("parameter2"));
396                  }
397                }
398                else if (conversion_node->HasAttribute("operation"))
399                {
400                  // One operation
401                  std::string operation1 = conversion_node->GetStringAttribute("operation");
402                  connect_options.conversion_operations = rrlib::rtti::conversion::tConversionOperationSequence(operation1, "", intermediate_type);
403                  if (conversion_node->HasAttribute("parameter"))
404                  {
405                    connect_options.conversion_operations.SetParameterValue(0, conversion_node->GetStringAttribute("parameter"));
406                  }
407                }
408              }
409            }
410
411            if (source_uri_parsed.scheme.length() == 0 && destination_uri_parsed.scheme.length() == 0)
412            {
413              core::tAbstractPort* source_port = GetChildPort(source_uri_parsed.path);
414              core::tAbstractPort* destination_port = GetChildPort(destination_uri_parsed.path);
415
416              if (source_port == nullptr && destination_port == nullptr)
417              {
418                FINROC_LOG_PRINT(WARNING, "Cannot create connector because neither port is available: ", source_uri_parsed.path, ", ", destination_uri_parsed.path);
419              }
420              else if (source_port == nullptr || source_port->GetFlag(tFlag::VOLATILE))
421              {
422                destination_port->ConnectTo(path_to_this.Append(source_uri_parsed.path), connect_options);
423              }
424              else if (destination_port == nullptr || destination_port->GetFlag(tFlag::VOLATILE))
425              {
426                source_port->ConnectTo(path_to_this.Append(destination_uri_parsed.path), connect_options);
427              }
428              else
429              {
430                source_port->ConnectTo(*destination_port, connect_options);
431              }
432            }
433            else
434            {
435              // Create URI connector
436              if (source_uri_parsed.scheme.length() && destination_uri_parsed.scheme.length())
437              {
438                throw std::runtime_error("Only one port may have an address with an URI scheme");
439              }
440              core::tAbstractPort* source_port = GetChildPort(source_uri_parsed.scheme.length() == 0 ? source_uri_parsed.path : destination_uri_parsed.path);
441              if (!source_port)
442              {
443                FINROC_LOG_PRINT(WARNING, "Cannot create connector because port is not available: ", source_uri_parsed.scheme.length() == 0 ? source_uri_parsed.path : destination_uri_parsed.path);
444              }
445              const core::tURI& scheme_uri = source_uri_parsed.scheme.length() != 0 ? source_uri : destination_uri;
446
447              // read parameters
448              for (rrlib::xml::tNode::const_iterator parameter_node = node->ChildrenBegin(); parameter_node != node->ChildrenEnd(); ++parameter_node)
449              {
450                if (parameter_node->Name() == "parameter")
451                {
452                  connect_options.parameters.emplace(parameter_node->GetStringAttribute("name"), parameter_node->GetTextContent());
453                }
454              }
455
456              core::tUriConnector::Create(*source_port, scheme_uri, connect_options);
457            }
458          }
459          catch (const std::exception& e)
460          {
461            FINROC_LOG_PRINT(WARNING, "Creating connector from ", source_uri.ToString(), " to ", destination_uri.ToString(), " failed. Reason: ", e.what());
462          }
463        }
464        else if (name == "parameter") // legacy parameter info support
465        {
466          core::tURI parameter_uri(node->GetStringAttribute("link"));
467          rrlib::uri::tURIElements parameter_uri_parsed;
468          parameter_uri.Parse(parameter_uri_parsed);
469          core::tAbstractPort* parameter = GetChildPort(parameter_uri_parsed.path);
470          if (!parameter)
471          {
472            FINROC_LOG_PRINT(WARNING, "Cannot set config entry, because parameter is not available: ", parameter_uri.ToString());
473          }
474          else
475          {
476            LoadParameter(*node, *parameter);
477          }
478        }
479        else if (name == "parameter_links")
480        {
481          ProcessParameterLinksNode(*node, *GetFrameworkElement());
482        }
483        else
484        {
485          FINROC_LOG_PRINT(WARNING, "Unknown XML tag: ", name);
486        }
487      }
488      FINROC_LOG_PRINT(DEBUG, "Loading XML successful");
489    }
490    catch (const std::exception& e)
491    {
492      FINROC_LOG_PRINT(WARNING, "Loading XML '", GetXmlFileString(), "' failed: ", e);
493    }
494  }
495}
496
497void tFinstructable::OnInitialization()
498{
499  if (!GetFrameworkElement()->GetFlag(tFlag::FINSTRUCTABLE_GROUP))
500  {
501    throw std::logic_error("Any class using tFinstructable must set tFlag::FINSTRUCTABLE_GROUP in constructor");
502  }
503}
504
505void tFinstructable::ProcessParameterLinksNode(const rrlib::xml::tNode& node, core::tFrameworkElement& element)
506{
507  for (auto it = node.ChildrenBegin(); it != node.ChildrenEnd(); ++it)
508  {
509    if (it->Name() == "element")
510    {
511      std::string name = it->GetStringAttribute("name");
512      core::tFrameworkElement* corresponding_element = element.GetChild(name);
513      if (corresponding_element)
514      {
515        ProcessParameterLinksNode(*it, *corresponding_element);
516      }
517      else
518      {
519        FINROC_LOG_PRINT(WARNING, "Cannot find '", element, "/", name, "'. Parameter entries below are not loaded.");
520      }
521    }
522    else if (it->Name() == "parameter")
523    {
524      std::string name = it->GetStringAttribute("name");
525      core::tFrameworkElement* parameter_element = element.GetChild(name);
526      if (!parameter_element)
527      {
528        core::tFrameworkElement* parameters_interface = element.GetChild("Parameters");
529        parameter_element = parameters_interface ? parameters_interface->GetChild(name) : nullptr;
530      }
531      if (parameter_element && parameter_element->IsPort())
532      {
533        LoadParameter(*it, static_cast<core::tAbstractPort&>(*parameter_element));
534      }
535      else
536      {
537        FINROC_LOG_PRINT(WARNING, "Cannot find parameter '", element, "/", name, "'. Parameter entry is not loaded.");
538      }
539    }
540  }
541}
542
543bool tFinstructable::SaveParameterConfigEntries(rrlib::xml::tNode& node, core::tFrameworkElement& element)
544{
545  // Get alphabetically sorted list of children
546  std::vector<core::tFrameworkElement*> child_elements;
547  std::vector<core::tAbstractPort*> parameter_ports;
548  for (auto it = element.ChildrenBegin(); it != element.ChildrenEnd(); ++it)
549  {
550    if (it->IsReady())
551    {
552      if (it->GetFlag(tFlag::INTERFACE) && it->GetName() == "Parameters")
553      {
554        for (auto parameter_it = it->ChildrenBegin(); parameter_it != it->ChildrenEnd(); ++parameter_it)
555        {
556          if (parameter_it->IsReady() && parameter_it->IsPort() && parameter_it->GetAnnotation<parameters::internal::tParameterInfo>())
557          {
558            parameter_ports.push_back(&static_cast<core::tAbstractPort&>(*parameter_it));
559          }
560        }
561      }
562      else if (it->IsPort() && it->GetAnnotation<parameters::internal::tParameterInfo>())
563      {
564        parameter_ports.push_back(&static_cast<core::tAbstractPort&>(*it));
565      }
566      else
567      {
568        child_elements.push_back(&(*it));
569      }
570    }
571  }
572
573  struct
574  {
575    bool operator()(core::tFrameworkElement* a, core::tFrameworkElement* b)
576    {
577      return a->GetName() < b->GetName();
578    }
579  } comparator;
580  std::sort(child_elements.begin(), child_elements.end(), comparator);
581  std::sort(parameter_ports.begin(), parameter_ports.end(), comparator);
582
583  // Save parameters first
584  bool result = false;
585  for (core::tAbstractPort * port : parameter_ports)
586  {
587    bool outermost_group = GetFrameworkElement()->GetParent() == &(core::tRuntimeEnvironment::GetInstance());
588    parameters::internal::tParameterInfo* info = port->GetAnnotation<parameters::internal::tParameterInfo>();
589    bool is_responsible_for_parameter_links = IsResponsibleForConfigFileConnections(*port);
590
591    if (info && info->HasNonDefaultFinstructInfo() && (is_responsible_for_parameter_links || (outermost_group && info->GetCommandLineOption().length())))
592    {
593      // Save Parameter
594      rrlib::xml::tNode& parameter_node = node.AddChildNode("parameter");
595      parameter_node.SetAttribute("name", port->GetName());
596
597      if (!is_responsible_for_parameter_links)
598      {
599        parameter_node.SetAttribute("cmdline", info->GetCommandLineOption());
600      }
601      else
602      {
603        info->Serialize(parameter_node, true, outermost_group);
604      }
605      result = true;
606    }
607  }
608
609  // Process child elements
610  for (core::tFrameworkElement * child : child_elements)
611  {
612    rrlib::xml::tNode& child_node = node.AddChildNode("element");
613    child_node.SetAttribute("name", child->GetName());
614    bool sub_result = SaveParameterConfigEntries(child_node, *child);
615    result |= sub_result;
616    if (!sub_result)
617    {
618      node.RemoveChildNode(child_node);
619    }
620  }
621  return result;
622
623  /*for (auto it = GetFrameworkElement()->SubElementsBegin(); it != GetFrameworkElement()->SubElementsEnd(); ++it)
624  {
625    if ((!it->IsPort()) || (!it->IsReady()))
626    {
627      continue;
628    }
629
630    core::tAbstractPort& port = static_cast<core::tAbstractPort&>(*it);
631    bool outermost_group = GetFrameworkElement()->GetParent() == &(core::tRuntimeEnvironment::GetInstance());
632    parameters::internal::tParameterInfo* info = port.GetAnnotation<parameters::internal::tParameterInfo>();
633
634    if (info && info->HasNonDefaultFinstructInfo() && (IsResponsibleForConfigFileConnections(port) || (outermost_group && info->GetCommandLineOption().length())))
635    {
636      bool parameter_interface = port.GetParent()->GetFlag(tFlag::INTERFACE) || port.GetParent()->GetName() == "Parameters"; // TODO: remove the latter as soon as
637      core::tFrameworkElement* target_hierarchy_element = (parameter_interface && port.GetParent()->GetParent()) ? port.GetParent()->GetParent() : port.GetParent();
638
639      // Possibly move up hierarchy in XML
640      while (!(target_hierarchy_element == current_hierarchy_element || target_hierarchy_element->IsChildOf(*current_hierarchy_element)))
641      {
642        current_hierarchy_element = current_hierarchy_element->GetParent();
643        current_parameter_links_node = &current_parameter_links_node->Parent();
644      }
645      // Possibly create hierarchy in XML
646      std::vector<core::tFrameworkElement*> elements_to_add;
647      core::tFrameworkElement* element_to_add = target_hierarchy_element;
648      while (element_to_add != current_hierarchy_element)
649      {
650        elements_to_add.push_back(element_to_add);
651        element_to_add = element_to_add->GetParent();
652      }
653      for (auto it = elements_to_add.rbegin(); it != elements_to_add.rend(); ++it)
654      {
655        current_parameter_links_node = &current_parameter_links_node->AddChildNode("element");
656        current_parameter_links_node->SetAttribute("name", (*it)->GetName());
657      }
658      current_hierarchy_element = target_hierarchy_element;
659
660      // Save Parameter
661      rrlib::xml::tNode& parameter_node = current_parameter_links_node->AddChildNode("parameter");
662      parameter_node.SetAttribute("name", port.GetName());
663
664      if (!IsResponsibleForConfigFileConnections(port))
665      {
666        parameter_node.SetAttribute("cmdline", info->GetCommandLineOption());
667      }
668      else
669      {
670        info->Serialize(parameter_node, true, outermost_group);
671      }
672    }
673  }
674  */
675}
676
677void tFinstructable::SaveXml()
678{
679  {
680    rrlib::thread::tLock lock2(core::tRuntimeEnvironment::GetInstance().GetStructureMutex());
681    saving_thread = &rrlib::thread::tThread::CurrentThread();
682    dependencies_tmp.clear();
683    std::string save_to = core::GetFinrocFileToSaveTo(GetXmlFileString());
684    if (save_to.length() == 0)
685    {
686      std::string save_to_alt = GetXmlFileString();
687      std::replace(save_to_alt.begin(), save_to_alt.end(), '/', '_');
688      FINROC_LOG_PRINT(USER, "There does not seem to be any suitable location for: '", GetXmlFileString(), "' . For now, using '", save_to_alt, "'.");
689      save_to = save_to_alt;
690    }
691    FINROC_LOG_PRINT(USER, "Saving XML: ", save_to);
692    rrlib::xml::tDocument doc;
693    try
694    {
695      rrlib::xml::tNode& root = doc.AddRootNode("Finstructable"); // TODO: find good name ("finroc_structure")
696
697      // serialize default main name
698      if (main_name.length() > 0)
699      {
700        root.SetAttribute("defaultname", main_name);
701      }
702
703      // serialize any editable interfaces
704      tEditableInterfaces* editable_interfaces = GetFrameworkElement()->GetAnnotation<tEditableInterfaces>();
705      if (editable_interfaces)
706      {
707        editable_interfaces->SaveAllNonEmptyInterfaces(root);
708      }
709
710      // serialize framework elements
711      SerializeChildren(root, *GetFrameworkElement());
712
713      // serialize connectors (sorted by port URIs (we do not want changes in finstruct files depending on whether or not an optional/volatile connector exists))
714      rrlib::uri::tPath this_path = GetFrameworkElement()->GetPath();
715      std::map<std::pair<rrlib::uri::tURI, rrlib::uri::tURI>, std::pair<core::tConnector*, core::tUriConnector*>> connector_map;
716      bool this_is_outermost_composite_component = GetFrameworkElement()->GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP) == nullptr;
717
718      // Get (primary) connectors
719      for (auto it = GetFrameworkElement()->SubElementsBegin(); it != GetFrameworkElement()->SubElementsEnd(); ++it)
720      {
721        if ((!it->IsPort()) || (!it->IsReady()))
722        {
723          continue;
724        }
725        core::tAbstractPort& port = static_cast<core::tAbstractPort&>(*it);
726        tFrameworkElement* port_parent_group = port.GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP);
727
728        // Plain connectors
729        for (auto it = port.OutgoingConnectionsBegin(); it != port.OutgoingConnectionsEnd(); ++it) // only outgoing edges => we don't get any edges twice
730        {
731          // Checks whether to save connector
732          // only save primary finstructed edges
733          if ((!it->Flags().Get(core::tConnectionFlag::FINSTRUCTED)) || it->Flags().Get(core::tConnectionFlag::NON_PRIMARY_CONNECTOR))
734          {
735            continue;
736          }
737
738          // 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
739          tFrameworkElement* common_parent = port_parent_group;
740          while (common_parent && (!it->Destination().IsChildOf(*common_parent)))
741          {
742            common_parent = common_parent->GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP);
743          }
744          if (common_parent != GetFrameworkElement() && (!(this_is_outermost_composite_component && common_parent == nullptr)))
745          {
746            // save connector in another group
747            continue;
748          }
749
750          std::pair<rrlib::uri::tURI, rrlib::uri::tURI> key(GetConnectorPath(port.GetPath(), this_path), GetConnectorPath(it->Destination().GetPath(), this_path));
751          std::pair<core::tConnector*, core::tUriConnector*> value(&(*it), nullptr);
752          connector_map.emplace(key, value);
753        }
754
755        // Get URI connectors
756        for (auto & connector : port.UriConnectors())
757        {
758          // Checks whether to save connector
759          // only save primary finstructed edges
760          if ((!connector) || (!connector->Flags().Get(core::tConnectionFlag::FINSTRUCTED)) || connector->Flags().Get(core::tConnectionFlag::NON_PRIMARY_CONNECTOR))
761          {
762            continue;
763          }
764
765          std::pair<rrlib::uri::tURI, rrlib::uri::tURI> key(GetConnectorPath(port.GetPath(), this_path), connector->Uri());
766
767          // 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
768          if (typeid(*connector) == typeid(core::internal::tLocalUriConnector))
769          {
770            auto& local_connector = static_cast<core::internal::tLocalUriConnector&>(*connector);
771            bool source_uri = local_connector.GetPortReferences()[0].path.Size();
772            rrlib::uri::tPath path = local_connector.GetPortReferences()[source_uri ? 0 : 1].path;
773            tFrameworkElement* common_parent = port_parent_group;
774            rrlib::uri::tPath parent_group_path = common_parent->GetPath();
775            while (common_parent && path.CountCommonElements(parent_group_path) != parent_group_path.Size())
776            {
777              common_parent = common_parent->GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP);
778              parent_group_path = common_parent ? common_parent->GetPath() : parent_group_path;
779            }
780            if (common_parent != GetFrameworkElement() && (!(this_is_outermost_composite_component && common_parent == nullptr)))
781            {
782              // save connector in another group
783              continue;
784            }
785
786            rrlib::uri::tURI port_uri = key.first;
787            key.first = source_uri ? rrlib::uri::tURI(path) : port_uri;
788            key.second = source_uri ? port_uri : rrlib::uri::tURI(path);
789          }
790
791          std::pair<core::tConnector*, core::tUriConnector*> value(nullptr, connector.get());
792          connector_map.emplace(key, value);
793        }
794      }
795
796      for (auto & entry : connector_map)
797      {
798        rrlib::xml::tNode& edge = root.AddChildNode("edge"); // TODO: "connector"?
799        edge.SetAttribute("src", entry.first.first.ToString());
800        edge.SetAttribute("dest", entry.first.second.ToString());
801
802        // Save flags
803        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;
804        core::tConnectionFlags flags_to_save((entry.second.first ? entry.second.first->Flags() : entry.second.second->Flags()).Raw() & cFLAGS_TO_SAVE.Raw());
805        if (flags_to_save.Raw())
806        {
807          edge.SetAttribute("flags", rrlib::serialization::Serialize(flags_to_save));
808        }
809
810        // Save conversion operation
811        const rrlib::rtti::conversion::tConversionOperationSequence& conversion_operations = entry.second.first ? entry.second.first->ConversionOperations() : entry.second.second->ConversionOperations();
812        if (conversion_operations.Size())
813        {
814          rrlib::xml::tNode& conversion = root.AddChildNode("conversion");
815          conversion.SetAttribute(conversion_operations.Size() == 2 ? "operation1" : "operation", conversion_operations[0].first);
816          if (conversion_operations.GetParameterValue(0))
817          {
818            conversion.SetAttribute(conversion_operations.Size() == 2 ? "parameter1" : "parameter", conversion_operations.GetParameterValue(0).ToString());
819          }
820          if (conversion_operations.Size() == 2)
821          {
822            conversion.SetAttribute("operation2", conversion_operations[1].first);
823            if (conversion_operations.GetParameterValue(0))
824            {
825              conversion.SetAttribute("parameter2", conversion_operations.GetParameterValue(1).ToString());
826            }
827          }
828          if (conversion_operations.IntermediateType())
829          {
830            conversion.SetAttribute("intermediate_type", conversion_operations.IntermediateType().GetName());
831          }
832        }
833
834        // Save parameters of URI connector
835        if (entry.second.second)
836        {
837          core::tUriConnector::tConstParameterDefinitionRange definition_range = entry.second.second->GetParameterDefinitions();
838          core::tUriConnector::tParameterValueRange value_range = entry.second.second->GetParameterValues();
839          assert(definition_range.End() - definition_range.Begin() == value_range.End() - value_range.Begin());
840
841          auto definition = definition_range.Begin();
842          auto value = value_range.Begin();
843          for (; definition < definition_range.End(); ++definition, ++value)
844          {
845            if (!value->Equals(definition->GetDefaultValue()))
846            {
847              rrlib::xml::tNode& parameter_node = root.AddChildNode("parameter");
848              parameter_node.SetAttribute("name", definition->GetName());
849              value->Serialize(parameter_node);
850            }
851          }
852        }
853      }
854
855      // Save parameter config entries
856      SaveParameterConfigEntries(root.AddChildNode("parameter_links"), *GetFrameworkElement());
857
858      // add dependencies
859      if (dependencies_tmp.size() > 0)
860      {
861        std::stringstream s;
862        for (auto it = dependencies_tmp.begin(); it != dependencies_tmp.end(); ++it)
863        {
864          if (it != dependencies_tmp.begin())
865          {
866            s << ", ";
867          }
868          s << it->ToString();
869        }
870
871        root.SetAttribute("dependencies", s.str());
872        dependencies_tmp.clear();
873      }
874
875      doc.WriteToFile(save_to);
876      FINROC_LOG_PRINT(USER, "Saving successful.");
877
878    }
879    catch (const rrlib::xml::tException& e)
880    {
881      const char* msg = e.what();
882      FINROC_LOG_PRINT(USER, "Saving failed: ", msg);
883      throw std::runtime_error(msg);
884    }
885  }
886  saving_thread = nullptr;
887}
888
889std::vector<std::string> tFinstructable::ScanForCommandLineArgs(const std::string& finroc_file)
890{
891  std::vector<std::string> result;
892  try
893  {
894    rrlib::xml::tDocument doc(core::GetFinrocXMLDocument(finroc_file, false));
895    try
896    {
897      FINROC_LOG_PRINT_STATIC(DEBUG, "Scanning for command line options in ", finroc_file);
898      rrlib::xml::tNode& root = doc.RootNode();
899      ScanForCommandLineArgsHelper(result, root);
900      FINROC_LOG_PRINTF_STATIC(DEBUG, "Scanning successful. Found %zu additional options.", result.size());
901    }
902    catch (std::exception& e)
903    {
904      FINROC_LOG_PRINT_STATIC(WARNING, "FinstructableGroup", "Scanning failed: ", finroc_file, e);
905    }
906  }
907  catch (std::exception& e)
908  {}
909  return result;
910}
911
912void tFinstructable::ScanForCommandLineArgsHelper(std::vector<std::string>& result, const rrlib::xml::tNode& parent)
913{
914  for (rrlib::xml::tNode::const_iterator node = parent.ChildrenBegin(); node != parent.ChildrenEnd(); ++node)
915  {
916    std::string name(node->Name());
917    if (node->HasAttribute("cmdline") && (name == "staticparameter" || name == "parameter"))
918    {
919      result.push_back(node->GetStringAttribute("cmdline"));
920    }
921    ScanForCommandLineArgsHelper(result, *node);
922  }
923}
924
925void tFinstructable::SerializeChildren(rrlib::xml::tNode& node, tFrameworkElement& current)
926{
927  for (auto child = current.ChildrenBegin(); child != current.ChildrenEnd(); ++child)
928  {
929    parameters::internal::tStaticParameterList* spl = child->GetAnnotation<parameters::internal::tStaticParameterList>();
930    tConstructorParameters* cps = child->GetAnnotation<tConstructorParameters>();
931    if (child->IsReady() && child->GetFlag(tFlag::FINSTRUCTED))
932    {
933      // serialize framework element
934      rrlib::xml::tNode& n = node.AddChildNode("element");
935      n.SetAttribute("name", child->GetName());
936      tCreateFrameworkElementAction* cma = tCreateFrameworkElementAction::GetConstructibleElements()[spl->GetCreateAction()];
937      n.SetAttribute("group", cma->GetModuleGroup().ToString());
938      //if (boost::ends_with(cma->GetModuleGroup(), ".so"))
939      //{
940      AddDependency(cma->GetModuleGroup());
941      //}
942      n.SetAttribute("type", cma->GetName());
943      if (cps != nullptr)
944      {
945        rrlib::xml::tNode& pn = n.AddChildNode("constructor");
946        cps->Serialize(pn, true);
947      }
948      if (spl != nullptr)
949      {
950        rrlib::xml::tNode& pn = n.AddChildNode("parameters");
951        spl->Serialize(pn, true);
952      }
953
954      // serialize its children
955      if (!child->GetFlag(tFlag::FINSTRUCTABLE_GROUP))
956      {
957        SerializeChildren(n, *child);
958      }
959    }
960  }
961}
962
963void tFinstructable::SetFinstructed(tFrameworkElement& fe, tCreateFrameworkElementAction& create_action, tConstructorParameters* params)
964{
965  assert(!fe.GetFlag(tFlag::FINSTRUCTED) && (!fe.IsReady()));
966  parameters::internal::tStaticParameterList& list = parameters::internal::tStaticParameterList::GetOrCreate(fe);
967  auto& element_list = tCreateFrameworkElementAction::GetConstructibleElements();
968  for (size_t i = 0, n = element_list.Size(); i < n; ++i)
969  {
970    if (element_list[i] == &create_action)
971    {
972      list.SetCreateAction(i);
973      break;
974    }
975  }
976  fe.SetFlag(tFlag::FINSTRUCTED);
977  if (params)
978  {
979    fe.AddAnnotation<tConstructorParameters>(*params);
980  }
981}
982
983void tFinstructable::StaticInit()
984{
985  startup_loaded_finroc_libs = GetLoadedFinrocLibraries();
986}
987
988//----------------------------------------------------------------------
989// End of namespace declaration
990//----------------------------------------------------------------------
991}
992}
Note: See TracBrowser for help on using the repository browser.