source: finroc_plugins_runtime_construction/tFinstructable.cpp @ 90:88b6f38d610a

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

Adds support for type conversion and other connector options in finstructable groups and administration service. CreateFrameworkElementAction register is now based on register mechanism from rrlib_serialization.

File size: 38.4 KB
Line 
1//
2// You received this file as part of Finroc
3// A framework for intelligent robot control
4//
5// Copyright (C) Finroc GbR (finroc.org)
6//
7// This program is free software; you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation; either version 2 of the License, or
10// (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License along
18// with this program; if not, write to the Free Software Foundation, Inc.,
19// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20//
21//----------------------------------------------------------------------
22/*!\file    plugins/runtime_construction/tFinstructable.cpp
23 *
24 * \author  Max Reichardt
25 *
26 * \date    2012-12-02
27 *
28 */
29//----------------------------------------------------------------------
30#include "plugins/runtime_construction/tFinstructable.h"
31
32//----------------------------------------------------------------------
33// External includes (system with <>, local with "")
34//----------------------------------------------------------------------
35#include <set>
36#include "rrlib/util/string.h"
37#include "core/file_lookup.h"
38#include "core/tRuntimeEnvironment.h"
39#include "plugins/parameters/internal/tParameterInfo.h"
40
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))    // source volatile
421              {
422                connect_options.flags |= core::tConnectionFlag::RECONNECT;
423                destination_port->ConnectTo(path_to_this.Append(source_uri_parsed.path), connect_options);
424              }
425              else if (destination_port == nullptr || destination_port->GetFlag(tFlag::VOLATILE))    // destination volatile
426              {
427                connect_options.flags |= core::tConnectionFlag::RECONNECT;
428                source_port->ConnectTo(path_to_this.Append(destination_uri_parsed.path), connect_options);
429              }
430              else
431              {
432                source_port->ConnectTo(*destination_port, connect_options);
433              }
434            }
435            else
436            {
437              // Create URI connector
438              if (source_uri_parsed.scheme.length() && destination_uri_parsed.scheme.length())
439              {
440                throw std::runtime_error("Only one port may have an address with an URI scheme");
441              }
442              core::tAbstractPort* source_port = GetChildPort(source_uri_parsed.scheme.length() == 0 ? source_uri_parsed.path : destination_uri_parsed.path);
443              if (!source_port)
444              {
445                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);
446              }
447              const core::tURI& scheme_uri = source_uri_parsed.scheme.length() != 0 ? source_uri : destination_uri;
448
449              // read parameters
450              for (rrlib::xml::tNode::const_iterator parameter_node = node->ChildrenBegin(); parameter_node != node->ChildrenEnd(); ++parameter_node)
451              {
452                if (parameter_node->Name() == "parameter")
453                {
454                  connect_options.parameters.emplace(parameter_node->GetStringAttribute("name"), parameter_node->GetTextContent());
455                }
456              }
457
458              core::tUriConnector::Create(*source_port, scheme_uri, connect_options);
459            }
460          }
461          catch (const std::exception& e)
462          {
463            FINROC_LOG_PRINT(WARNING, "Creating connector from ", source_uri.ToString(), " to ", destination_uri.ToString(), " failed. Reason: ", e.what());
464          }
465        }
466        else if (name == "parameter") // legacy parameter info support
467        {
468          core::tURI parameter_uri(node->GetStringAttribute("link"));
469          rrlib::uri::tURIElements parameter_uri_parsed;
470          parameter_uri.Parse(parameter_uri_parsed);
471          core::tAbstractPort* parameter = GetChildPort(parameter_uri_parsed.path);
472          if (!parameter)
473          {
474            FINROC_LOG_PRINT(WARNING, "Cannot set config entry, because parameter is not available: ", parameter_uri.ToString());
475          }
476          else
477          {
478            LoadParameter(*node, *parameter);
479          }
480        }
481        else if (name == "parameter_links")
482        {
483          ProcessParameterLinksNode(*node, *GetFrameworkElement());
484        }
485        else
486        {
487          FINROC_LOG_PRINT(WARNING, "Unknown XML tag: ", name);
488        }
489      }
490      FINROC_LOG_PRINT(DEBUG, "Loading XML successful");
491    }
492    catch (const std::exception& e)
493    {
494      FINROC_LOG_PRINT(WARNING, "Loading XML '", GetXmlFileString(), "' failed: ", e);
495    }
496  }
497}
498
499void tFinstructable::OnInitialization()
500{
501  if (!GetFrameworkElement()->GetFlag(tFlag::FINSTRUCTABLE_GROUP))
502  {
503    throw std::logic_error("Any class using tFinstructable must set tFlag::FINSTRUCTABLE_GROUP in constructor");
504  }
505}
506
507void tFinstructable::ProcessParameterLinksNode(const rrlib::xml::tNode& node, core::tFrameworkElement& element)
508{
509  for (auto it = node.ChildrenBegin(); it != node.ChildrenEnd(); ++it)
510  {
511    if (it->Name() == "element")
512    {
513      std::string name = it->GetStringAttribute("name");
514      core::tFrameworkElement* corresponding_element = element.GetChild(name);
515      if (corresponding_element)
516      {
517        ProcessParameterLinksNode(*it, *corresponding_element);
518      }
519      else
520      {
521        FINROC_LOG_PRINT(WARNING, "Cannot find '", element, "/", name, "'. Parameter entries below are not loaded.");
522      }
523    }
524    else if (it->Name() == "parameter")
525    {
526      std::string name = it->GetStringAttribute("name");
527      core::tFrameworkElement* parameter_element = element.GetChild(name);
528      if (!parameter_element)
529      {
530        core::tFrameworkElement* parameters_interface = element.GetChild("Parameters");
531        parameter_element = parameters_interface ? parameters_interface->GetChild(name) : nullptr;
532      }
533      if (parameter_element && parameter_element->IsPort())
534      {
535        LoadParameter(*it, static_cast<core::tAbstractPort&>(*parameter_element));
536      }
537      else
538      {
539        FINROC_LOG_PRINT(WARNING, "Cannot find parameter '", element, "/", name, "'. Parameter entry is not loaded.");
540      }
541    }
542  }
543}
544
545bool tFinstructable::SaveParameterConfigEntries(rrlib::xml::tNode& node, core::tFrameworkElement& element)
546{
547  // Get alphabetically sorted list of children
548  std::vector<core::tFrameworkElement*> child_elements;
549  std::vector<core::tAbstractPort*> parameter_ports;
550  for (auto it = element.ChildrenBegin(); it != element.ChildrenEnd(); ++it)
551  {
552    if (it->IsReady())
553    {
554      if (it->GetFlag(tFlag::INTERFACE) && it->GetName() == "Parameters")
555      {
556        for (auto parameter_it = it->ChildrenBegin(); parameter_it != it->ChildrenEnd(); ++parameter_it)
557        {
558          if (parameter_it->IsReady() && parameter_it->IsPort() && parameter_it->GetAnnotation<parameters::internal::tParameterInfo>())
559          {
560            parameter_ports.push_back(&static_cast<core::tAbstractPort&>(*parameter_it));
561          }
562        }
563      }
564      else if (it->IsPort() && it->GetAnnotation<parameters::internal::tParameterInfo>())
565      {
566        parameter_ports.push_back(&static_cast<core::tAbstractPort&>(*it));
567      }
568      else
569      {
570        child_elements.push_back(&(*it));
571      }
572    }
573  }
574
575  struct
576  {
577    bool operator()(core::tFrameworkElement* a, core::tFrameworkElement* b)
578    {
579      return a->GetName() < b->GetName();
580    }
581  } comparator;
582  std::sort(child_elements.begin(), child_elements.end(), comparator);
583  std::sort(parameter_ports.begin(), parameter_ports.end(), comparator);
584
585  // Save parameters first
586  bool result = false;
587  for (core::tAbstractPort * port : parameter_ports)
588  {
589    bool outermost_group = GetFrameworkElement()->GetParent() == &(core::tRuntimeEnvironment::GetInstance());
590    parameters::internal::tParameterInfo* info = port->GetAnnotation<parameters::internal::tParameterInfo>();
591    bool is_responsible_for_parameter_links = IsResponsibleForConfigFileConnections(*port);
592
593    if (info && info->HasNonDefaultFinstructInfo() && (is_responsible_for_parameter_links || (outermost_group && info->GetCommandLineOption().length())))
594    {
595      // Save Parameter
596      rrlib::xml::tNode& parameter_node = node.AddChildNode("parameter");
597      parameter_node.SetAttribute("name", port->GetName());
598
599      if (!is_responsible_for_parameter_links)
600      {
601        parameter_node.SetAttribute("cmdline", info->GetCommandLineOption());
602      }
603      else
604      {
605        info->Serialize(parameter_node, true, outermost_group);
606      }
607      result = true;
608    }
609  }
610
611  // Process child elements
612  for (core::tFrameworkElement * child : child_elements)
613  {
614    rrlib::xml::tNode& child_node = node.AddChildNode("element");
615    child_node.SetAttribute("name", child->GetName());
616    bool sub_result = SaveParameterConfigEntries(child_node, *child);
617    result |= sub_result;
618    if (!sub_result)
619    {
620      node.RemoveChildNode(child_node);
621    }
622  }
623  return result;
624
625  /*for (auto it = GetFrameworkElement()->SubElementsBegin(); it != GetFrameworkElement()->SubElementsEnd(); ++it)
626  {
627    if ((!it->IsPort()) || (!it->IsReady()))
628    {
629      continue;
630    }
631
632    core::tAbstractPort& port = static_cast<core::tAbstractPort&>(*it);
633    bool outermost_group = GetFrameworkElement()->GetParent() == &(core::tRuntimeEnvironment::GetInstance());
634    parameters::internal::tParameterInfo* info = port.GetAnnotation<parameters::internal::tParameterInfo>();
635
636    if (info && info->HasNonDefaultFinstructInfo() && (IsResponsibleForConfigFileConnections(port) || (outermost_group && info->GetCommandLineOption().length())))
637    {
638      bool parameter_interface = port.GetParent()->GetFlag(tFlag::INTERFACE) || port.GetParent()->GetName() == "Parameters"; // TODO: remove the latter as soon as
639      core::tFrameworkElement* target_hierarchy_element = (parameter_interface && port.GetParent()->GetParent()) ? port.GetParent()->GetParent() : port.GetParent();
640
641      // Possibly move up hierarchy in XML
642      while (!(target_hierarchy_element == current_hierarchy_element || target_hierarchy_element->IsChildOf(*current_hierarchy_element)))
643      {
644        current_hierarchy_element = current_hierarchy_element->GetParent();
645        current_parameter_links_node = &current_parameter_links_node->Parent();
646      }
647      // Possibly create hierarchy in XML
648      std::vector<core::tFrameworkElement*> elements_to_add;
649      core::tFrameworkElement* element_to_add = target_hierarchy_element;
650      while (element_to_add != current_hierarchy_element)
651      {
652        elements_to_add.push_back(element_to_add);
653        element_to_add = element_to_add->GetParent();
654      }
655      for (auto it = elements_to_add.rbegin(); it != elements_to_add.rend(); ++it)
656      {
657        current_parameter_links_node = &current_parameter_links_node->AddChildNode("element");
658        current_parameter_links_node->SetAttribute("name", (*it)->GetName());
659      }
660      current_hierarchy_element = target_hierarchy_element;
661
662      // Save Parameter
663      rrlib::xml::tNode& parameter_node = current_parameter_links_node->AddChildNode("parameter");
664      parameter_node.SetAttribute("name", port.GetName());
665
666      if (!IsResponsibleForConfigFileConnections(port))
667      {
668        parameter_node.SetAttribute("cmdline", info->GetCommandLineOption());
669      }
670      else
671      {
672        info->Serialize(parameter_node, true, outermost_group);
673      }
674    }
675  }
676  */
677}
678
679void tFinstructable::SaveXml()
680{
681  {
682    rrlib::thread::tLock lock2(core::tRuntimeEnvironment::GetInstance().GetStructureMutex());
683    saving_thread = &rrlib::thread::tThread::CurrentThread();
684    dependencies_tmp.clear();
685    std::string save_to = core::GetFinrocFileToSaveTo(GetXmlFileString());
686    if (save_to.length() == 0)
687    {
688      std::string save_to_alt = GetXmlFileString();
689      std::replace(save_to_alt.begin(), save_to_alt.end(), '/', '_');
690      FINROC_LOG_PRINT(USER, "There does not seem to be any suitable location for: '", GetXmlFileString(), "' . For now, using '", save_to_alt, "'.");
691      save_to = save_to_alt;
692    }
693    FINROC_LOG_PRINT(USER, "Saving XML: ", save_to);
694    rrlib::xml::tDocument doc;
695    try
696    {
697      rrlib::xml::tNode& root = doc.AddRootNode("Finstructable"); // TODO: find good name ("finroc_structure")
698
699      // serialize default main name
700      if (main_name.length() > 0)
701      {
702        root.SetAttribute("defaultname", main_name);
703      }
704
705      // serialize any editable interfaces
706      tEditableInterfaces* editable_interfaces = GetFrameworkElement()->GetAnnotation<tEditableInterfaces>();
707      if (editable_interfaces)
708      {
709        editable_interfaces->SaveAllNonEmptyInterfaces(root);
710      }
711
712      // serialize framework elements
713      SerializeChildren(root, *GetFrameworkElement());
714
715      // serialize connectors (sorted by port URIs (we do not want changes in finstruct files depending on whether or not an optional/volatile connector exists))
716      rrlib::uri::tPath this_path = GetFrameworkElement()->GetPath();
717      std::map<std::pair<rrlib::uri::tURI, rrlib::uri::tURI>, std::pair<core::tConnector*, core::tUriConnector*>> connector_map;
718      bool this_is_outermost_composite_component = GetFrameworkElement()->GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP) == nullptr;
719
720      // Get (primary) connectors
721      for (auto it = GetFrameworkElement()->SubElementsBegin(); it != GetFrameworkElement()->SubElementsEnd(); ++it)
722      {
723        if ((!it->IsPort()) || (!it->IsReady()))
724        {
725          continue;
726        }
727        core::tAbstractPort& port = static_cast<core::tAbstractPort&>(*it);
728        tFrameworkElement* port_parent_group = port.GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP);
729
730        // Plain connectors
731        for (auto it = port.OutgoingConnectionsBegin(); it != port.OutgoingConnectionsEnd(); ++it) // only outgoing edges => we don't get any edges twice
732        {
733          // Checks whether to save connector
734          // only save primary finstructed edges
735          if ((!it->Flags().Get(core::tConnectionFlag::FINSTRUCTED)) || it->Flags().Get(core::tConnectionFlag::NON_PRIMARY_CONNECTOR))
736          {
737            continue;
738          }
739
740          // 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
741          tFrameworkElement* common_parent = port_parent_group;
742          while (common_parent && (!it->Destination().IsChildOf(*common_parent)))
743          {
744            common_parent = common_parent->GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP);
745          }
746          if (common_parent != GetFrameworkElement() && (!(this_is_outermost_composite_component && common_parent == nullptr)))
747          {
748            // save connector in another group
749            continue;
750          }
751
752          std::pair<rrlib::uri::tURI, rrlib::uri::tURI> key(GetConnectorPath(port.GetPath(), this_path), GetConnectorPath(it->Destination().GetPath(), this_path));
753          std::pair<core::tConnector*, core::tUriConnector*> value(&(*it), nullptr);
754          connector_map.emplace(key, value);
755        }
756
757        // Get URI connectors
758        for (auto & connector : port.UriConnectors())
759        {
760          // Checks whether to save connector
761          // only save primary finstructed edges
762          if ((!connector) || (!connector->Flags().Get(core::tConnectionFlag::FINSTRUCTED)) || connector->Flags().Get(core::tConnectionFlag::NON_PRIMARY_CONNECTOR))
763          {
764            continue;
765          }
766
767          std::pair<rrlib::uri::tURI, rrlib::uri::tURI> key(GetConnectorPath(port.GetPath(), this_path), connector->Uri());
768
769          // 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
770          if (typeid(*connector) == typeid(core::internal::tLocalUriConnector))
771          {
772            auto& local_connector = static_cast<core::internal::tLocalUriConnector&>(*connector);
773            bool source_uri = local_connector.GetPortReferences()[0].path.Size();
774            rrlib::uri::tPath path = local_connector.GetPortReferences()[source_uri ? 0 : 1].path;
775            tFrameworkElement* common_parent = port_parent_group;
776            rrlib::uri::tPath parent_group_path = common_parent->GetPath();
777            while (common_parent && path.CountCommonElements(parent_group_path) != parent_group_path.Size())
778            {
779              common_parent = common_parent->GetParentWithFlags(tFlag::FINSTRUCTABLE_GROUP);
780              parent_group_path = common_parent ? common_parent->GetPath() : parent_group_path;
781            }
782            if (common_parent != GetFrameworkElement() && (!(this_is_outermost_composite_component && common_parent == nullptr)))
783            {
784              // save connector in another group
785              continue;
786            }
787
788            rrlib::uri::tURI port_uri = key.first;
789            key.first = source_uri ? rrlib::uri::tURI(path) : port_uri;
790            key.second = source_uri ? port_uri : rrlib::uri::tURI(path);
791          }
792
793          std::pair<core::tConnector*, core::tUriConnector*> value(nullptr, connector.get());
794          connector_map.emplace(key, value);
795        }
796      }
797
798      for (auto & entry : connector_map)
799      {
800        rrlib::xml::tNode& edge = root.AddChildNode("edge"); // TODO: "connector"?
801        edge.SetAttribute("src", entry.first.first.ToString());
802        edge.SetAttribute("dest", entry.first.second.ToString());
803
804        // Save flags
805        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;
806        core::tConnectionFlags flags_to_save((entry.second.first ? entry.second.first->Flags() : entry.second.second->Flags()).Raw() & cFLAGS_TO_SAVE.Raw());
807        if (flags_to_save.Raw())
808        {
809          edge.SetAttribute("flags", rrlib::serialization::Serialize(flags_to_save));
810        }
811
812        // Save conversion operation
813        const rrlib::rtti::conversion::tConversionOperationSequence& conversion_operations = entry.second.first ? entry.second.first->ConversionOperations() : entry.second.second->ConversionOperations();
814        if (conversion_operations.Size())
815        {
816          rrlib::xml::tNode& conversion = root.AddChildNode("conversion");
817          conversion.SetAttribute(conversion_operations.Size() == 2 ? "operation1" : "operation", conversion_operations[0].first);
818          if (conversion_operations.GetParameterValue(0))
819          {
820            conversion.SetAttribute(conversion_operations.Size() == 2 ? "parameter1" : "parameter", conversion_operations.GetParameterValue(0).ToString());
821          }
822          if (conversion_operations.Size() == 2)
823          {
824            conversion.SetAttribute("operation2", conversion_operations[1].first);
825            if (conversion_operations.GetParameterValue(0))
826            {
827              conversion.SetAttribute("parameter2", conversion_operations.GetParameterValue(1).ToString());
828            }
829          }
830          if (conversion_operations.IntermediateType())
831          {
832            conversion.SetAttribute("intermediate_type", conversion_operations.IntermediateType().GetName());
833          }
834        }
835
836        // Save parameters of URI connector
837        if (entry.second.second)
838        {
839          core::tUriConnector::tConstParameterDefinitionRange definition_range = entry.second.second->GetParameterDefinitions();
840          core::tUriConnector::tParameterValueRange value_range = entry.second.second->GetParameterValues();
841          assert(definition_range.End() - definition_range.Begin() == value_range.End() - value_range.Begin());
842
843          auto definition = definition_range.Begin();
844          auto value = value_range.Begin();
845          for (; definition < definition_range.End(); ++definition, ++value)
846          {
847            if (!value->Equals(definition->GetDefaultValue()))
848            {
849              rrlib::xml::tNode& parameter_node = root.AddChildNode("parameter");
850              parameter_node.SetAttribute("name", definition->GetName());
851              value->Serialize(parameter_node);
852            }
853          }
854        }
855      }
856
857      // Save parameter config entries
858      SaveParameterConfigEntries(root.AddChildNode("parameter_links"), *GetFrameworkElement());
859
860      // add dependencies
861      if (dependencies_tmp.size() > 0)
862      {
863        std::stringstream s;
864        for (auto it = dependencies_tmp.begin(); it != dependencies_tmp.end(); ++it)
865        {
866          if (it != dependencies_tmp.begin())
867          {
868            s << ", ";
869          }
870          s << it->ToString();
871        }
872
873        root.SetAttribute("dependencies", s.str());
874        dependencies_tmp.clear();
875      }
876
877      doc.WriteToFile(save_to);
878      FINROC_LOG_PRINT(USER, "Saving successful.");
879
880    }
881    catch (const rrlib::xml::tException& e)
882    {
883      const char* msg = e.what();
884      FINROC_LOG_PRINT(USER, "Saving failed: ", msg);
885      throw std::runtime_error(msg);
886    }
887  }
888  saving_thread = nullptr;
889}
890
891std::vector<std::string> tFinstructable::ScanForCommandLineArgs(const std::string& finroc_file)
892{
893  std::vector<std::string> result;
894  try
895  {
896    rrlib::xml::tDocument doc(core::GetFinrocXMLDocument(finroc_file, false));
897    try
898    {
899      FINROC_LOG_PRINT_STATIC(DEBUG, "Scanning for command line options in ", finroc_file);
900      rrlib::xml::tNode& root = doc.RootNode();
901      ScanForCommandLineArgsHelper(result, root);
902      FINROC_LOG_PRINTF_STATIC(DEBUG, "Scanning successful. Found %zu additional options.", result.size());
903    }
904    catch (std::exception& e)
905    {
906      FINROC_LOG_PRINT_STATIC(WARNING, "FinstructableGroup", "Scanning failed: ", finroc_file, e);
907    }
908  }
909  catch (std::exception& e)
910  {}
911  return result;
912}
913
914void tFinstructable::ScanForCommandLineArgsHelper(std::vector<std::string>& result, const rrlib::xml::tNode& parent)
915{
916  for (rrlib::xml::tNode::const_iterator node = parent.ChildrenBegin(); node != parent.ChildrenEnd(); ++node)
917  {
918    std::string name(node->Name());
919    if (node->HasAttribute("cmdline") && (name == "staticparameter" || name == "parameter"))
920    {
921      result.push_back(node->GetStringAttribute("cmdline"));
922    }
923    ScanForCommandLineArgsHelper(result, *node);
924  }
925}
926
927void tFinstructable::SerializeChildren(rrlib::xml::tNode& node, tFrameworkElement& current)
928{
929  for (auto child = current.ChildrenBegin(); child != current.ChildrenEnd(); ++child)
930  {
931    parameters::internal::tStaticParameterList* spl = child->GetAnnotation<parameters::internal::tStaticParameterList>();
932    tConstructorParameters* cps = child->GetAnnotation<tConstructorParameters>();
933    if (child->IsReady() && child->GetFlag(tFlag::FINSTRUCTED))
934    {
935      // serialize framework element
936      rrlib::xml::tNode& n = node.AddChildNode("element");
937      n.SetAttribute("name", child->GetName());
938      tCreateFrameworkElementAction* cma = tCreateFrameworkElementAction::GetConstructibleElements()[spl->GetCreateAction()];
939      n.SetAttribute("group", cma->GetModuleGroup().ToString());
940      //if (boost::ends_with(cma->GetModuleGroup(), ".so"))
941      //{
942      AddDependency(cma->GetModuleGroup());
943      //}
944      n.SetAttribute("type", cma->GetName());
945      if (cps != nullptr)
946      {
947        rrlib::xml::tNode& pn = n.AddChildNode("constructor");
948        cps->Serialize(pn, true);
949      }
950      if (spl != nullptr)
951      {
952        rrlib::xml::tNode& pn = n.AddChildNode("parameters");
953        spl->Serialize(pn, true);
954      }
955
956      // serialize its children
957      if (!child->GetFlag(tFlag::FINSTRUCTABLE_GROUP))
958      {
959        SerializeChildren(n, *child);
960      }
961    }
962  }
963}
964
965void tFinstructable::SetFinstructed(tFrameworkElement& fe, tCreateFrameworkElementAction& create_action, tConstructorParameters* params)
966{
967  assert(!fe.GetFlag(tFlag::FINSTRUCTED) && (!fe.IsReady()));
968  parameters::internal::tStaticParameterList& list = parameters::internal::tStaticParameterList::GetOrCreate(fe);
969  auto& element_list = tCreateFrameworkElementAction::GetConstructibleElements();
970  for (size_t i = 0, n = element_list.Size(); i < n; ++i)
971  {
972    if (element_list[i] == &create_action)
973    {
974      list.SetCreateAction(i);
975      break;
976    }
977  }
978  fe.SetFlag(tFlag::FINSTRUCTED);
979  if (params)
980  {
981    fe.AddAnnotation<tConstructorParameters>(*params);
982  }
983}
984
985void tFinstructable::StaticInit()
986{
987  startup_loaded_finroc_libs = GetLoadedFinrocLibraries();
988}
989
990//----------------------------------------------------------------------
991// End of namespace declaration
992//----------------------------------------------------------------------
993}
994}
Note: See TracBrowser for help on using the repository browser.