source: finroc_plugins_parameters/internal/tParameterInfo.cpp @ 90:a57746b21369

17.03
Last change on this file since 90:a57746b21369 was 90:a57746b21369, checked in by Max Reichardt <max.reichardt@…>, 3 years ago

Fixes issue in last commit (missing parameter reload on setting config entry)

File size: 13.7 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/parameters/internal/tParameterInfo.cpp
23 *
24 * \author  Max Reichardt
25 *
26 * \date    2012-11-28
27 *
28 */
29//----------------------------------------------------------------------
30#include "plugins/parameters/internal/tParameterInfo.h"
31
32//----------------------------------------------------------------------
33// External includes (system with <>, local with "")
34//----------------------------------------------------------------------
35#include "rrlib/rtti/rtti.h"
36#include "core/port/tAbstractPort.h"
37#include "core/tRuntimeEnvironment.h"
38#include "plugins/data_ports/tGenericPort.h"
39
40//----------------------------------------------------------------------
41// Internal includes with ""
42//----------------------------------------------------------------------
43#include "plugins/parameters/tConfigFile.h"
44#include "plugins/parameters/tConfigNode.h"
45
46//----------------------------------------------------------------------
47// Debugging
48//----------------------------------------------------------------------
49#include <cassert>
50
51//----------------------------------------------------------------------
52// Namespace usage
53//----------------------------------------------------------------------
54
55//----------------------------------------------------------------------
56// Namespace declaration
57//----------------------------------------------------------------------
58namespace finroc
59{
60namespace parameters
61{
62namespace internal
63{
64
65//----------------------------------------------------------------------
66// Forward declarations / typedefs / enums
67//----------------------------------------------------------------------
68
69//----------------------------------------------------------------------
70// Const values
71//----------------------------------------------------------------------
72
73/* Initializes parameter info annotation type */
74__attribute__((unused))
75static rrlib::rtti::tDataType<tParameterInfo> cTYPE;
76
77namespace
78{
79
80const std::string cEMPTY_STRING;
81
82}
83
84//----------------------------------------------------------------------
85// Implementation
86//----------------------------------------------------------------------
87
88tParameterInfo::tParameterInfo()
89{}
90
91#ifdef _LIB_RRLIB_XML_PRESENT_
92void tParameterInfo::Deserialize(const rrlib::xml::tNode& node, bool high_precedence, bool include_commmand_line, tOrigin* origin)
93{
94  std::string config_entry = "";
95  if (node.HasAttribute("config"))
96  {
97    config_entry = node.GetStringAttribute("config");
98  }
99  Set(tSettingType::CONFIG_ENTRY, origin, high_precedence, config_entry, false);
100
101  if (include_commmand_line)
102  {
103    std::string command_line_option = "";
104    if (node.HasAttribute("cmdline"))
105    {
106      command_line_option = node.GetStringAttribute("cmdline");
107    }
108    Set(tSettingType::COMMAND_LINE_OPTION, origin, high_precedence, command_line_option, false);
109  }
110  std::string finstruct_default = "";
111  if (node.HasAttribute("default"))
112  {
113    finstruct_default = node.GetStringAttribute("default");
114  }
115  Set(tSettingType::FINSTRUCT_DEFAULT, origin, high_precedence, finstruct_default, false);
116}
117#endif
118
119const std::string& tParameterInfo::GetSetting(tSettingType type) const
120{
121  const tSetting* low_precedence_setting = nullptr;
122  const tSetting* high_precedence_setting = nullptr;
123  for (auto & setting : settings)
124  {
125    if (setting.type == type)
126    {
127      (setting.high_precedence ? high_precedence_setting : low_precedence_setting) = &setting;
128    }
129  }
130  return high_precedence_setting ? high_precedence_setting->value : (low_precedence_setting ? low_precedence_setting->value : cEMPTY_STRING);
131}
132
133const std::string& tParameterInfo::GetSetting(tSettingType type, bool high_precedence, tOrigin* origin) const
134{
135  for (auto & setting : settings)
136  {
137    if (setting.type == type && setting.high_precedence == high_precedence && setting.origin == origin)
138    {
139      return setting.value;
140    }
141  }
142  return cEMPTY_STRING;
143}
144
145bool tParameterInfo::IsConfigEntrySetFromFinstruct() const
146{
147  for (auto & setting : settings)
148  {
149    if (setting.type == tSettingType::CONFIG_ENTRY && setting.high_precedence)
150    {
151      return true;
152    }
153  }
154  return false;
155}
156
157bool tParameterInfo::IsFinstructableGroupResponsibleForConfigFileConnections(const core::tFrameworkElement& finstructable_group, const core::tFrameworkElement& ap)
158{
159  core::tFrameworkElement* responsible = ap.GetParentWithFlags(core::tFrameworkElement::tFlag::FINSTRUCTABLE_GROUP | core::tFrameworkElement::tFlag::MANAGES_PARAMETER_CONFIGURATION);
160  return responsible == &finstructable_group;
161}
162
163void tParameterInfo::LoadValue(bool ignore_ready)
164{
165  core::tAbstractPort* ann = this->GetAnnotated<core::tAbstractPort>();
166  {
167    rrlib::thread::tLock lock(ann->GetStructureMutex());
168    if (ann && (ignore_ready || ann->IsReady()))
169    {
170      // command line option
171      auto& command_line_option = GetCommandLineOption();
172      if (command_line_option.length() > 0)
173      {
174        std::string arg = core::tRuntimeEnvironment::GetInstance().GetCommandLineArgument(command_line_option);
175        if (arg.length() > 0)
176        {
177          if (data_ports::IsDataFlowType(ann->GetDataType()))
178          {
179            rrlib::serialization::tStringInputStream sis(arg);
180            data_ports::tGenericPort port = data_ports::tGenericPort::Wrap(*ann);
181            data_ports::tPortDataPointer<rrlib::rtti::tGenericObject> buffer = port.GetUnusedBuffer();
182            try
183            {
184              buffer->Deserialize(sis);
185              std::string error = port.BrowserPublish(buffer);
186              if (error.size() > 0)
187              {
188                FINROC_LOG_PRINT(WARNING, "Failed to load parameter '", ann, "' from command line argument '", arg, "': ", error);
189              }
190              return;
191            }
192            catch (const std::exception& e)
193            {
194              FINROC_LOG_PRINT(ERROR, "Failed to load parameter '", ann, "' from command line argument '", arg, "': ", e);
195            }
196          }
197          else
198          {
199            throw std::runtime_error("Port Type not supported as a parameter");
200          }
201        }
202      }
203
204      // config file entry
205      tConfigFile* cf = tConfigFile::Find(*ann);
206      auto& config_entry = GetConfigEntry();
207      if (cf && config_entry.length() > 0)
208      {
209        std::string full_config_entry = tConfigNode::GetFullConfigEntry(*ann, config_entry);
210        if (cf->HasEntry(full_config_entry))
211        {
212#ifdef _LIB_RRLIB_XML_PRESENT_
213          rrlib::xml::tNode& node = cf->GetEntry(full_config_entry, false);
214          if (data_ports::IsDataFlowType(ann->GetDataType()))
215          {
216            data_ports::tGenericPort port = data_ports::tGenericPort::Wrap(*ann);
217            data_ports::tPortDataPointer<rrlib::rtti::tGenericObject> buffer = port.GetUnusedBuffer();
218
219            try
220            {
221              buffer->Deserialize(node);
222              std::string error = port.BrowserPublish(buffer);
223              if (error.size() > 0)
224              {
225                FINROC_LOG_PRINT(WARNING, "Failed to load parameter '", ann, "' from config entry '", full_config_entry, "': ", error);
226              }
227              return;
228            }
229            catch (const std::exception& e)
230            {
231              FINROC_LOG_PRINT(ERROR, "Failed to load parameter '", ann, "' from config entry '", full_config_entry, "': ", e);
232            }
233          }
234          else
235          {
236            throw std::runtime_error("Port Type not supported as a parameter");
237          }
238#endif
239        }
240      }
241
242      // finstruct default
243      auto& finstruct_default = GetFinstructDefault();
244      if (finstruct_default.length() > 0)
245      {
246        if (data_ports::IsDataFlowType(ann->GetDataType()))
247        {
248          rrlib::serialization::tStringInputStream sis(finstruct_default);
249          data_ports::tGenericPort port = data_ports::tGenericPort::Wrap(*ann);
250          data_ports::tPortDataPointer<rrlib::rtti::tGenericObject> buffer = port.GetUnusedBuffer();
251
252          try
253          {
254            buffer->Deserialize(sis);
255            std::string error = port.BrowserPublish(buffer);
256            if (error.size() > 0)
257            {
258              FINROC_LOG_PRINT(WARNING, "Failed to load parameter '", ann, "' from finstruct default '", finstruct_default, "': ", error);
259            }
260            return;
261          }
262          catch (const std::exception& e)
263          {
264            FINROC_LOG_PRINT(ERROR, "Failed to load parameter '", ann, "' from finstruct default '", finstruct_default, "': ", e);
265          }
266        }
267        else
268        {
269          throw std::runtime_error("Port Type not supported as a parameter");
270        }
271      }
272    }
273  }
274}
275
276void tParameterInfo::OnInitialization()
277{
278  try
279  {
280    LoadValue(true);
281  }
282  catch (const std::exception& e)
283  {
284    FINROC_LOG_PRINT(ERROR, e);
285  }
286}
287
288void tParameterInfo::SaveValue()
289{
290  core::tAbstractPort* ann = GetAnnotated<core::tAbstractPort>();
291  if ((!ann) || (!ann->IsReady()))
292  {
293    return;
294  }
295  tConfigFile* cf = tConfigFile::Find(*ann);
296  auto& config_entry = GetConfigEntry();
297  bool has_entry = cf->HasEntry(config_entry);
298  if (data_ports::IsDataFlowType(ann->GetDataType()))
299  {
300    data_ports::tGenericPort port = data_ports::tGenericPort::Wrap(*ann);
301    if (has_entry)
302    {
303#ifdef _LIB_RRLIB_XML_PRESENT_
304      rrlib::xml::tNode& node = cf->GetEntry(config_entry, true);
305      std::unique_ptr<rrlib::rtti::tGenericObject> current_value(port.GetDataType().CreateGenericObject());
306      port.Get(*current_value);
307      current_value->Serialize(node);
308#endif
309    }
310  }
311  else
312  {
313    throw std::runtime_error("Port Type not supported as a parameter");
314  }
315}
316
317#ifdef _LIB_RRLIB_XML_PRESENT_
318void tParameterInfo::Serialize(rrlib::xml::tNode& node, bool high_precedence, bool include_command_line, tOrigin* origin) const
319{
320  assert(!(node.HasAttribute("default") || node.HasAttribute("cmdline") || node.HasAttribute("config")));
321  auto config_entry = GetSetting(tSettingType::CONFIG_ENTRY, high_precedence, origin);
322  if (config_entry.length())
323  {
324    node.SetAttribute("config", config_entry);
325  }
326  if (include_command_line)
327  {
328    auto command_line_option = GetSetting(tSettingType::COMMAND_LINE_OPTION, high_precedence, origin);
329    if (command_line_option.length())
330    {
331      node.SetAttribute("cmdline", command_line_option);
332    }
333  }
334  auto finstruct_default = GetSetting(tSettingType::FINSTRUCT_DEFAULT, high_precedence, origin);
335  if (finstruct_default.length() > 0)
336  {
337    node.SetAttribute("default", finstruct_default);
338  }
339}
340#endif
341
342bool tParameterInfo::Set(tSettingType type, tOrigin* origin, bool high_precedence, const std::string& value, bool load_on_change)
343{
344  auto existing_setting = settings.end();
345  for (auto it = settings.begin(); it != settings.end(); ++it)
346  {
347    if (it->type == type && it->origin == origin && it->high_precedence == high_precedence)
348    {
349      existing_setting = it;
350      break;
351    }
352  }
353
354  bool reload = false;
355  bool changed = false;
356  if (existing_setting != settings.end())
357  {
358    if (value.empty())
359    {
360      settings.erase(existing_setting);
361      changed = true;
362    }
363    else if (existing_setting->value != value)
364    {
365      existing_setting->value = value;
366      reload = true;
367    }
368  }
369  else if (!value.empty())
370  {
371    tSetting new_setting = { type, origin, high_precedence, value };
372    settings.push_back(new_setting);
373    reload = true;
374  }
375  changed |= reload;
376
377  if (load_on_change && reload)
378  {
379    try
380    {
381      LoadValue();
382    }
383    catch (const std::exception& e)
384    {
385      FINROC_LOG_PRINT(ERROR, e);
386    }
387  }
388
389  return changed;
390}
391
392
393rrlib::serialization::tOutputStream& operator << (rrlib::serialization::tOutputStream & stream, const tParameterInfo & parameter_info)
394{
395  stream.WriteBoolean(parameter_info.IsConfigEntrySetFromFinstruct());
396  stream.WriteString(parameter_info.GetConfigEntry());
397  stream.WriteString(parameter_info.GetCommandLineOption());
398  stream.WriteString(parameter_info.GetFinstructDefault());
399  return stream;
400}
401
402rrlib::serialization::tInputStream& operator >> (rrlib::serialization::tInputStream & stream, tParameterInfo & parameter_info)
403{
404  bool entry_set_from_finstruct = stream.ReadBoolean();
405  std::string config_entry = stream.ReadString();
406  std::string command_line_option = stream.ReadString();
407  std::string finstruct_default = stream.ReadString();
408  bool changed = parameter_info.Set(tParameterInfo::tSettingType::CONFIG_ENTRY, nullptr, entry_set_from_finstruct, config_entry, false);
409  changed |= parameter_info.Set(tParameterInfo::tSettingType::COMMAND_LINE_OPTION, nullptr, true, command_line_option, false);
410  changed |= parameter_info.Set(tParameterInfo::tSettingType::FINSTRUCT_DEFAULT, nullptr, true, finstruct_default, false);
411
412  if (changed)
413  {
414    try
415    {
416      parameter_info.LoadValue();
417    }
418    catch (std::exception& e)
419    {
420      FINROC_LOG_PRINT_STATIC(ERROR, e);
421    }
422  }
423  return stream;
424}
425
426//----------------------------------------------------------------------
427// End of namespace declaration
428//----------------------------------------------------------------------
429}
430}
431}
Note: See TracBrowser for help on using the repository browser.